[
  {
    "path": ".config/nextest.toml",
    "content": "experimental = [\"setup-scripts\"]\n\n# For tests that need to run one at a time to avoid resource contention from\n# parallel execution.\n[test-groups]\nserial-integration = { max-threads = 1 }\n\n# The built-in `fn build_test_service` in the test runs a single instance of\n# `cargo leptos build` guaranteed with `std::sync::Once`.  However,\n# `cargo-nexttest` does not have the ability to group tests under the same\n# process to emulate what `cargo test` does, thus resulting in competing\n# `wasm-bindgen` executions that will clobber each other, breaking the tests.\n# While serializing the tests was done at one point as a quick workaround,\n# using setup-scripts is the better approach.\n#\n# References:\n# - https://github.com/nextest-rs/nextest/issues/27\n# - https://github.com/nextest-rs/nextest/issues/209\n#\n# [[profile.default.overrides]]\n# filter = \"binary_id(leptos_axum::axum_integration)\"\n# test-group = \"serial-integration\"\n\n# Ideally we want this to work to allow for as much as (relatively) cross-\n# platform compatibility via cargo, but there doesn't appear to be a way to\n# apply environment variables or otherwise set `LEPTOS_OUTPUT_NAME` within\n# this section.\n#\n# ```\n# [scripts.setup.axum_integration_service_mode]\n# command = [\"cargo\", \"leptos\", \"--manifest-path\", \"integrations/axum/tests/service_mode/Cargo.toml\", \"build\"]\n#\n# [[profile.default.scripts]]\n# filter = \"binary_id(leptos_axum::axum_integration)\"\n# setup = [\"axum_integration_service_mode\"]\n# ```\n#\n# Instead, we have to do the platform specific route...\n[scripts.setup.axum_integration_service_mode_sh]\ncommand = [\"./scripts/axum_integration_service_mode.sh\"]\n\n[scripts.setup.axum_integration_service_mode_bat]\ncommand = [\"cmd\", \"/c\", \"scripts\\\\axum_integration_service_mode.bat\"]\n\n[[profile.default.scripts]]\nplatform = { host = \"cfg(unix)\" }\nfilter = \"binary_id(leptos_axum::axum_integration)\"\nsetup = [\"axum_integration_service_mode_sh\"]\n\n[[profile.default.scripts]]\nplatform = { host = \"cfg(windows)\" }\nfilter = \"binary_id(leptos_axum::axum_integration)\"\nsetup = [\"axum_integration_service_mode_bat\"]\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: gbj\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**Leptos Dependencies**\n\nPlease copy and paste the Leptos dependencies and features from your `Cargo.toml`.\n\nFor example:\n```toml\nleptos = { version = \"0.3\", features = [\"serde\"] }\nleptos_axum = { version = \"0.3\", optional = true }\nleptos_meta = { version = \"0.3\"}\nleptos_router = { version = \"0.3\"}\n```\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Next Steps**\n\n- [ ] I will make a PR\n- [ ] I would like to make a PR, but need help getting started\n- [ ] I want someone else to take the time to fix this\n- [ ] This is a low priority for me and is just shared for your information\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Support or Question\n    url: https://github.com/leptos-rs/leptos/discussions/new?category=q-a\n    about: Do you need help figuring out how to do something, or want some help troubleshooting a bug? You can ask in our Discussions section.\n  - name: Discord Discussions\n    url: https://discord.gg/YdRAhS7eQB\n    about: For more informal, real-time conversation and support, you can join our Discord server.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n  # Grouping all dependencies in one PR weekly\n  - package-ecosystem: cargo\n    directory: \"/\"\n    schedule:\n      interval: weekly\n      day: monday\n    open-pull-requests-limit: 1\n    allow:\n      - dependency-type: \"all\"\n    groups:\n      rust-dependencies:\n        patterns:\n          - \"*\"\n"
  },
  {
    "path": ".github/workflows/autofix.yml",
    "content": "name: autofix.ci\non:\n  pull_request:\n  # Running this workflow on main branch pushes requires write permission to apply changes.\n  # Leave it alone for future uses.\n  # push:\n  #   branches: [\"main\"]\npermissions:\n  contents: read\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\nenv:\n  CARGO_TERM_COLOR: always\n  RUST_BACKTRACE: 1\n  DEBIAN_FRONTEND: noninteractive\njobs:\n  autofix:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions-rust-lang/setup-rust-toolchain@v1\n        with: {toolchain: \"nightly-2026-02-24\", components: \"rustfmt, clippy\", target: \"wasm32-unknown-unknown\", rustflags: \"\"}\n      - name: Install Glib\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y libglib2.0-dev\n      - name: Install cargo-all-features\n        run: cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support\n      - name: Install jq\n        run: sudo apt-get install jq\n      - name: Format the workspace\n        run: cargo fmt --all\n      - name: Clippy the workspace\n        run: cargo all-features clippy --allow-dirty --fix --lib --no-deps\n      - uses: autofix-ci/action@v1.3.3\n        if: ${{ always() }}\n        with:\n          fail-fast: false\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\non:\n  push:\n    branches:\n      - main\n      - leptos_0.6\n      - leptos_0.8\n  pull_request:\n    branches:\n      - main\n      - leptos_0.6\n      - leptos_0.8\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\njobs:\n  get-leptos-changed:\n    uses: ./.github/workflows/get-leptos-changed.yml\n  get-leptos-matrix:\n    uses: ./.github/workflows/get-leptos-matrix.yml\n  get-example-changed:\n    uses: ./.github/workflows/get-example-changed.yml\n  get-examples-matrix:\n    uses: ./.github/workflows/get-examples-matrix.yml\n  test-members:\n    name: CI (members)\n    needs: [get-leptos-changed, get-leptos-matrix]\n    if: needs.get-leptos-changed.outputs.leptos_changed == 'true'\n    strategy:\n      matrix: ${{ fromJSON(needs.get-leptos-matrix.outputs.matrix) }}\n      fail-fast: false\n    uses: ./.github/workflows/run-cargo-make-task.yml\n    with:\n      directory: ${{ matrix.directory }}\n  test-examples:\n    name: CI (examples)\n    needs: [test-members, get-examples-matrix]\n    if: ${{ success() }}\n    strategy:\n      matrix: ${{ fromJSON(needs.get-examples-matrix.outputs.matrix) }}\n      fail-fast: false\n    uses: ./.github/workflows/run-cargo-make-task.yml\n    with:\n      directory: ${{ matrix.directory }}\n  test-only-examples:\n    name: CI (examples)\n    needs: [get-leptos-changed, get-example-changed]\n    if: needs.get-leptos-changed.outputs.leptos_changed != 'true' && needs.get-example-changed.outputs.example_changed == 'true'\n    strategy:\n      matrix: ${{ fromJSON(needs.get-example-changed.outputs.matrix) }}\n      fail-fast: false\n    uses: ./.github/workflows/run-cargo-make-task.yml\n    with:\n      directory: ${{ matrix.directory }}\n  semver-check:\n    name: SemVer check (stable)\n    needs: [get-leptos-changed, test-members, test-examples]\n    if: ${{ success() && needs.get-leptos-changed.outputs.leptos_changed == 'true' && !contains(github.event.pull_request.labels.*.name, 'breaking') }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Install Glib\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y libglib2.0-dev\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Semver Checks\n        uses: obi1kenobi/cargo-semver-checks-action@v2\n"
  },
  {
    "path": ".github/workflows/get-example-changed.yml",
    "content": "name: Examples Changed Call\non:\n  workflow_call:\n    outputs:\n      example_changed:\n        description: \"Example Changed\"\n        value: ${{ jobs.get-example-changed.outputs.example_changed }}\n      # This is for test-only-examples workflow in ci.yml\n      matrix:\n        description: \"Example Changed Directories\"\n        value: ${{ jobs.get-example-changed.outputs.matrix }}\njobs:\n  get-example-changed:\n    name: Get Example Changed\n    runs-on: ubuntu-latest\n    outputs:\n      example_changed: ${{ steps.set-example-changed.outputs.example_changed }}\n      # This is for test-only-examples workflow in ci.yml\n      matrix: ${{ steps.set-example-changed.outputs.matrix }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: Get example files that changed\n        id: changed-files\n        uses: tj-actions/changed-files@v47\n        with:\n          files: |\n            examples/**\n            !examples/cargo-make/**\n            !examples/Makefile.toml\n            !examples/*.md\n      - name: List example files that changed\n        run: echo '${{ steps.changed-files.outputs.all_changed_files }}'\n      - name: Install jq\n        run: sudo apt-get install jq\n      - name: Set example_changed\n        id: set-example-changed\n        run: |\n          echo \"example_changed=${{ steps.changed-files.outputs.any_changed }}\" >> \"$GITHUB_OUTPUT\"\n          # This is for test-only-examples workflow in ci.yml\n          echo \"matrix={\\\"directory\\\": $(echo '${{ steps.changed-files.outputs.all_changed_files }}' | tr ' ' '\\n' | awk -F'/' '{print $1 \"/\" $2}'| sort -u | jq -R -s -c 'split(\"\\n\") | .[:-1]')}\" >> \"$GITHUB_OUTPUT\"\n"
  },
  {
    "path": ".github/workflows/get-examples-matrix.yml",
    "content": "name: Get Examples Matrix Call\non:\n  workflow_call:\n    outputs:\n      matrix:\n        description: \"Matrix\"\n        value: ${{ jobs.create.outputs.matrix }}\njobs:\n  create:\n    name: Create Examples Matrix\n    runs-on: ubuntu-latest\n    outputs:\n      matrix: ${{ steps.set-matrix.outputs.matrix }}\n    env:\n      # separate examples using \"|\" (vertical bar) char like \"a|b|c\".\n      # cargo-make should be excluded by default.\n      EXCLUDED_EXAMPLES: cargo-make\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Install jq\n        run: sudo apt-get install jq\n      - name: Set Matrix\n        id: set-matrix\n        run: |\n          examples=$(ls -1d examples/*/ |\n          grep -vE \"($EXCLUDED_EXAMPLES)\" |\n          sed 's/\\/$//' |\n          jq -R -s -c 'split(\"\\n\")[:-1]')\n          echo \"Example Directories: $examples\"\n          echo \"matrix={\\\"directory\\\":$examples}\" >> \"$GITHUB_OUTPUT\"\n      - name: Print Location Info\n        run: |\n          echo \"Workspace: ${{ github.workspace }}\"\n          pwd\n          ls | sort -u\n"
  },
  {
    "path": ".github/workflows/get-leptos-changed.yml",
    "content": "name: Get Leptos Changed Call\non:\n  workflow_call:\n    outputs:\n      leptos_changed:\n        description: \"Leptos Changed\"\n        value: ${{ jobs.create.outputs.leptos_changed }}\njobs:\n  create:\n    name: Detect Source Change\n    runs-on: ubuntu-latest\n    outputs:\n      leptos_changed: ${{ steps.set-source-changed.outputs.leptos_changed }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: Get source files that changed\n        id: changed-source\n        uses: tj-actions/changed-files@v47\n        with:\n          files_ignore: |\n            .*/**/*\n            cargo-make/**/*\n            examples/**/*\n            projects/**/*\n            benchmarks/**/*\n            docs/**/*\n      - name: List source files that changed\n        run: echo '${{ steps.changed-source.outputs.all_changed_files }}'\n      - name: Set leptos_changed\n        id: set-source-changed\n        run: |\n          echo \"leptos_changed=${{ steps.changed-source.outputs.any_changed }}\" >> \"$GITHUB_OUTPUT\"\n"
  },
  {
    "path": ".github/workflows/get-leptos-matrix.yml",
    "content": "name: Get Leptos Matrix Call\non:\n  workflow_call:\n    outputs:\n      matrix:\n        description: \"Matrix\"\n        value: ${{ jobs.create.outputs.matrix }}\njobs:\n  create:\n    name: Create Leptos Matrix\n    runs-on: ubuntu-latest\n    outputs:\n      matrix: ${{ steps.set-matrix.outputs.matrix }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Install jq\n        run: sudo apt-get install jq\n      - name: Set Matrix\n        id: set-matrix\n        run: |\n          crates=$(cargo metadata --no-deps --quiet --format-version 1 |\n          jq -r '.packages[] | select(.name != \"workspace\") | .manifest_path| rtrimstr(\"/Cargo.toml\")' |\n          sed \"s|$(pwd)/||\" |\n          jq -R -s -c 'split(\"\\n\")[:-1]')\n          echo \"Leptos Directories: $crates\"\n          echo \"matrix={\\\"directory\\\":$crates}\" >> \"$GITHUB_OUTPUT\"\n      - name: Print Location Info\n        run: |\n          echo \"Workspace: ${{ github.workspace }}\"\n          pwd\n          ls | sort -u\n"
  },
  {
    "path": ".github/workflows/publish-book.yml",
    "content": "name: Deploy book\non:\n  push:\n    paths: [\"docs/book/**\"]\n    branches:\n      - main\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write # To push a branch\n      pull-requests: write # To create a PR from that branch\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: Install mdbook\n        run: |\n          mkdir mdbook\n          curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.27/mdbook-v0.4.27-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook\n          echo `pwd`/mdbook >> $GITHUB_PATH\n      - name: Deploy GitHub Pages\n        run: |\n          cd docs/book\n          mdbook build\n          git worktree add gh-pages\n          git config user.name \"Deploy book from CI\"\n          git config user.email \"\"\n          cd gh-pages\n          # Delete the ref to avoid keeping history.\n          git update-ref -d refs/heads/gh-pages\n          rm -rf *\n          mv ../book/* .\n          git add .\n          git commit -m \"Deploy book $GITHUB_SHA to gh-pages\"\n          git push --force --set-upstream origin gh-pages\n"
  },
  {
    "path": ".github/workflows/run-cargo-make-task.yml",
    "content": "name: Run Task\non:\n  workflow_call:\n    inputs:\n      directory:\n        required: true\n        type: string\nenv:\n  CARGO_TERM_COLOR: always\n  CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse\n  DEBIAN_FRONTEND: noninteractive\n  RUSTFLAGS: ${{ inputs.erased_mode && '--cfg erase_components' || '' }}\n  LEPTOS_TAILWIND_VERSION: v4.0.14\n  LEPTOS_SASS_VERSION: 1.86.0\njobs:\n  test:\n    name: \"Run (${{ matrix.toolchain }}) (erased_mode: ${{ matrix.erased_mode && 'enabled' || 'disabled' }})\"\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        toolchain: [stable, nightly-2026-02-24]\n        erased_mode: [true, false]\n    steps:\n      - name: Free Disk Space\n        run: |\n          echo \"Disk space before cleanup:\"\n          df -h\n          sudo rm -rf /usr/local/.ghcup\n          sudo rm -rf /opt/hostedtoolcache/CodeQL\n          sudo rm -rf /usr/local/lib/android\n          sudo rm -rf /usr/share/dotnet\n          sudo rm -rf /opt/ghc\n          sudo rm -rf /usr/local/share/boost\n          sudo rm -rf /usr/local/lib/node_modules\n\n          # following lines currenly not needed as it takes too much time\n          # the new isolated CI doesn't need much space to test libraries\n          #\n          # uncommet only if nneded\n          #\n          # sudo apt-get clean\n          # sudo apt-get purge -y '^ghc-.*' '^dotnet-.*' '^llvm-.*' '^mono-.*' '^php.*' '^ruby.*'\n          # sudo apt-get autoremove -y\n          # sudo apt-get clean\n          # sudo rm -rf \"$AGENT_TOOLSDIRECTORY\"\n          # docker system prune -af\n          # docker image prune -af\n          # docker volume prune -f\n          echo \"Disk space after cleanup:\"\n          df -h\n      # Setup environment\n      - name: Install Glib\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y libglib2.0-dev\n      - uses: actions/checkout@v6\n      - name: Setup Rust\n        uses: dtolnay/rust-toolchain@master\n        with:\n          toolchain: ${{ matrix.toolchain }}\n          targets: wasm32-unknown-unknown\n          components: clippy,rustfmt\n      - name: Install binstall\n        uses: cargo-bins/cargo-binstall@main\n      - name: Install wasm-bindgen\n        run: cargo binstall wasm-bindgen-cli --no-confirm\n      - name: Install cargo-leptos\n        run: cargo binstall cargo-leptos --locked --no-confirm\n      - name: Install cargo-make\n        run: cargo binstall cargo-make --no-confirm\n      - name: Install nextest\n        run: cargo binstall cargo-nextest --locked --no-confirm\n      - name: Install cargo-all-features\n        run: cargo install cargo-all-features\n      # Part of direct-minimal-versions check\n      - name: Install cargo-hack\n        if: contains(matrix.toolchain, 'nightly')\n        uses: taiki-e/install-action@cargo-hack\n      # Part of direct-minimal-versions check\n      - name: Install cargo-minimal-versions\n        if: contains(matrix.toolchain, 'nightly')\n        uses: taiki-e/install-action@cargo-minimal-versions\n      - name: Install Trunk\n        if: contains(inputs.directory, 'examples')\n        run: cargo binstall trunk --no-confirm\n      - name: Print Trunk Version\n        if: contains(inputs.directory, 'examples')\n        run: trunk --version\n      - name: Install Node.js\n        if: contains(inputs.directory, 'examples')\n        uses: actions/setup-node@v6\n        with:\n          node-version: 20\n      - uses: pnpm/action-setup@v4\n        name: Install pnpm\n        if: contains(inputs.directory, 'examples')\n        id: pnpm-install\n        with:\n          version: 8\n          run_install: false\n      - name: Get pnpm store directory\n        if: contains(inputs.directory, 'examples')\n        id: pnpm-cache\n        run: |\n          echo \"STORE_PATH=$(pnpm store path)\" >> $GITHUB_OUTPUT\n      - uses: actions/cache@v5\n        if: contains(inputs.directory, 'examples')\n        name: Setup pnpm cache\n        with:\n          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}\n          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}\n          restore-keys: |\n            ${{ runner.os }}-pnpm-store-\n      - name: Maybe install chromedriver\n        if: contains(inputs.directory, 'examples')\n        run: |\n          project_makefile='${{inputs.directory}}/Makefile.toml'\n          webdriver_count=$(cat $project_makefile | grep \"cargo-make/webdriver.toml\" | wc -l)\n          if [ $webdriver_count -eq 1 ]; then\n              if ! command -v chromedriver &>/dev/null; then\n                  echo chromedriver required\n                  sudo apt-get update\n                  sudo apt-get install chromium-chromedriver\n              else\n                  echo chromedriver is already installed\n              fi\n          else\n              echo chromedriver is not required\n          fi\n      - name: Maybe install playwright browser dependencies\n        if: contains(inputs.directory, 'examples')\n        run: |\n          for pw_path in $(find '${{inputs.directory}}' -name playwright.config.ts)\n          do\n            pw_dir=$(dirname $pw_path)\n            if [ ! -v $pw_dir ]; then\n              echo \"Playwright required in $pw_dir\"\n              cd $pw_dir\n              pnpm dlx playwright install --with-deps\n            else\n              echo Playwright is not required\n            fi\n          done\n      - name: Install Deno\n        if: contains(inputs.directory, 'examples')\n        uses: denoland/setup-deno@v2\n        with:\n          deno-version: v1.x\n      - name: Maybe install gtk-rs dependencies\n        if: contains(inputs.directory, 'gtk')\n        run: |\n          sudo apt-get install -y libglib2.0-dev libgio2.0-cil-dev libgraphene-1.0-dev libcairo2-dev libpango1.0-dev libgtk-4-dev\n      - name: Install Tailwind and Sass dependencies\n        if: contains(inputs.directory, 'examples')\n        run: |\n          cd '${{ inputs.directory }}'\n          tailwindcss_version=$(echo \"$LEPTOS_TAILWIND_VERSION\" | sed 's/^v//')\n          sass_version=\"$LEPTOS_SASS_VERSION\"\n          pnpm add \"tailwindcss@$tailwindcss_version\" \"@tailwindcss/cli@$tailwindcss_version\" \"sass@$sass_version\"\n\n          echo \"Tailwind CSS version:\"\n          ./node_modules/.bin/tailwindcss --version\n\n          echo \"Sass version:\"\n          ./node_modules/.bin/sass --version\n      # Run Cargo Make Task\n      - name: ${{ inputs.cargo_make_task }}\n        run: |\n          cd '${{ inputs.directory }}'\n          cargo make --no-workspace --profile=github-actions ci\n          # check the direct-minimal-versions on release\n          COMMIT_MSG=$(git log -1 --pretty=format:'%s')\n          # Supports: v1.2.3, v1.2.3-alpha, v1.2.3-beta1, v1.2.3-rc.1, etc.\n          if [[ \"$COMMIT_MSG\" =~ ^v[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9]+(\\.?[0-9]+)?)?$ ]]; then\n            cargo make --no-workspace --profile=github-actions check-minimal-versions --direct\n          fi\n      # Check if the counter_isomorphic can be built with leptos_debuginfo cfg flag in release mode\n      - name: ${{ inputs.cargo_make_task }} with --cfg=leptos_debuginfo\n        if: contains(inputs.directory, 'counter_isomorphic')\n        run: |\n          cd '${{ inputs.directory }}'\n          RUSTFLAGS=\"$RUSTFLAGS --cfg leptos_debuginfo\" cargo leptos build --release\n      - name: Clean up ${{ inputs.directory }}\n        if: always()\n        run: |\n          cd '${{ inputs.directory }}'\n          cargo clean || true\n          rm -rf node_modules || true\n"
  },
  {
    "path": ".gitignore",
    "content": "target\ndist\npkg\ncomparisons\nblob.rs\n**/projects/**/Cargo.lock\n**/examples/**/Cargo.lock\n**/benchmarks/**/Cargo.lock\n**/*.rs.bk\n.DS_Store\n.idea\n.direnv\n.envrc\n\n.vscode\nvendor\nhash.txt\n"
  },
  {
    "path": "ARCHITECTURE.md",
    "content": "# Architecture\n\nThe goal of this document is to make it easier for contributors (and anyone\nwho’s interested!) to understand the architecture of the framework.\n\nThe whole Leptos framework is built from a series of layers. Each of these layers\ndepends on the one below it, but each can be used independently from the ones\nbuilt on top of it. While running a command like `cargo leptos new --git \nleptos-rs/start` pulls in the whole framework, it’s important to remember that\nnone of this is magic: each layer of that onion can be stripped away and\nreimplemented, configured, or adapted as needed, incrementally.\n\n> Everything that follows will assume you have a good working understanding\n> of the framework. There will be explanations of how some parts of it work\n> or fit together, but these are not docs. They assume you know what I’m\n> talking about.\n\n## The Reactive System: `leptos_reactive`\n\nThe reactive system allows you to define dynamic values (signals),\nthe relationships between them (derived signals and memos), and the side effects\nthat run in response to them (effects).\n\nThese concepts are completely independent of the DOM and can be used to drive\nany kind of reactive updates. The reactive system is based on the assumption\nthat data is relatively cheap, and side effects are relatively expensive. Its\ngoal is to minimize those side effects (like updating the DOM or making a network\nrequests) as infrequently as possible.\n\nThe reactive system is implemented as a single data structure that exists at\nruntime. In exchange for giving ownership over a value to the reactive system\n(by creating a signal), you receive a `Copy + 'static` identifier for its\nlocation in the reactive system. This enables most of the ergonomics of storing\nand sharing state, the use of callback closures without lifetime issues, etc.\nThis is implemented by storing signals in a slotmap arena. The signal, memo,\nand scope types that are exposed to users simply carry around an index into that\nslotmap.\n\n> Items owned by the reactive system are dropped when the corresponding reactive\n> scope is dropped, i.e., when the component or section of the UI they’re\n> created in is removed. In a sense, Leptos implements a “garbage collector”\n> in which the lifetime of data is tied to the lifetime of the UI, not Rust’s\n> lexical scopes.\n\n## The DOM Renderer: `leptos_dom`\n\nThe reactive system can be used to drive any kinds of side effects. One very\ncommon side effect is calling an imperative method, for example to update the\nDOM.\n\nThe entire DOM renderer is built on top of the reactive system. It provides\na builder pattern that can be used to create DOM elements dynamically.\n\nThe renderer assumes, as a convention, that dynamic attributes, classes,\nstyles, and children are defined by being passed a `Fn() -> T`, where their\nstatic equivalents just receive `T`. There’s nothing about this that is\ndivinely ordained, but it’s a useful convention because it allows us to use\nzero-overhead derived signals as one of several ways to indicate dynamic\ncontent.\n\n`leptos_dom` also contains code for server-side rendering of the same\nUI views to HTML, either for out-of-order streaming (`src/ssr.rs`) or\nin-order streaming/async rendering (`src/ssr_in_order.rs`).\n\n## The Macros: `leptos_macro`\n\nIt’s entirely possible to write Leptos code with no macros at all. The\n`view` and `component` macros, the most common, can be replaced by\nthe builder syntax and simple functions (see the `counter_without_macros`\nexample). But the macros enable a JSX-like syntax for describing views.\n\nThis package also contains the `Params` derive macro used for typed\nqueries and route params in the router.\n\n### Macro-based Optimizations\n\nLeptos 0.0.x was built much more heavily on macros. Taking its cues  \nfrom SolidJS, the `view` macro emitted different code for CSR, SSR, and\nhydration, optimizing each. The CSR/hydrate versions worked by compiling\nthe view to an HTML template string, cloning that `<template>`, and\ntraversing the DOM to set up reactivity. The SSR version worked similarly\nby compiling the static parts of the view to strings at compile time,\nreducing the amount of work that needed to be done on each request.\n\nProc macros are hard, and this system was brittle. 0.1 introduced a\nmore robust renderer, including the builder syntax, and rebuilt the `view`\nmacro to use that builder syntax instead. It moved the optimized-but-buggy\nCSR version of the macro to a more-limited `template` macro.\n\nThe `view` macro now separately optimizes SSR to use the same static-string\noptimizations, which (by our benchmarks) makes Leptos about 3-4x faster\nthan similar Rust frontend frameworks in its HTML rendering.\n\n> The optimization is pretty straightforward. Consider the following view:\n>\n> ```rust\n> view! { cx,\n>   <main class=\"text-center\">\n>     <div class=\"flex-col\">\n>       <button>\"Click me.\"</button>\n>       <p class=\"italic\">\"Text.\"</p>\n>     </div>\n>   </main>\n> }\n> ```\n>\n> Internally, with the builder this is something like\n>\n> ```rust\n> Element {\n>   tag: \"main\",\n>   attrs: vec![(\"class\", \"text-center\")],\n>   children: vec![\n> \t  Element {\n> \t\ttag: \"div\",\n> \t\tattrs: vec![(\"class\", \"flex-col\")],\n>       children: vec![\n>         Element {\n> \t        tag: \"button\",\n> \t\t\tattrs: vec![],\n> \t\t\tchildren: vec![\"Click me\"]\n>         },\n>         Element {\n> \t        tag: \"p\",\n> \t\t\tattrs: vec![(\"class\", \"italic\")],\n> \t\t\tchildren: vec![\"Text\"]\n>         }\n>       ]\n> \t  }\n>   ]\n> }\n> ```\n>\n> This is a _bunch_ of small allocations and separate strings,\n> and in early 0.1 versions we used a `SmallVec` for children and\n> attributes and actually caused some stack overflows.\n>\n> But if you look at the view itself you can see that none of this\n> will _ever_ change. So we can actually optimize it at compile\n> time to a single `&'static str`:\n>\n> ```rust\n> r#\"<main class=\"text-center\">\n>     <div class=\"flex-col\">\n>       <button>\"Click me.\"</button>\n>       <p class=\"italic\">\"Text.\"</p>\n>     </div>\n>   </main>\"#\n> ```\n\n## Server Functions (`leptos_server`, `server_fn`, and `server_fn_macro`)\n\nServer functions are a framework-agnostic shorthand for converting\na function, whose body can only be run on the server, into an ad hoc\nREST API endpoint, and then generating code on the client to call that\nendpoint when you call the function.\n\nThese are inspired by Solid/Bling’s `server$` functions, and there’s\nsimilar work being done in a number of other JavaScript frameworks.\n\nRPC is not a new idea, but these kinds of server functions may be.\nSpecifically, by using web standards (defaulting to `POST`/`GET` requests\nwith URL-encoded form data) they allow easy graceful degradation and the\nuse of the `<form>` element.\n\nThis function is split across three packages so that `server_fn` and\n`server_fn_macro` can be used by other frameworks. `leptos_server`\nincludes some Leptos-specific reactive functionality (like actions).\n\n## `leptos`\n\nThis package is built on and reexports most of the layers already\nmentioned, and implements a number of control-flow components (`<Show/>`,\n`<ErrorBoundary/>`, `<For/>`, `<Suspense/>`, `<Transition/>`) that use\npublic APIs of the other packages.\n\nThis is the main entrypoint for users, but is relatively light itself.\n\n## `leptos_meta`\n\nThis package exists to allow you to work with tags normally found in\nthe `<head>`, from within your components.\n\nIt is implemented as a distinct package, rather than part of\n`leptos_dom`, on the principle that “what can be implemented in userland,\nshould be.” The framework can be used without it, so it’s not in core.\n\n## `leptos_router`\n\nThe router originates as a direct port of `solid-router`, which is the\norigin of most of its terminology, architecture, and route-matching logic.\n\nSubsequent developments (like animated routing, and managing route transitions\ngiven the lack of `useTransition` in Leptos) have caused it to diverge\nslightly from Solid’s exact code, but it is still very closely related.\n\nThe core principle here is “nested routing,” dividing a single page\ninto independently-rendered parts. This is described in some detail in the docs.\n\nLike `leptos_meta`, it is implemented as a distinct package, because it\ncan be replaced with another router or with none. The framework can be used\nwithout it, so it’s not in core.\n\n## Server Integrations\n\nThe server integrations are the most “frameworky” layer of the whole framework.\nThese **do** assume the use of `leptos`, `leptos_router`, and `leptos_meta`.\nThey specifically draw routing data from `leptos_router`, and inject the\nmetadata from `leptos_meta` into the `<head>` appropriately.\n\nBut of course, if you one day create `leptos-helmet` and `leptos-better-router`,\nyou can create new server integrations that plug them into the SSR rendering\nmethods from `leptos_dom` instead. Everything involved is quite modular.\n\nThese packages essentially provide helpers that save the templates and user apps\nfrom including a huge amount of boilerplate to connect the various other packages\ncorrectly. Again, early versions of the framework examples are illustrative here\nfor reference: they include large amounts of manual SSR route handling, etc.\n\n## `cargo-leptos` helpers\n\n`leptos_config` and `leptos_hot_reload` exist to support two different features\nof `cargo-leptos`, namely its configuration and its view-patching/hot-reloading \nfeatures.\n\nIt’s important to say that the main feature `cargo-leptos` remains its ability\nto conveniently tie together different build tooling, compiling your app to\nWASM for the browser, building the server version, pulling in SASS and\nTailwind, etc. It is an extremely good build tool, not a magic formula. Each\nof the examples includes instructions for how to run the examples without\n`cargo-leptos`.\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n_This Code of Conduct is based on the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct)\nand the [Bevy Code of Conduct](https://raw.githubusercontent.com/bevyengine/bevy/main/CODE_OF_CONDUCT.md),\nwhich are adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling)\nand the [Contributor Covenant](https://www.contributor-covenant.org)._\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nWe are a community of people learning and exploring how to build better web applications\nwith Rust. When interacting with one another, please remember that there are no experts and there are\nno stupid questions. Assume the best in other people's communication, and take a step back if\nyou find yourself getting defensive.\n\nPlease note the following guidelines as well:\n\n* Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all.\n* Please be kind and courteous. There’s no need to be mean or rude.\n* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.\n* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.\n* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term “harassment” as including the definition in the [Citizen Code of Conduct](https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behavior that excludes people in socially marginalized groups.\n* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact the maintainers immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back.\n* Do not make casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology.\n* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.\n\n## Moderation\n\nThese are the policies for upholding [our community’s standards of conduct](#our-standards). If you feel that a thread needs moderation, please contact the maintainers.\n\n1. Remarks that violate the community standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner).\n2. Remarks that maintainers find inappropriate, whether listed in the code of conduct or not, are also not allowed.\n3. Maintainers will first respond to such remarks with a warning.\n4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off.\n5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.\n6. Maintainers may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.\n7. If a maintainer bans someone and you think it was unjustified, please take it up with that maintainer, or with a different maintainer, in private. Complaints about bans in-channel are not allowed.\n8. Maintainers are held to a higher standard than other community members. If a maintainer creates an inappropriate situation, they should expect less leeway than others.\n\nThe enforcement policies in the code of conduct apply to all official venues, including Discord channels, GitHub repositories, and all other forums.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Leptos\n\nThanks for your interesting in contributing to Leptos! This is a truly\ncommunity-driven framework, and while we have a central maintainer (@gbj)\nlarge parts of the renderer, reactive system, and server integrations have\nall been written by other contributors. Contributions are always welcome.\n\nParticipation in this community is governed by a [Code of Conduct](./CODE_OF_CONDUCT.md).\nSome of the most active conversations around development take place on our\n[Discord server](https://discord.gg/YdRAhS7eQB).\n\nThis guide seeks to\n\n- describe some of the framework’s values (in a technical, not an ethical, sense)\n- provide a high-level overview of how the pieces of the framework fit together\n- orient you to the organization of this repository\n\n## Values\n\nLeptos, as a framework, reflects certain technical values:\n\n- **Expose primitives rather than imposing patterns.** Provide building blocks\n  that users can combine together to build up more complex behavior, rather than\n  requiring users follow certain templates, file formats, etc. e.g., components\n  are defined as functions, rather than a bespoke single-file component format.\n  The reactive system feeds into the rendering system, rather than being defined\n  by it.\n- **Bottom-up over top-down.** If you envision a user’s application as a tree\n  (like an HTML document), push meaning toward the leaves of the tree. e.g., If data\n  needs to be loaded, load it in a granular primitive (resources) rather than a\n  route- or page-level data structure.\n- **Performance by default.** When possible, users should only pay for what they\n  use. e.g., we don’t make all component props reactive by default. This is\n  because doing so would force the overhead of a reactive prop onto props that don’t\n  need to be reactive.\n- **Full-stack performance.** Performance can’t be limited to a single metric,\n  whether that’s a DOM rendering benchmark, WASM binary size, or server response\n  time. Use methods like HTTP streaming and progressive enhancement to enable\n  applications to load, become interactive, and respond as quickly as possible.\n- **Use safe Rust.** There’s no need for `unsafe` Rust in the framework, and\n  avoiding it at all costs reduces the maintenance and testing burden significantly.\n- **Embrace Rust semantics.** Especially in things like UI templating, use Rust\n  semantics or extend them in a predictable way with control-flow components\n  rather than overloading the meaning of Rust terms like `if` or `for` in a\n  framework-specific way.\n- **Enhance ergonomics without obfuscating what’s happening.** This is by far\n  the hardest to achieve. It’s often the case that adding additional layers to\n  improve DX (like a custom build tool and starter templates) comes across as\n  “too magic” to some people who haven’t had to build the same things manually.\n  When possible, make it easier to see how the pieces fit together, without\n  sacrificing the improved DX.\n\n## Processes\n\nWe do not have PR templates or formal processes for approving PRs. But there\nare a few guidelines that will make it a better experience for everyone:\n\n- Run `cargo fmt` before submitting your code.\n- Keep PRs limited to addressing one feature or one issue, in general. In some\n  cases (e.g., “reduce allocations in the reactive system”) this may touch a number\n  of different areas, but is still conceptually one thing.\n- If it’s an unsolicited PR not linked to an open issue, please include a\n  specific explanation for what it’s trying to achieve. For example: “When I\n  was trying to deploy my app under _circumstances X_, I found that the way\n  _function Y_ was implemented caused _issue Z_. This PR should fix that by\n  _solution._”\n- Our CI tests every PR against all the existing examples, sometimes requiring\n  compilation for both server and client side, etc. It’s thorough but slow. If\n  you want to run CI locally to reduce frustration, you can do that by installing\n  `cargo-make` and using `cargo make check && cargo make test && cargo make\ncheck-examples`.\n\n## Before Submitting a PR\n\nWe have a fairly extensive CI setup that runs both lints (like `rustfmt` and `clippy`)\nand tests on PRs. You can run most of these locally if you have `cargo-make` installed.\n\nNote that some of the `rustfmt` settings used require usage of the nightly compiler.\nFormatting the code using the stable toolchain may result in a wrong code format and\nsubsequently CI errors.\nRun `cargo +nightly fmt` if you want to keep the stable toolchain active.\nYou may want to let your IDE automatically use the `+nightly` parameter when a\n\"format on save\" action is used.\n\nIf you added an example, make sure to add it to the list in `examples/Makefile.toml`.\n\nFrom the root directory of the repo, run\n- `cargo +nightly fmt`\n- `cargo +nightly make check`\n- `cargo +nightly make test`\n- `cargo +nightly make check-examples`\n- `cargo +nightly make --profile=github-actions ci`\n\nIf you modified an example:\n- `cd examples/your_example`\n- `cargo +nightly fmt -- --config-path ../..`\n- `cargo +nightly make --profile=github-actions verify-flow`\n\n## Architecture\n\nSee [ARCHITECTURE.md](./ARCHITECTURE.md).\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n  # utilities\n  \"any_spawner\",\n  \"const_str_slice_concat\",\n  \"either_of\",\n  \"next_tuple\",\n  \"oco\",\n  \"or_poisoned\",\n\n  # core\n  \"hydration_context\",\n  \"leptos\",\n  \"leptos_dom\",\n  \"leptos_config\",\n  \"leptos_hot_reload\",\n  \"leptos_macro\",\n  \"leptos_server\",\n  \"reactive_graph\",\n  \"reactive_stores\",\n  \"reactive_stores_macro\",\n  \"server_fn\",\n  \"server_fn_macro\",\n  \"server_fn/server_fn_macro_default\",\n  \"tachys\",\n\n  # integrations\n  \"integrations/actix\",\n  \"integrations/axum\",\n  \"integrations/utils\",\n\n  # libraries\n  \"meta\",\n  \"router\",\n  \"router_macro\",\n  \"any_error\",\n]\nexclude = [\"benchmarks\", \"examples\", \"projects\"]\n\n[workspace.package]\nedition = \"2021\"\nrust-version = \"1.88\"\n\n[workspace.dependencies]\n# members\nthrow_error = { path = \"./any_error/\", version = \"0.3.1\" }\nany_spawner = { path = \"./any_spawner/\", version = \"0.3.0\" }\nconst_str_slice_concat = { path = \"./const_str_slice_concat\", version = \"0.1\" }\neither_of = { path = \"./either_of/\", version = \"0.1.8\" }\nhydration_context = { path = \"./hydration_context\", version = \"0.3.0\" }\nleptos = { path = \"./leptos\", version = \"0.8.17\" }\nleptos_config = { path = \"./leptos_config\", version = \"0.8.9\" }\nleptos_dom = { path = \"./leptos_dom\", version = \"0.8.8\" }\nleptos_hot_reload = { path = \"./leptos_hot_reload\", version = \"0.8.6\" }\nleptos_integration_utils = { path = \"./integrations/utils\", version = \"0.8.8\" }\nleptos_macro = { path = \"./leptos_macro\", version = \"0.8.15\" }\nleptos_router = { path = \"./router\", version = \"0.8.12\" }\nleptos_router_macro = { path = \"./router_macro\", version = \"0.8.6\" }\nleptos_server = { path = \"./leptos_server\", version = \"0.8.7\" }\nleptos_meta = { path = \"./meta\", version = \"0.8.6\" }\nnext_tuple = { path = \"./next_tuple\", version = \"0.1.0\" }\noco_ref = { path = \"./oco\", version = \"0.2.1\" }\nor_poisoned = { path = \"./or_poisoned\", version = \"0.1.0\" }\nreactive_graph = { path = \"./reactive_graph\", version = \"0.2.13\" }\nreactive_stores = { path = \"./reactive_stores\", version = \"0.4.2\" }\nreactive_stores_macro = { path = \"./reactive_stores_macro\", version = \"0.4.1\" }\nserver_fn = { path = \"./server_fn\", version = \"0.8.11\" }\nserver_fn_macro = { path = \"./server_fn_macro\", version = \"0.8.10\" }\nserver_fn_macro_default = { path = \"./server_fn/server_fn_macro_default\", version = \"0.8.5\" }\ntachys = { path = \"./tachys\", version = \"0.2.14\" }\n\n# members deps\nasync-once-cell = { default-features = false, version = \"0.5\" }\nitertools = { default-features = false, version = \"0.14\" }\nconvert_case = { default-features = false, version = \"0.11\" }\nconvert_case_extras = { default-features = false, version = \"0.2\" }\nserde_json = { default-features = false, version = \"1.0\" }\ntrybuild = { default-features = false, version = \"1.0\" }\ntyped-builder = { default-features = false, version = \"0.23\" }\ntyped-builder-macro = { default-features = false, version = \"0.23\" }\nthiserror = { default-features = false, version = \"2.0\" }\nwasm-bindgen = { default-features = false, version = \"0.2\" }\nindexmap = { default-features = false, version = \"2.13\" }\nrstml = { default-features = false, version = \"0.12\" }\nrustc_version = { default-features = false, version = \"0.4\" }\nguardian = { default-features = false, version = \"1.3\" }\nrustc-hash = { default-features = false, version = \"2.1\" }\nactix-web = { default-features = false, version = \"4.12\" }\ntracing = { default-features = false, version = \"0.1\" }\nslotmap = { default-features = false, version = \"1.1\" }\nfutures = { default-features = false, version = \"0.3\" }\npin-project-lite = { default-features = false, version = \"0.2\" }\nsend_wrapper = { default-features = false, version = \"0.6\" }\ntokio-test = { default-features = false, version = \"0.4\" }\nhtml-escape = { default-features = false, version = \"0.2\" }\nproc-macro-error2 = { default-features = false, version = \"2.0\" }\nconst_format = { default-features = false, version = \"0.2\" }\ngloo-net = { default-features = false, version = \"0.6\" }\nurl = { default-features = false, version = \"2.5\" }\ntokio = { default-features = false, version = \"1.49\" }\nbase64 = { default-features = false, version = \"0.22\" }\ncfg-if = { default-features = false, version = \"1.0\" }\nwasm-bindgen-futures = { default-features = false, version = \"0.4\" }\ntower = { default-features = false, version = \"0.5\" }\nproc-macro2 = { default-features = false, version = \"1.0\" }\nserde = { default-features = false, version = \"1.0\" }\nparking_lot = { default-features = false, version = \"0.12\" }\naxum = { default-features = false, version = \"0.8\" }\nserde_qs = { default-features = false, version = \"0.15\" }\nsyn = { default-features = false, version = \"2.0\" }\nxxhash-rust = { default-features = false, version = \"0.8\" }\npaste = { default-features = false, version = \"1.0\" }\nquote = { default-features = false, version = \"1.0\" }\nweb-sys = { default-features = false, version = \"0.3\" }\njs-sys = { default-features = false, version = \"0.3\" }\nrand = { default-features = false, version = \"0.9\" }\nserde-lite = { default-features = false, version = \"0.5\" }\ntokio-tungstenite = { default-features = false, version = \"0.28\" }\nserial_test = { default-features = false, version = \"3.3\" }\nerased = { default-features = false, version = \"0.1\" }\nglib = { default-features = false, version = \"0.21\" }\nasync-trait = { default-features = false, version = \"0.1\" }\nanyhow = { default-features = false, version = \"1.0\" }\nwalkdir = { default-features = false, version = \"2.5\" }\nactix-ws = { default-features = false, version = \"0.3\" }\ntower-http = { default-features = false, version = \"0.6\" }\nprettyplease = { default-features = false, version = \"0.2\" }\ninventory = { default-features = false, version = \"0.3\" }\nconfig = { default-features = false, version = \"0.15\" }\ncamino = { default-features = false, version = \"1.2\" }\nciborium = { default-features = false, version = \"0.2\" }\nbitcode = { default-features = false, version = \"0.6\" }\nmulter = { default-features = false, version = \"3.1\" }\nleptos-spin-macro = { default-features = false, version = \"0.2\" }\nsledgehammer_utils = { default-features = false, version = \"0.3\" }\nsledgehammer_bindgen = { default-features = false, version = \"0.6\" }\nwasm-streams = { default-features = false, version = \"0.5\" }\nrkyv = { default-features = false, version = \"0.8\" }\ntemp-env = { default-features = false, version = \"0.3\" }\nuuid = { default-features = false, version = \"1.20\" }\nbytes = { default-features = false, version = \"1.11\" }\nhttp = { default-features = false, version = \"1.4\" }\nregex = { default-features = false, version = \"1.12\" }\ndrain_filter_polyfill = { default-features = false, version = \"0.1\" }\ntempfile = { default-features = false, version = \"3.24\" }\nfutures-lite = { default-features = false, version = \"2.6\" }\nlog = { default-features = false, version = \"0.4\" }\npercent-encoding = { default-features = false, version = \"2.3\" }\nasync-executor = { default-features = false, version = \"1.13\" }\nconst-str = { default-features = false, version = \"1.1\" }\nhttp-body-util = { default-features = false, version = \"0.1\" }\nhyper = { default-features = false, version = \"1.8\" }\npostcard = { default-features = false, version = \"1.1\" }\nrmp-serde = { default-features = false, version = \"1.3\" }\nreqwest = { default-features = false, version = \"0.13\" }\ntower-layer = { default-features = false, version = \"0.3\" }\nattribute-derive = { default-features = false, version = \"0.10\" }\ninsta = { default-features = false, version = \"1.46\" }\ncodee = { default-features = false, version = \"0.3\" }\nactix-http = { default-features = false, version = \"3.11\" }\nwasm-bindgen-test = { default-features = false, version = \"0.3\" }\nrustversion = { default-features = false, version = \"1.0\" }\ngetrandom = { default-features = false, version = \"0.4\" }\nactix-files = { default-features = false, version = \"0.6\" }\nasync-lock = { default-features = false, version = \"3.4\" }\nbase16 = { default-features = false, version = \"0.2\" }\ndigest = { default-features = false, version = \"0.10\" }\nsha2 = { default-features = false, version = \"0.10\" }\nsubsecond = { default-features = false, version = \"0.7\" }\ndioxus-cli-config = { default-features = false, version = \"0.7\" }\ndioxus-devtools = { default-features = false, version = \"0.7\" }\nwasm_split_helpers = { default-features = false, version = \"0.2\" }\n\n[profile.release]\ncodegen-units = 1\nlto = true\nopt-level = 'z'\n\n[workspace.metadata.cargo-all-features]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"]]\nmax_combination_size = 2\n\n[workspace.lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = [\n  'cfg(leptos_debuginfo)',\n  'cfg(erase_components)',\n] }\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile.toml",
    "content": "############\n# A make file for cargo-make, please install it with:\n#     cargo install --force cargo-make\n############\n\n[env]\nCARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true\n\n[tasks.check-stable]\nworkspace = false\nclear = true\ndependencies = [\n  { name = \"lint\", path = \"examples/counter_without_macros\" },\n  { name = \"lint\", path = \"examples/counters_stable\" },\n]\n\n[tasks.ci-examples]\nworkspace = false\ncwd = \"examples\"\ncommand = \"cargo\"\nargs = [\"make\", \"ci-clean\"]\n\n[tasks.check-examples]\nworkspace = false\ncwd = \"examples\"\ncommand = \"cargo\"\nargs = [\"make\", \"check-clean\"]\n\n[tasks.build-examples]\nworkspace = false\ncwd = \"examples\"\ncommand = \"cargo\"\nargs = [\"make\", \"build-clean\"]\n\n[tasks.clean-examples]\nworkspace = false\ncwd = \"examples\"\ncommand = \"cargo\"\nargs = [\"make\", \"clean\"]\n"
  },
  {
    "path": "README.md",
    "content": "<picture>\n    <source srcset=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_pref_dark_RGB.svg\" media=\"(prefers-color-scheme: dark)\">\n    <img src=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg\" alt=\"Leptos Logo\">\n</picture>\n\n[![crates.io](https://img.shields.io/crates/v/leptos.svg)](https://crates.io/crates/leptos)\n[![docs.rs](https://docs.rs/leptos/badge.svg)](https://docs.rs/leptos)\n![Crates.io MSRV](https://img.shields.io/crates/msrv/leptos)\n[![Discord](https://img.shields.io/discord/1031524867910148188?color=%237289DA&label=discord)](https://discord.gg/YdRAhS7eQB)\n[![Matrix](https://img.shields.io/badge/Matrix-leptos-grey?logo=matrix&labelColor=white&logoColor=black)](https://matrix.to/#/#leptos:matrix.org)\n\n[Website](https://leptos.dev) | [Book](https://leptos-rs.github.io/leptos/) | [Docs.rs](https://docs.rs/leptos/latest/leptos/) | [Playground](https://codesandbox.io/p/devbox/playground-j23dz7?file=%2Fsrc%2Fmain.rs) | [Discord](https://discord.gg/YdRAhS7eQB)\n\nYou can find a list of useful libraries and example projects at [`awesome-leptos`](https://github.com/leptos-rs/awesome-leptos).\n\n# Leptos\n\n```rust\nuse leptos::*;\n\n#[component]\npub fn SimpleCounter(initial_value: i32) -> impl IntoView {\n    // create a reactive signal with the initial value\n    let (value, set_value) = signal(initial_value);\n\n    // create event handlers for our buttons\n    // note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures\n    let clear = move |_| set_value(0);\n    let decrement = move |_| set_value.update(|value| *value -= 1);\n    let increment = move |_| set_value.update(|value| *value += 1);\n\n    // create user interfaces with the declarative `view!` macro\n    view! {\n        <div>\n            <button on:click=clear>Clear</button>\n            <button on:click=decrement>-1</button>\n            // text nodes can be quoted or unquoted\n            <span>\"Value: \" {value} \"!\"</span>\n            <button on:click=increment>+1</button>\n        </div>\n    }\n}\n\n// we also support a builder syntax rather than the JSX-like `view` macro\n#[component]\npub fn SimpleCounterWithBuilder(initial_value: i32) -> impl IntoView {\n    use leptos::html::*;\n\n    let (value, set_value) = signal(initial_value);\n    let clear = move |_| set_value(0);\n    let decrement = move |_| set_value.update(|value| *value -= 1);\n    let increment = move |_| set_value.update(|value| *value += 1);\n\n    // the `view` macro above expands to this builder syntax\n    div().child((\n        button().on(ev::click, clear).child(\"Clear\"),\n        button().on(ev::click, decrement).child(\"-1\"),\n        span().child((\"Value: \", value, \"!\")),\n        button().on(ev::click, increment).child(\"+1\")\n    ))\n}\n\n// Easy to use with Trunk (trunkrs.dev) or with a simple wasm-bindgen setup\npub fn main() {\n    mount_to_body(|| view! {\n        <SimpleCounter initial_value=3 />\n    })\n}\n```\n\n## About the Framework\n\nLeptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces.\n\n## What does that mean?\n\n- **Full-stack**: Leptos can be used to build apps that run in the browser (client-side rendering), on the server (server-side rendering), or by rendering HTML on the server and then adding interactivity in the browser (server-side rendering with hydration). This includes support for HTTP streaming of both data ([`Resource`s](https://docs.rs/leptos/latest/leptos/prelude/struct.Resource.html)) and HTML (out-of-order or in-order streaming of [`<Suspense/>`](https://docs.rs/leptos/latest/leptos/suspense/fn.Suspense.html) components.)\n- **Isomorphic**: Leptos provides primitives to write isomorphic [server functions](https://docs.rs/server_fn/latest/server_fn/), i.e., functions that can be called with the “same shape” on the client or server, but only run on the server. This means you can write your server-only logic (database requests, authentication etc.) alongside the client-side components that will consume it, and call server functions as if they were running in the browser, without needing to create and maintain a separate REST or other API.\n- **Web**: Leptos is built on the Web platform and Web standards. The [router](https://docs.rs/leptos_router/latest/leptos_router/) is designed to use Web fundamentals (like links and forms) and build on top of them rather than trying to replace them.\n- **Framework**: Leptos provides most of what you need to build a modern web app: a reactive system, templating library, and a router that works on both the server and client side.\n- **Fine-grained reactivity**: The entire framework is built from reactive primitives. This allows for extremely performant code with minimal overhead: when a reactive signal’s value changes, it can update a single text node, toggle a single class, or remove an element from the DOM without any other code running. (So, no virtual DOM overhead!)\n- **Declarative**: Tell Leptos how you want the page to look, and let the framework tell the browser how to do it.\n\n## Learn more\n\nHere are some resources for learning more about Leptos:\n\n- [Book](https://leptos-rs.github.io/leptos/) (work in progress)\n- [Examples](https://github.com/leptos-rs/leptos/tree/main/examples)\n- [API Documentation](https://docs.rs/leptos/latest/leptos/)\n- [Common Bugs](https://github.com/leptos-rs/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)\n\n### Random numbers on wasm (`rand` / `getrandom`)\n\nWhen you compile a Leptos app to `wasm32-unknown-unknown`, `rand` and `getrandom` need a JavaScript-backed source of randomness. If that backend isn’t enabled, your build can fail or randomness just won’t work in the browser.\n\nLeptos itself takes care of this for its own code, but that does **not** automatically configure your app’s own `rand` / `getrandom` dependencies. If you use them directly, you need to turn on the JS backend yourself.\n\nA simple setup in your `Cargo.toml` might look like this:\n\n```toml\n[dependencies]\n# Make sure getrandom works on wasm by enabling its JS backend\ngetrandom = { version = \"0.2\", features = [\"js\"] }\nrand      = { version = \"0.8\", features = [\"small_rng\"] }\n```\n\nSome of the examples in this repo (for example `js-framework-benchmark` and `hackernews_js_fetch`) already do this, so you can use them as a reference if you’re unsure.\n\n## `cargo-leptos`\n\n[`cargo-leptos`](https://github.com/leptos-rs/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` and our starter templates for [Actix](https://github.com/leptos-rs/start) or [Axum](https://github.com/leptos-rs/start-axum).\n\n```bash\ncargo install cargo-leptos --locked\ncargo leptos new --git https://github.com/leptos-rs/start-axum\ncd [your project name]\ncargo leptos watch\n```\n\nOpen browser to [http://localhost:3000/](http://localhost:3000/).\n\n## FAQs\n\n### What’s up with the name?\n\n_Leptos_ (λεπτός) is an ancient Greek word meaning “thin, light, refined, fine-grained.” To me, a classicist and not a dog owner, it evokes the lightweight reactive system that powers the framework. I've since learned the same word is at the root of the medical term “leptospirosis,” a blood infection that affects humans and animals... My bad. No dogs were harmed in the creation of this framework.\n\n### Is it production ready?\n\nPeople usually mean one of three things by this question.\n\n1. **Are the APIs stable?** i.e., will I have to rewrite my whole app from Leptos 0.1 to 0.2 to 0.3 to 0.4, or can I write it now and benefit from new features and updates as new versions come?\n\nThe APIs are basically settled. We’re adding new features, but we’re very happy with where the type system and patterns have landed. I would not expect major breaking changes to your code to adapt to future releases, in terms of architecture.\n\n2. **Are there bugs?**\n\nYes, I’m sure there are. You can see from the state of our issue tracker over time that there aren’t that _many_ bugs and they’re usually resolved pretty quickly. But for sure, there may be moments where you encounter something that requires a fix at the framework level, which may not be immediately resolved.\n\n3. **Am I a consumer or a contributor?**\n\nThis may be the big one: “production ready” implies a certain orientation to a library: that you can basically use it, without any special knowledge of its internals or ability to contribute. Everyone has this at some level in their stack: for example I (@gbj) don’t have the capacity or knowledge to contribute to something like `wasm-bindgen` at this point: I simply rely on it to work.\n\nThere are several people in the community using Leptos right now for many websites at work, who have also become significant contributors. There may be missing features that you need, and you may end up building them! But, if you're willing to contribute a few missing pieces along the way, the framework is most definitely usable for production applications, especially given the ecosystem of libraries that have sprung up around it.\n\n### Can I use this for native GUI?\n\nSure! Obviously the `view` macro is for generating DOM nodes but you can use the reactive system to drive any native GUI toolkit that uses the same kind of object-oriented, event-callback-based framework as the DOM pretty easily. The principles are the same:\n\n- Use signals, derived signals, and memos to create your reactive system\n- Create GUI widgets\n- Use event listeners to update signals\n- Create effects to update the UI\n\nThe 0.7 update originally set out to create a \"generic rendering\" approach that would allow us to reuse most of the same view logic to do all of the above. Unfortunately, this has had to be shelved for now due to difficulties encountered by the Rust compiler when building larger-scale applications with the number of generics spread throughout the codebase that this required. It's an approach I'm looking forward to exploring again in the future; feel free to reach out if you're interested in this kind of work.\n\n### How is this different from Yew?\n\nYew is the most-used library for Rust web UI development, but there are several differences between Yew and Leptos, in philosophy, approach, and performance.\n\n- **VDOM vs. fine-grained:** Yew is built on the virtual DOM (VDOM) model: state changes cause components to re-render, generating a new virtual DOM tree. Yew diffs this against the previous VDOM, and applies those patches to the actual DOM. Component functions rerun whenever state changes. Leptos takes an entirely different approach. Components run once, creating (and returning) actual DOM nodes and setting up a reactive system to update those DOM nodes.\n- **Performance:** This has huge performance implications: Leptos is simply much faster at both creating and updating the UI than Yew is.\n- **Server integration:** Yew was created in an era in which browser-rendered single-page apps (SPAs) were the dominant paradigm. While Leptos supports client-side rendering, it also focuses on integrating with the server side of your application via server functions and multiple modes of serving HTML, including out-of-order streaming.\n\n### How is this different from Dioxus?\n\nLike Leptos, Dioxus is a framework for building UIs using web technologies. However, there are significant differences in approach and features.\n\n- **VDOM vs. fine-grained:** While Dioxus has a performant virtual DOM (VDOM), it still uses coarse-grained/component-scoped reactivity: changing a stateful value reruns the component function and diffs the old UI against the new one. Leptos components use a different mental model, creating (and returning) actual DOM nodes and setting up a reactive system to update those DOM nodes.\n- **Web vs. desktop priorities:** Dioxus uses Leptos server functions in its fullstack mode, but does not have the same `<Suspense>`-based support for things like streaming HTML rendering, or share the same focus on holistic web performance. Leptos tends to prioritize holistic web performance (streaming HTML rendering, smaller WASM binary sizes, etc.), whereas Dioxus has an unparalleled experience when building desktop apps, because your application logic runs as a native Rust binary.\n\n### How is this different from Sycamore?\n\nSycamore and Leptos are both heavily influenced by SolidJS. At this point, Leptos has a larger community and ecosystem and is more actively developed. Other differences:\n\n- **Templating DSLs:** Sycamore uses a custom templating language for its views, while Leptos uses a JSX-like template format.\n- **`'static` signals:** One of Leptos’s main innovations was the creation of `Copy + 'static` signals, which have excellent ergonomics. Sycamore is in the process of adopting the same pattern, but this is not yet released.\n- **Perseus vs. server functions:** The Perseus metaframework provides an opinionated way to build Sycamore apps that include server functionality. Leptos instead provides primitives like server functions in the core of the framework.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nTo report a suspected security issue, please contact security@leptos.dev rather than opening\na public issue.\n\n## Supported Versions\n\nThe most-recently-released version of the library is supported with security updates.\nFor example, if a security issue is discovered that affects 0.3.2 and all later releases,\na 0.4.x patch will be released but a new 0.3.x patch release will not be made. You should \nplan to update to the latest version to receive any new features or bugfixes of any kind.\n"
  },
  {
    "path": "TODO.md",
    "content": "- core examples\n    - [x] counter\n    - [x] counters\n    - [x] fetch\n    - [x] todomvc \n    - [x] error_boundary\n    - [x] parent\\_child\n        - [x] on: on components\n    - [ ] router\n    - [ ] slots \n    - [ ] hackernews\n    - [ ] counter\\_isomorphic\n    - [ ] todo\\_app\\_sqlite\n- other ssr examples\n    - [ ] error boundary SSR\n- reactivity \n    - Signal wrappers\n    - SignalDispose implementations on all Copy types\n    - untracked access warnings\n- ErrorBoundary\n    - [ ] RenderHtml implementation \n    - [ ] Separate component?\n- Suspense/Transition components?\n- callbacks\n    - unsync StoredValue\n- SSR\n    - escaping HTML correctly (attributes + text nodes)\n- router\n    - nested routes\n    - trailing slashes\n- \\_meta package (and use in hackernews)\n- integrations\n- update tests\n- hackernews example\n  - TODOs\n  - Suspense/Transition/Await components\n  - nicer routing components\n  - async routing (waiting for data to load before navigation)\n  - `<A>` component\n  - figure out rebuilding issues: list (needs new signal IDs) vs. regular rebuild\n"
  },
  {
    "path": "any_error/Cargo.toml",
    "content": "[package]\nname = \"throw_error\"\nversion = \"0.3.1\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Utilities for wrapping, throwing, and catching errors.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\npin-project-lite = { workspace = true, default-features = true }\n\n[dev-dependencies]\nanyhow.workspace = true\n"
  },
  {
    "path": "any_error/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "any_error/README.md",
    "content": "A utility library for wrapping arbitrary errors, and for “throwing” errors in a way\nthat can be caught by user-defined error hooks.\n"
  },
  {
    "path": "any_error/src/lib.rs",
    "content": "#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\n//! A utility library for wrapping arbitrary errors, and for “throwing” errors in a way\n//! that can be caught by user-defined error hooks.\n\nuse std::{\n    cell::RefCell,\n    error,\n    fmt::{self, Display},\n    future::Future,\n    ops,\n    pin::Pin,\n    sync::Arc,\n    task::{Context, Poll},\n};\n\n/* Wrapper Types */\n\n/// A generic wrapper for any error.\n#[derive(Debug, Clone)]\n#[repr(transparent)]\npub struct Error(Arc<dyn error::Error + Send + Sync>);\n\nimpl Error {\n    /// Converts the wrapper into the inner reference-counted error.\n    pub fn into_inner(self) -> Arc<dyn error::Error + Send + Sync> {\n        Arc::clone(&self.0)\n    }\n}\n\nimpl ops::Deref for Error {\n    type Target = Arc<dyn error::Error + Send + Sync>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\n\nimpl<T> From<T> for Error\nwhere\n    T: Into<Box<dyn error::Error + Send + Sync + 'static>>,\n{\n    fn from(value: T) -> Self {\n        Error(Arc::from(value.into()))\n    }\n}\n\n/// Implements behavior that allows for global or scoped error handling.\n///\n/// This allows for both \"throwing\" errors to register them, and \"clearing\" errors when they are no\n/// longer valid. This is useful for something like a user interface, in which an error can be\n/// \"thrown\" on some invalid user input, and later \"cleared\" if the user corrects the input.\n/// Keeping a unique identifier for each error allows the UI to be updated accordingly.\npub trait ErrorHook: Send + Sync {\n    /// Handles the given error, returning a unique identifier.\n    fn throw(&self, error: Error) -> ErrorId;\n\n    /// Clears the error associated with the given identifier.\n    fn clear(&self, id: &ErrorId);\n}\n\n/// A unique identifier for an error. This is returned when you call [`throw`], which calls a\n/// global error handler.\n#[derive(Debug, PartialEq, Eq, Hash, Clone, Default)]\npub struct ErrorId(usize);\n\nimpl Display for ErrorId {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(&self.0, f)\n    }\n}\n\nimpl From<usize> for ErrorId {\n    fn from(value: usize) -> Self {\n        Self(value)\n    }\n}\n\nthread_local! {\n    static ERROR_HOOK: RefCell<Option<Arc<dyn ErrorHook>>> = RefCell::new(None);\n}\n\n/// Resets the error hook to its previous state when dropped.\npub struct ResetErrorHookOnDrop(Option<Arc<dyn ErrorHook>>);\n\nimpl Drop for ResetErrorHookOnDrop {\n    fn drop(&mut self) {\n        ERROR_HOOK.with_borrow_mut(|this| *this = self.0.take())\n    }\n}\n\n/// Returns the current error hook.\npub fn get_error_hook() -> Option<Arc<dyn ErrorHook>> {\n    ERROR_HOOK.with_borrow(Clone::clone)\n}\n\n/// Sets the current thread-local error hook, which will be invoked when [`throw`] is called.\npub fn set_error_hook(hook: Arc<dyn ErrorHook>) -> ResetErrorHookOnDrop {\n    ResetErrorHookOnDrop(\n        ERROR_HOOK.with_borrow_mut(|this| Option::replace(this, hook)),\n    )\n}\n\n/// Invokes the error hook set by [`set_error_hook`] with the given error.\npub fn throw(error: impl Into<Error>) -> ErrorId {\n    ERROR_HOOK\n        .with_borrow(|hook| hook.as_ref().map(|hook| hook.throw(error.into())))\n        .unwrap_or_default()\n}\n\n/// Clears the given error from the current error hook.\npub fn clear(id: &ErrorId) {\n    ERROR_HOOK\n        .with_borrow(|hook| hook.as_ref().map(|hook| hook.clear(id)))\n        .unwrap_or_default()\n}\n\npin_project_lite::pin_project! {\n    /// A [`Future`] that reads the error hook that is set when it is created, and sets this as the\n    /// current error hook whenever it is polled.\n    pub struct ErrorHookFuture<Fut> {\n        hook: Option<Arc<dyn ErrorHook>>,\n        #[pin]\n        inner: Fut\n    }\n}\n\nimpl<Fut> ErrorHookFuture<Fut> {\n    /// Reads the current hook and wraps the given [`Future`], returning a new `Future` that will\n    /// set the error hook whenever it is polled.\n    pub fn new(inner: Fut) -> Self {\n        Self {\n            hook: ERROR_HOOK.with_borrow(Clone::clone),\n            inner,\n        }\n    }\n}\n\nimpl<Fut> Future for ErrorHookFuture<Fut>\nwhere\n    Fut: Future,\n{\n    type Output = Fut::Output;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let _hook = this\n            .hook\n            .as_ref()\n            .map(|hook| set_error_hook(Arc::clone(hook)));\n        this.inner.poll(cx)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::error::Error as StdError;\n\n    #[derive(Debug)]\n    struct MyError;\n\n    impl Display for MyError {\n        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n            write!(f, \"MyError\")\n        }\n    }\n\n    impl StdError for MyError {}\n\n    #[test]\n    fn test_from() {\n        let e = MyError;\n        let _le = Error::from(e);\n\n        let e = \"some error\".to_string();\n        let _le = Error::from(e);\n\n        let e = anyhow::anyhow!(\"anyhow error\");\n        let _le = Error::from(e);\n    }\n}\n"
  },
  {
    "path": "any_spawner/Cargo.toml",
    "content": "[package]\nname = \"any_spawner\"\nversion = \"0.3.0\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Spawn asynchronous tasks in an executor-independent way.\"\nedition.workspace = true\n\n[dependencies]\nasync-executor = { optional = true , workspace = true, default-features = true }\nfutures = { workspace = true, default-features = true }\nglib = { optional = true , workspace = true, default-features = true }\nthiserror = { workspace = true , default-features = true }\ntokio = { optional = true, default-features = false, features = [\n  \"rt\",\n] , workspace = true }\ntracing = { optional = true , workspace = true, default-features = true }\nwasm-bindgen-futures = { optional = true , workspace = true, default-features = true }\n\n[dev-dependencies]\nfutures-lite = { default-features = false , workspace = true }\ntokio = { default-features = false, features = [\n  \"rt\",\n  \"macros\",\n  \"time\",\n] , workspace = true }\nwasm-bindgen-test = { workspace = true, default-features = true }\nserial_test = { workspace = true, default-features = true }\n\n[features]\nasync-executor = [\"dep:async-executor\"]\ntracing = [\"dep:tracing\"]\ntokio = [\"dep:tokio\"]\nglib = [\"dep:glib\"]\nwasm-bindgen = [\"dep:wasm-bindgen-futures\"]\nfutures-executor = [\"futures/thread-pool\", \"futures/executor\"]\n\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\"]\nmax_combination_size = 2\n"
  },
  {
    "path": "any_spawner/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/wasm-test.toml\" },\n]\n"
  },
  {
    "path": "any_spawner/README.md",
    "content": "This crate makes it easier to write asynchronous code that is executor-agnostic, by providing a\nutility that can be used to spawn tasks in a variety of executors.\n\nIt only supports single executor per program, but that executor can be set at runtime, anywhere\nin your crate (or an application that depends on it).\n\nThis can be extended to support any executor or runtime that supports spawning [`Future`]s.\n\nThis is a least common denominator implementation in many ways. Limitations include:\n\n- setting an executor is a one-time, global action\n- no \"join handle\" or other result is returned from the spawn\n- the `Future` must output `()`\n\n```rust\nuse any_spawner::Executor;\n\nExecutor::init_futures_executor()\n    .expect(\"executor should only be initialized once\");\n\n// spawn a thread-safe Future\nExecutor::spawn(async { /* ... */ });\n\n// spawn a Future that is !Send\nExecutor::spawn_local(async { /* ... */ });\n```\n"
  },
  {
    "path": "any_spawner/src/lib.rs",
    "content": "//! This crate makes it easier to write asynchronous code that is executor-agnostic, by providing a\n//! utility that can be used to spawn tasks in a variety of executors.\n//!\n//! It only supports single executor per program, but that executor can be set at runtime, anywhere\n//! in your crate (or an application that depends on it).\n//!\n//! This can be extended to support any executor or runtime that supports spawning [`Future`]s.\n//!\n//! This is a least common denominator implementation in many ways. Limitations include:\n//! - setting an executor is a one-time, global action\n//! - no \"join handle\" or other result is returned from the spawn\n//! - the `Future` must output `()`\n//!\n//! ```no_run\n//! use any_spawner::Executor;\n//!\n//! // make sure an Executor has been initialized with one of the init_ functions\n//!\n//! // spawn a thread-safe Future\n//! Executor::spawn(async { /* ... */ });\n//!\n//! // spawn a Future that is !Send\n//! Executor::spawn_local(async { /* ... */ });\n//! ```\n\n#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\nuse std::{future::Future, pin::Pin, sync::OnceLock};\nuse thiserror::Error;\n\n/// A future that has been pinned.\npub type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;\n/// A future that has been pinned.\npub type PinnedLocalFuture<T> = Pin<Box<dyn Future<Output = T>>>;\n\n// Type alias for the spawn function pointer.\ntype SpawnFn = fn(PinnedFuture<()>);\n// Type alias for the spawn_local function pointer.\ntype SpawnLocalFn = fn(PinnedLocalFuture<()>);\n// Type alias for the poll_local function pointer.\ntype PollLocalFn = fn();\n\n/// Holds the function pointers for the current global executor.\n#[derive(Clone, Copy)]\nstruct ExecutorFns {\n    spawn: SpawnFn,\n    spawn_local: SpawnLocalFn,\n    poll_local: PollLocalFn,\n}\n\n// Use a single OnceLock to ensure atomic initialization of all functions.\nstatic EXECUTOR_FNS: OnceLock<ExecutorFns> = OnceLock::new();\n\n// No-op functions to use when an executor doesn't support a specific operation.\n#[cfg(any(feature = \"tokio\", feature = \"wasm-bindgen\", feature = \"glib\"))]\n#[cold]\n#[inline(never)]\nfn no_op_poll() {}\n\n#[cfg(all(not(feature = \"wasm-bindgen\"), not(debug_assertions)))]\n#[cold]\n#[inline(never)]\nfn no_op_spawn(_: PinnedFuture<()>) {\n    #[cfg(debug_assertions)]\n    eprintln!(\n        \"Warning: Executor::spawn called, but no global 'spawn' function is \\\n         configured (perhaps only spawn_local is supported, e.g., on wasm \\\n         without threading?).\"\n    );\n}\n\n// Wasm panics if you spawn without an executor\n#[cfg(feature = \"wasm-bindgen\")]\n#[cold]\n#[inline(never)]\nfn no_op_spawn(_: PinnedFuture<()>) {\n    panic!(\n        \"Executor::spawn called, but no global 'spawn' function is configured.\"\n    );\n}\n\n#[cfg(not(debug_assertions))]\n#[cold]\n#[inline(never)]\nfn no_op_spawn_local(_: PinnedLocalFuture<()>) {\n    panic!(\n        \"Executor::spawn_local called, but no global 'spawn_local' function \\\n         is configured.\"\n    );\n}\n\n/// Errors that can occur when using the executor.\n#[derive(Error, Debug)]\npub enum ExecutorError {\n    /// The executor has already been set.\n    #[error(\"Global executor has already been set.\")]\n    AlreadySet,\n}\n\n/// A global async executor that can spawn tasks.\npub struct Executor;\n\nimpl Executor {\n    /// Spawns a thread-safe [`Future`].\n    ///\n    /// Uses the globally configured executor.\n    /// Panics if no global executor has been initialized.\n    #[inline(always)]\n    #[track_caller]\n    pub fn spawn(fut: impl Future<Output = ()> + Send + 'static) {\n        let pinned_fut = Box::pin(fut);\n\n        if let Some(fns) = EXECUTOR_FNS.get() {\n            (fns.spawn)(pinned_fut)\n        } else {\n            // No global executor set.\n            handle_uninitialized_spawn(pinned_fut);\n        }\n    }\n\n    /// Spawns a [`Future`] that cannot be sent across threads.\n    ///\n    /// Uses the globally configured executor.\n    /// Panics if no global executor has been initialized.\n    #[inline(always)]\n    #[track_caller]\n    pub fn spawn_local(fut: impl Future<Output = ()> + 'static) {\n        let pinned_fut = Box::pin(fut);\n\n        if let Some(fns) = EXECUTOR_FNS.get() {\n            (fns.spawn_local)(pinned_fut)\n        } else {\n            // No global executor set.\n            handle_uninitialized_spawn_local(pinned_fut);\n        }\n    }\n\n    /// Waits until the next \"tick\" of the current async executor.\n    /// Respects the global executor.\n    #[inline(always)]\n    pub async fn tick() {\n        let (tx, rx) = futures::channel::oneshot::channel();\n        #[cfg(not(all(feature = \"wasm-bindgen\", target_family = \"wasm\")))]\n        Executor::spawn(async move {\n            _ = tx.send(());\n        });\n        #[cfg(all(feature = \"wasm-bindgen\", target_family = \"wasm\"))]\n        Executor::spawn_local(async move {\n            _ = tx.send(());\n        });\n\n        _ = rx.await;\n    }\n\n    /// Polls the global async executor.\n    ///\n    /// Uses the globally configured executor.\n    /// Does nothing if the global executor does not support polling.\n    #[inline(always)]\n    pub fn poll_local() {\n        if let Some(fns) = EXECUTOR_FNS.get() {\n            (fns.poll_local)()\n        }\n        // If not initialized or doesn't support polling, do nothing gracefully.\n    }\n}\n\nimpl Executor {\n    /// Globally sets the [`tokio`] runtime as the executor used to spawn tasks.\n    ///\n    /// Returns `Err(_)` if a global executor has already been set.\n    ///\n    /// Requires the `tokio` feature to be activated on this crate.\n    #[cfg(feature = \"tokio\")]\n    #[cfg_attr(docsrs, doc(cfg(feature = \"tokio\")))]\n    pub fn init_tokio() -> Result<(), ExecutorError> {\n        let executor_impl = ExecutorFns {\n            spawn: |fut| {\n                tokio::spawn(fut);\n            },\n            spawn_local: |fut| {\n                tokio::task::spawn_local(fut);\n            },\n            // Tokio doesn't have an explicit global poll function like LocalPool::run_until_stalled\n            poll_local: no_op_poll,\n        };\n        EXECUTOR_FNS\n            .set(executor_impl)\n            .map_err(|_| ExecutorError::AlreadySet)\n    }\n\n    /// Globally sets the [`wasm-bindgen-futures`] runtime as the executor used to spawn tasks.\n    ///\n    /// Returns `Err(_)` if a global executor has already been set.\n    ///\n    /// Requires the `wasm-bindgen` feature to be activated on this crate.\n    #[cfg(feature = \"wasm-bindgen\")]\n    #[cfg_attr(docsrs, doc(cfg(feature = \"wasm-bindgen\")))]\n    pub fn init_wasm_bindgen() -> Result<(), ExecutorError> {\n        let executor_impl = ExecutorFns {\n            // wasm-bindgen-futures only supports spawn_local\n            spawn: no_op_spawn,\n            spawn_local: |fut| {\n                wasm_bindgen_futures::spawn_local(fut);\n            },\n            poll_local: no_op_poll,\n        };\n        EXECUTOR_FNS\n            .set(executor_impl)\n            .map_err(|_| ExecutorError::AlreadySet)\n    }\n\n    /// Globally sets the [`glib`] runtime as the executor used to spawn tasks.\n    ///\n    /// Returns `Err(_)` if a global executor has already been set.\n    ///\n    /// Requires the `glib` feature to be activated on this crate.\n    #[cfg(feature = \"glib\")]\n    #[cfg_attr(docsrs, doc(cfg(feature = \"glib\")))]\n    pub fn init_glib() -> Result<(), ExecutorError> {\n        let executor_impl = ExecutorFns {\n            spawn: |fut| {\n                let main_context = glib::MainContext::default();\n                main_context.spawn(fut);\n            },\n            spawn_local: |fut| {\n                let main_context = glib::MainContext::default();\n                main_context.spawn_local(fut);\n            },\n            // Glib needs event loop integration, explicit polling isn't the standard model here.\n            poll_local: no_op_poll,\n        };\n        EXECUTOR_FNS\n            .set(executor_impl)\n            .map_err(|_| ExecutorError::AlreadySet)\n    }\n\n    /// Globally sets the [`futures`] executor as the executor used to spawn tasks,\n    /// lazily creating a thread pool to spawn tasks into.\n    ///\n    /// Returns `Err(_)` if a global executor has already been set.\n    ///\n    /// Requires the `futures-executor` feature to be activated on this crate.\n    #[cfg(feature = \"futures-executor\")]\n    #[cfg_attr(docsrs, doc(cfg(feature = \"futures-executor\")))]\n    pub fn init_futures_executor() -> Result<(), ExecutorError> {\n        use futures::{\n            executor::{LocalPool, LocalSpawner, ThreadPool},\n            task::{LocalSpawnExt, SpawnExt},\n        };\n        use std::cell::RefCell;\n\n        // Keep the lazy-init ThreadPool and thread-local LocalPool for spawn_local impl\n        static THREAD_POOL: OnceLock<ThreadPool> = OnceLock::new();\n        thread_local! {\n            static LOCAL_POOL: RefCell<LocalPool> = RefCell::new(LocalPool::new());\n            // SPAWNER is derived from LOCAL_POOL, keep it for efficiency inside the closure\n            static SPAWNER: LocalSpawner = LOCAL_POOL.with(|pool| pool.borrow().spawner());\n        }\n\n        fn get_thread_pool() -> &'static ThreadPool {\n            THREAD_POOL.get_or_init(|| {\n                ThreadPool::new()\n                    .expect(\"could not create futures executor ThreadPool\")\n            })\n        }\n\n        let executor_impl = ExecutorFns {\n            spawn: |fut| {\n                get_thread_pool()\n                    .spawn(fut)\n                    .expect(\"failed to spawn future on ThreadPool\");\n            },\n            spawn_local: |fut| {\n                // Use the thread_local SPAWNER derived from LOCAL_POOL\n                SPAWNER.with(|spawner| {\n                    spawner\n                        .spawn_local(fut)\n                        .expect(\"failed to spawn local future\");\n                });\n            },\n            poll_local: || {\n                // Use the thread_local LOCAL_POOL\n                LOCAL_POOL.with(|pool| {\n                    // Use try_borrow_mut to prevent panic during re-entrant calls\n                    if let Ok(mut pool) = pool.try_borrow_mut() {\n                        pool.run_until_stalled();\n                    }\n                    // If already borrowed, we're likely in a nested poll, so do nothing.\n                });\n            },\n        };\n\n        EXECUTOR_FNS\n            .set(executor_impl)\n            .map_err(|_| ExecutorError::AlreadySet)\n    }\n\n    /// Globally sets the [`async_executor`] executor as the executor used to spawn tasks,\n    /// lazily creating a thread pool to spawn tasks into.\n    ///\n    /// Returns `Err(_)` if a global executor has already been set.\n    ///\n    /// Requires the `async-executor` feature to be activated on this crate.\n    #[cfg(feature = \"async-executor\")]\n    #[cfg_attr(docsrs, doc(cfg(feature = \"async-executor\")))]\n    pub fn init_async_executor() -> Result<(), ExecutorError> {\n        use async_executor::{Executor as AsyncExecutor, LocalExecutor};\n\n        // Keep the lazy-init global Executor and thread-local LocalExecutor for spawn_local impl\n        static ASYNC_EXECUTOR: OnceLock<AsyncExecutor<'static>> =\n            OnceLock::new();\n        thread_local! {\n            static LOCAL_EXECUTOR_POOL: LocalExecutor<'static> = const { LocalExecutor::new() };\n        }\n\n        fn get_async_executor() -> &'static AsyncExecutor<'static> {\n            ASYNC_EXECUTOR.get_or_init(AsyncExecutor::new)\n        }\n\n        let executor_impl = ExecutorFns {\n            spawn: |fut| {\n                get_async_executor().spawn(fut).detach();\n            },\n            spawn_local: |fut| {\n                LOCAL_EXECUTOR_POOL.with(|pool| pool.spawn(fut).detach());\n            },\n            poll_local: || {\n                LOCAL_EXECUTOR_POOL.with(|pool| {\n                    // try_tick polls the local executor without blocking\n                    // This prevents issues if called recursively or from within a task.\n                    pool.try_tick();\n                });\n            },\n        };\n        EXECUTOR_FNS\n            .set(executor_impl)\n            .map_err(|_| ExecutorError::AlreadySet)\n    }\n\n    /// Globally sets a custom executor as the executor used to spawn tasks.\n    ///\n    /// Requires the custom executor to be `Send + Sync` as it will be stored statically.\n    ///\n    /// Returns `Err(_)` if a global executor has already been set.\n    pub fn init_custom_executor(\n        custom_executor: impl CustomExecutor + Send + Sync + 'static,\n    ) -> Result<(), ExecutorError> {\n        // Store the custom executor instance itself to call its methods.\n        // Use Box for dynamic dispatch.\n        static CUSTOM_EXECUTOR_INSTANCE: OnceLock<\n            Box<dyn CustomExecutor + Send + Sync>,\n        > = OnceLock::new();\n\n        CUSTOM_EXECUTOR_INSTANCE\n            .set(Box::new(custom_executor))\n            .map_err(|_| ExecutorError::AlreadySet)?;\n\n        // Now set the ExecutorFns using the stored instance\n        let executor_impl = ExecutorFns {\n            spawn: |fut| {\n                // Unwrap is safe because we just set it successfully or returned Err.\n                CUSTOM_EXECUTOR_INSTANCE.get().unwrap().spawn(fut);\n            },\n            spawn_local: |fut| {\n                CUSTOM_EXECUTOR_INSTANCE.get().unwrap().spawn_local(fut);\n            },\n            poll_local: || {\n                CUSTOM_EXECUTOR_INSTANCE.get().unwrap().poll_local();\n            },\n        };\n\n        EXECUTOR_FNS\n            .set(executor_impl)\n            .map_err(|_| ExecutorError::AlreadySet)\n        // If setting EXECUTOR_FNS fails (extremely unlikely race if called *concurrently*\n        // with another init_* after CUSTOM_EXECUTOR_INSTANCE was set), we technically\n        // leave CUSTOM_EXECUTOR_INSTANCE set but EXECUTOR_FNS not. This is an edge case,\n        // but the primary race condition is solved.\n    }\n\n    /// Sets a custom executor *for the current thread only*.\n    ///\n    /// This overrides the global executor for calls to `spawn`, `spawn_local`, and `poll_local`\n    /// made *from the current thread*. It does not affect other threads or the global state.\n    ///\n    /// The provided `custom_executor` must implement [`CustomExecutor`] and `'static`, but does\n    /// **not** need to be `Send` or `Sync`.\n    ///\n    /// Returns `Err(ExecutorError::AlreadySet)` if a *local* executor has already been set\n    /// *for this thread*.\n    pub fn init_local_custom_executor(\n        custom_executor: impl CustomExecutor + 'static,\n    ) -> Result<(), ExecutorError> {\n        // Store the custom executor instance itself to call its methods.\n        // Use Box for dynamic dispatch.\n        thread_local! {\n            static CUSTOM_EXECUTOR_INSTANCE: OnceLock<\n                Box<dyn CustomExecutor>,\n            > = OnceLock::new();\n        };\n\n        CUSTOM_EXECUTOR_INSTANCE.with(|this| {\n            this.set(Box::new(custom_executor))\n                .map_err(|_| ExecutorError::AlreadySet)\n        })?;\n\n        // Now set the ExecutorFns using the stored instance\n        let executor_impl = ExecutorFns {\n            spawn: |fut| {\n                // Unwrap is safe because we just set it successfully or returned Err.\n                CUSTOM_EXECUTOR_INSTANCE\n                    .with(|this| this.get().unwrap().spawn(fut));\n            },\n            spawn_local: |fut| {\n                CUSTOM_EXECUTOR_INSTANCE\n                    .with(|this| this.get().unwrap().spawn_local(fut));\n            },\n            poll_local: || {\n                CUSTOM_EXECUTOR_INSTANCE\n                    .with(|this| this.get().unwrap().poll_local());\n            },\n        };\n\n        EXECUTOR_FNS\n            .set(executor_impl)\n            .map_err(|_| ExecutorError::AlreadySet)\n    }\n}\n\n/// A trait for custom executors.\n/// Custom executors can be used to integrate with any executor that supports spawning futures.\n///\n/// If used with `init_custom_executor`, the implementation must be `Send + Sync + 'static`.\n///\n/// All methods can be called recursively. Implementors should be mindful of potential\n/// deadlocks or excessive resource consumption if recursive calls are not handled carefully\n/// (e.g., using `try_borrow_mut` or non-blocking polls within implementations).\npub trait CustomExecutor {\n    /// Spawns a future, usually on a thread pool.\n    fn spawn(&self, fut: PinnedFuture<()>);\n    /// Spawns a local future. May require calling `poll_local` to make progress.\n    fn spawn_local(&self, fut: PinnedLocalFuture<()>);\n    /// Polls the executor, if it supports polling. Implementations should ideally be\n    /// non-blocking or use mechanisms like `try_tick` or `try_borrow_mut` to handle\n    /// re-entrant calls safely.\n    fn poll_local(&self);\n}\n\n// Ensure CustomExecutor is object-safe\n#[allow(dead_code)]\nfn test_object_safety(_: Box<dyn CustomExecutor + Send + Sync>) {} // Added Send + Sync constraint here for global usage\n\n/// Handles the case where `Executor::spawn` is called without an initialized executor.\n#[cold] // Less likely path\n#[inline(never)]\n#[track_caller]\nfn handle_uninitialized_spawn(_fut: PinnedFuture<()>) {\n    let caller = std::panic::Location::caller();\n    #[cfg(all(debug_assertions, feature = \"tracing\"))]\n    {\n        tracing::error!(\n            target: \"any_spawner\",\n            spawn_caller=%caller,\n            \"Executor::spawn called before a global executor was initialized. Task dropped.\"\n        );\n        // Drop the future implicitly after logging\n        drop(_fut);\n    }\n    #[cfg(all(debug_assertions, not(feature = \"tracing\")))]\n    {\n        panic!(\n            \"At {caller}, tried to spawn a Future with Executor::spawn() \\\n             before a global executor was initialized.\"\n        );\n    }\n    // In release builds (without tracing), call the specific no-op function.\n    #[cfg(not(debug_assertions))]\n    {\n        no_op_spawn(_fut);\n    }\n}\n\n/// Handles the case where `Executor::spawn_local` is called without an initialized executor.\n#[cold] // Less likely path\n#[inline(never)]\n#[track_caller]\nfn handle_uninitialized_spawn_local(_fut: PinnedLocalFuture<()>) {\n    let caller = std::panic::Location::caller();\n    #[cfg(all(debug_assertions, feature = \"tracing\"))]\n    {\n        tracing::error!(\n            target: \"any_spawner\",\n            spawn_caller=%caller,\n            \"Executor::spawn_local called before a global executor was initialized. \\\n            Task likely dropped or panicked.\"\n        );\n        // Fall through to panic or no-op depending on build/target\n    }\n    #[cfg(all(debug_assertions, not(feature = \"tracing\")))]\n    {\n        panic!(\n            \"At {caller}, tried to spawn a Future with \\\n             Executor::spawn_local() before a global executor was initialized.\"\n        );\n    }\n    // In release builds (without tracing), call the specific no-op function (which usually panics).\n    #[cfg(not(debug_assertions))]\n    {\n        no_op_spawn_local(_fut);\n    }\n}\n"
  },
  {
    "path": "any_spawner/tests/already_set_error.rs",
    "content": "use any_spawner::{Executor, ExecutorError};\n\n#[test]\nfn test_already_set_error() {\n    struct SimpleExecutor;\n\n    impl any_spawner::CustomExecutor for SimpleExecutor {\n        fn spawn(&self, _fut: any_spawner::PinnedFuture<()>) {}\n        fn spawn_local(&self, _fut: any_spawner::PinnedLocalFuture<()>) {}\n        fn poll_local(&self) {}\n    }\n\n    // First initialization should succeed\n    Executor::init_custom_executor(SimpleExecutor)\n        .expect(\"First initialization failed\");\n\n    // Second initialization should fail with AlreadySet error\n    let result = Executor::init_custom_executor(SimpleExecutor);\n    assert!(matches!(result, Err(ExecutorError::AlreadySet)));\n\n    // First local initialization should fail\n    let result = Executor::init_local_custom_executor(SimpleExecutor);\n    assert!(matches!(result, Err(ExecutorError::AlreadySet)));\n}\n"
  },
  {
    "path": "any_spawner/tests/async_executor.rs",
    "content": "#![cfg(feature = \"async-executor\")]\n\nuse std::{\n    future::Future,\n    pin::Pin,\n    sync::{Arc, Mutex},\n};\n\n// A simple async executor for testing\nstruct TestExecutor {\n    tasks: Mutex<Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>>,\n}\n\nimpl TestExecutor {\n    fn new() -> Self {\n        TestExecutor {\n            tasks: Mutex::new(Vec::new()),\n        }\n    }\n\n    fn spawn<F>(&self, future: F)\n    where\n        F: Future<Output = ()> + Send + 'static,\n    {\n        self.tasks.lock().unwrap().push(Box::pin(future));\n    }\n\n    fn run_all(&self) {\n        // Take all tasks out to process them\n        let tasks = self.tasks.lock().unwrap().drain(..).collect::<Vec<_>>();\n\n        // Use a basic future executor to run each task to completion\n        for mut task in tasks {\n            // Use futures-lite's block_on to complete the future\n            futures::executor::block_on(async {\n                unsafe {\n                    let task_mut = Pin::new_unchecked(&mut task);\n                    let _ = std::future::Future::poll(\n                        task_mut,\n                        &mut std::task::Context::from_waker(\n                            futures::task::noop_waker_ref(),\n                        ),\n                    );\n                }\n            });\n        }\n    }\n}\n\n#[test]\nfn test_async_executor() {\n    let executor = Arc::new(TestExecutor::new());\n    let executor_clone = executor.clone();\n\n    // Create a spawner function that will use our test executor\n    let spawner = move |future| {\n        executor_clone.spawn(future);\n    };\n\n    // Prepare test data\n    let counter = Arc::new(Mutex::new(0));\n    let counter_clone = counter.clone();\n\n    // Use the spawner to spawn a task\n    spawner(async move {\n        *counter_clone.lock().unwrap() += 1;\n    });\n\n    // Run all tasks\n    executor.run_all();\n\n    // Check if the task completed correctly\n    assert_eq!(*counter.lock().unwrap(), 1);\n}\n"
  },
  {
    "path": "any_spawner/tests/custom_executor.rs",
    "content": "use any_spawner::Executor;\nuse std::sync::{\n    atomic::{AtomicBool, Ordering},\n    Arc,\n};\n\n#[test]\nfn test_custom_executor() {\n    // Define a simple custom executor\n    struct TestExecutor {\n        spawn_called: Arc<AtomicBool>,\n        spawn_local_called: Arc<AtomicBool>,\n        poll_local_called: Arc<AtomicBool>,\n    }\n\n    impl any_spawner::CustomExecutor for TestExecutor {\n        fn spawn(&self, fut: any_spawner::PinnedFuture<()>) {\n            self.spawn_called.store(true, Ordering::SeqCst);\n            // Execute the future immediately (this works for simple test futures)\n            futures::executor::block_on(fut);\n        }\n\n        fn spawn_local(&self, fut: any_spawner::PinnedLocalFuture<()>) {\n            self.spawn_local_called.store(true, Ordering::SeqCst);\n            // Execute the future immediately\n            futures::executor::block_on(fut);\n        }\n\n        fn poll_local(&self) {\n            self.poll_local_called.store(true, Ordering::SeqCst);\n        }\n    }\n\n    let spawn_called = Arc::new(AtomicBool::new(false));\n    let spawn_local_called = Arc::new(AtomicBool::new(false));\n    let poll_local_called = Arc::new(AtomicBool::new(false));\n\n    let executor = TestExecutor {\n        spawn_called: spawn_called.clone(),\n        spawn_local_called: spawn_local_called.clone(),\n        poll_local_called: poll_local_called.clone(),\n    };\n\n    // Initialize with our custom executor\n    Executor::init_custom_executor(executor)\n        .expect(\"Failed to initialize custom executor\");\n\n    // Test spawn\n    Executor::spawn(async {\n        // Simple task\n    });\n    assert!(spawn_called.load(Ordering::SeqCst));\n\n    // Test spawn_local\n    Executor::spawn_local(async {\n        // Simple local task\n    });\n    assert!(spawn_local_called.load(Ordering::SeqCst));\n\n    // Test poll_local\n    Executor::poll_local();\n    assert!(poll_local_called.load(Ordering::SeqCst));\n}\n"
  },
  {
    "path": "any_spawner/tests/custom_runtime.rs",
    "content": "#![cfg(feature = \"futures-executor\")]\n\nuse any_spawner::{CustomExecutor, Executor, PinnedFuture, PinnedLocalFuture};\n\n#[test]\nfn can_create_custom_executor() {\n    use futures::{\n        executor::{LocalPool, LocalSpawner},\n        task::LocalSpawnExt,\n    };\n    use std::{\n        cell::RefCell,\n        sync::{\n            atomic::{AtomicUsize, Ordering},\n            Arc,\n        },\n    };\n\n    thread_local! {\n        static LOCAL_POOL: RefCell<LocalPool> = RefCell::new(LocalPool::new());\n        static SPAWNER: LocalSpawner = LOCAL_POOL.with(|pool| pool.borrow().spawner());\n    }\n\n    struct CustomFutureExecutor;\n    impl CustomExecutor for CustomFutureExecutor {\n        fn spawn(&self, _fut: PinnedFuture<()>) {\n            panic!(\"not supported in this test\");\n        }\n\n        fn spawn_local(&self, fut: PinnedLocalFuture<()>) {\n            SPAWNER.with(|spawner| {\n                spawner.spawn_local(fut).expect(\"failed to spawn future\");\n            });\n        }\n\n        fn poll_local(&self) {\n            LOCAL_POOL.with(|pool| {\n                if let Ok(mut pool) = pool.try_borrow_mut() {\n                    pool.run_until_stalled();\n                }\n                // If we couldn't borrow_mut, we're in a nested call to poll, so we don't need to do anything.\n            });\n        }\n    }\n\n    Executor::init_custom_executor(CustomFutureExecutor)\n        .expect(\"couldn't set executor\");\n\n    let counter = Arc::new(AtomicUsize::new(0));\n    let counter_clone = Arc::clone(&counter);\n    Executor::spawn_local(async move {\n        counter_clone.store(1, Ordering::Release);\n    });\n    Executor::poll_local();\n    assert_eq!(counter.load(Ordering::Acquire), 1);\n}\n"
  },
  {
    "path": "any_spawner/tests/executor_tick.rs",
    "content": "#![cfg(feature = \"tokio\")]\n\nuse any_spawner::Executor;\nuse std::{\n    sync::{Arc, Mutex},\n    time::Duration,\n};\n\n#[tokio::test]\nasync fn test_executor_tick() {\n    // Initialize the tokio executor\n    Executor::init_tokio().expect(\"Failed to initialize tokio executor\");\n\n    let value = Arc::new(Mutex::new(false));\n    let value_clone = value.clone();\n\n    // Spawn a task that sets the value after a tick\n    Executor::spawn(async move {\n        Executor::tick().await;\n        *value_clone.lock().unwrap() = true;\n    });\n\n    // Allow some time for the task to complete\n    tokio::time::sleep(Duration::from_millis(50)).await;\n\n    // Check that the value was set\n    assert!(*value.lock().unwrap());\n}\n"
  },
  {
    "path": "any_spawner/tests/futures_executor.rs",
    "content": "#![cfg(feature = \"futures-executor\")]\n\nuse any_spawner::Executor;\nuse futures::channel::oneshot;\nuse std::{\n    sync::{Arc, Mutex},\n    time::Duration,\n};\n\n#[test]\nfn test_futures_executor() {\n    // Initialize the futures executor\n    Executor::init_futures_executor()\n        .expect(\"Failed to initialize futures executor\");\n\n    let (tx, rx) = oneshot::channel();\n    let result = Arc::new(Mutex::new(None));\n    let result_clone = result.clone();\n\n    // Spawn a task\n    Executor::spawn(async move {\n        tx.send(84).expect(\"Failed to send value\");\n    });\n\n    // Spawn a task that waits for the result\n    Executor::spawn(async move {\n        match rx.await {\n            Ok(val) => *result_clone.lock().unwrap() = Some(val),\n            Err(_) => panic!(\"Failed to receive value\"),\n        }\n    });\n\n    // Poll a few times to ensure the task completes\n    for _ in 0..10 {\n        Executor::poll_local();\n        std::thread::sleep(Duration::from_millis(10));\n\n        if result.lock().unwrap().is_some() {\n            break;\n        }\n    }\n\n    assert_eq!(*result.lock().unwrap(), Some(84));\n}\n"
  },
  {
    "path": "any_spawner/tests/futures_runtime.rs",
    "content": "#![cfg(feature = \"futures-executor\")]\n\nuse any_spawner::Executor;\n// All tests in this file use the same executor.\n\n#[test]\nfn can_spawn_local_future() {\n    use std::rc::Rc;\n\n    let _ = Executor::init_futures_executor();\n    let rc = Rc::new(());\n    Executor::spawn_local(async {\n        _ = rc;\n    });\n    Executor::spawn(async {});\n}\n\n#[test]\nfn can_make_local_progress() {\n    use std::sync::{\n        atomic::{AtomicUsize, Ordering},\n        Arc,\n    };\n    let _ = Executor::init_futures_executor();\n    let counter = Arc::new(AtomicUsize::new(0));\n    Executor::spawn_local({\n        let counter = Arc::clone(&counter);\n        async move {\n            assert_eq!(counter.fetch_add(1, Ordering::AcqRel), 0);\n            Executor::spawn_local(async {\n                // Should not crash\n            });\n        }\n    });\n    Executor::poll_local();\n    assert_eq!(counter.load(Ordering::Acquire), 1);\n}\n"
  },
  {
    "path": "any_spawner/tests/glib.rs",
    "content": "#![cfg(feature = \"glib\")]\n\nuse any_spawner::Executor;\nuse glib::{MainContext, MainLoop};\nuse serial_test::serial;\nuse std::{\n    cell::Cell,\n    future::Future,\n    rc::Rc,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc, Mutex,\n    },\n    time::Duration,\n};\n\n// Helper to run a future to completion on a dedicated glib MainContext.\n// Returns true if the future completed within the timeout, false otherwise.\nfn run_on_glib_context<F>(fut: F)\nwhere\n    F: Future<Output = ()> + Send + 'static,\n{\n    let _ = Executor::init_glib();\n\n    let context = MainContext::default();\n    let main_loop = MainLoop::new(Some(&context), false);\n    let main_loop_clone = main_loop.clone();\n\n    Executor::spawn(async move {\n        fut.await;\n        main_loop_clone.quit();\n    });\n\n    main_loop.run();\n}\n\n// Helper to run a local (!Send) future on the glib context.\nfn run_local_on_glib_context<F>(fut: F)\nwhere\n    F: Future<Output = ()> + 'static,\n{\n    let _ = Executor::init_glib();\n\n    let context = MainContext::default();\n    let main_loop = MainLoop::new(Some(&context), false);\n    let main_loop_clone = main_loop.clone();\n\n    Executor::spawn_local(async move {\n        fut.await;\n        main_loop_clone.quit();\n    });\n\n    main_loop.run();\n}\n\n// This test must run after a test that successfully initializes glib,\n// or within its own process.\n#[test]\n#[serial]\nfn test_glib_spawn() {\n    let success_flag = Arc::new(AtomicBool::new(false));\n    let flag_clone = success_flag.clone();\n\n    run_on_glib_context(async move {\n        // Simulate async work\n        futures_lite::future::yield_now().await;\n        flag_clone.store(true, Ordering::SeqCst);\n\n        // We need to give the spawned task time to run.\n        // The run_on_glib_context handles the main loop.\n        // We just need to ensure spawn happened correctly.\n        // Let's wait a tiny bit within the driving future to ensure spawn gets processed.\n        glib::timeout_future(Duration::from_millis(10)).await;\n    });\n\n    assert!(\n        success_flag.load(Ordering::SeqCst),\n        \"Spawned future did not complete successfully\"\n    );\n}\n\n// Similar conditions as test_glib_spawn regarding initialization state.\n#[test]\n#[serial]\nfn test_glib_spawn_local() {\n    let success_flag = Rc::new(Cell::new(false));\n    let flag_clone = success_flag.clone();\n\n    run_local_on_glib_context(async move {\n        // Use Rc to make the future !Send\n        let non_send_data = Rc::new(Cell::new(10));\n\n        let data = non_send_data.get();\n        assert_eq!(data, 10, \"Rc data should be accessible\");\n        non_send_data.set(20); // Modify non-Send data\n\n        // Simulate async work\n        futures_lite::future::yield_now().await;\n\n        assert_eq!(\n            non_send_data.get(),\n            20,\n            \"Rc data should persist modification\"\n        );\n        flag_clone.set(true);\n\n        // Wait a tiny bit\n        glib::timeout_future(Duration::from_millis(10)).await;\n    });\n\n    assert!(\n        success_flag.get(),\n        \"Spawned local future did not complete successfully\"\n    );\n}\n\n// Test Executor::tick with glib backend\n#[test]\n#[serial]\nfn test_glib_tick() {\n    run_on_glib_context(async {\n        let value = Arc::new(Mutex::new(false));\n        let value_clone = value.clone();\n\n        // Spawn a task that sets the value after a tick\n        Executor::spawn(async move {\n            Executor::tick().await;\n            *value_clone.lock().unwrap() = true;\n        });\n\n        // Allow some time for the task to complete\n        glib::timeout_future(Duration::from_millis(10)).await;\n\n        // Check that the value was set\n        assert!(*value.lock().unwrap());\n    });\n}\n\n// Test Executor::poll_local with glib backend (should be a no-op)\n#[test]\n#[serial]\nfn test_glib_poll_local_is_no_op() {\n    // Ensure glib executor is initialized\n    let _ = Executor::init_glib();\n    // poll_local for glib is configured as a no-op\n    // Calling it should not panic or cause issues.\n    Executor::poll_local();\n    Executor::poll_local();\n\n    println!(\"Executor::poll_local called successfully (expected no-op).\");\n}\n"
  },
  {
    "path": "any_spawner/tests/local_custom_executor.rs",
    "content": "use any_spawner::Executor;\nuse std::sync::{\n    atomic::{AtomicBool, Ordering},\n    Arc,\n};\n\n#[test]\nfn test_local_custom_executor() {\n    // Define a thread-local custom executor\n    struct LocalTestExecutor {\n        spawn_called: Arc<AtomicBool>,\n        spawn_local_called: Arc<AtomicBool>,\n    }\n\n    impl any_spawner::CustomExecutor for LocalTestExecutor {\n        fn spawn(&self, fut: any_spawner::PinnedFuture<()>) {\n            self.spawn_called.store(true, Ordering::SeqCst);\n            futures::executor::block_on(fut);\n        }\n\n        fn spawn_local(&self, fut: any_spawner::PinnedLocalFuture<()>) {\n            self.spawn_local_called.store(true, Ordering::SeqCst);\n            futures::executor::block_on(fut);\n        }\n\n        fn poll_local(&self) {\n            // No-op for this test\n        }\n    }\n\n    let local_spawn_called = Arc::new(AtomicBool::new(false));\n    let local_spawn_local_called = Arc::new(AtomicBool::new(false));\n\n    let local_executor = LocalTestExecutor {\n        spawn_called: local_spawn_called.clone(),\n        spawn_local_called: local_spawn_local_called.clone(),\n    };\n\n    // Initialize a thread-local executor\n    Executor::init_local_custom_executor(local_executor)\n        .expect(\"Failed to initialize local custom executor\");\n\n    // Test spawn - should use the thread-local executor\n    Executor::spawn(async {\n        // Simple task\n    });\n    assert!(local_spawn_called.load(Ordering::SeqCst));\n\n    // Test spawn_local - should use the thread-local executor\n    Executor::spawn_local(async {\n        // Simple local task\n    });\n    assert!(local_spawn_local_called.load(Ordering::SeqCst));\n}\n"
  },
  {
    "path": "any_spawner/tests/multiple_tasks.rs",
    "content": "#![cfg(feature = \"tokio\")]\n\nuse any_spawner::Executor;\nuse futures::channel::oneshot;\nuse std::sync::{Arc, Mutex};\n\n#[tokio::test]\nasync fn test_multiple_tasks() {\n    Executor::init_tokio().expect(\"Failed to initialize tokio executor\");\n\n    let counter = Arc::new(Mutex::new(0));\n    let tasks = 10;\n    let mut handles = Vec::new();\n\n    // Spawn multiple tasks that increment the counter\n    for _ in 0..tasks {\n        let counter_clone = counter.clone();\n        let (tx, rx) = oneshot::channel();\n\n        Executor::spawn(async move {\n            *counter_clone.lock().unwrap() += 1;\n            tx.send(()).expect(\"Failed to send completion signal\");\n        });\n\n        handles.push(rx);\n    }\n\n    // Wait for all tasks to complete\n    for handle in handles {\n        handle.await.expect(\"Task failed\");\n    }\n\n    // Verify that all tasks incremented the counter\n    assert_eq!(*counter.lock().unwrap(), tasks);\n}\n"
  },
  {
    "path": "any_spawner/tests/tokio_executor.rs",
    "content": "#![cfg(feature = \"tokio\")]\n\nuse any_spawner::Executor;\nuse futures::channel::oneshot;\n\n#[tokio::test]\nasync fn test_tokio_executor() {\n    // Initialize the tokio executor\n    Executor::init_tokio().expect(\"Failed to initialize tokio executor\");\n\n    let (tx, rx) = oneshot::channel();\n\n    // Spawn a task that sends a value\n    Executor::spawn(async move {\n        tx.send(42).expect(\"Failed to send value\");\n    });\n\n    // Wait for the spawned task to complete\n    assert_eq!(rx.await.unwrap(), 42);\n}\n"
  },
  {
    "path": "any_spawner/tests/wasm_bindgen_tests.rs",
    "content": "#![cfg(all(feature = \"wasm-bindgen\", target_family = \"wasm\"))]\n\nuse any_spawner::Executor;\nuse futures::channel::oneshot;\nuse std::sync::{\n    atomic::{AtomicBool, Ordering},\n    Arc,\n};\nuse wasm_bindgen_test::*;\n\nwasm_bindgen_test_configure!(run_in_browser);\n\n#[wasm_bindgen_test]\nasync fn test_wasm_bindgen_spawn_local() {\n    // Initialize the wasm-bindgen executor\n    let _ = Executor::init_wasm_bindgen();\n\n    // Create a channel to verify the task completes\n    let (tx, rx) = oneshot::channel();\n\n    // Spawn a local task (wasm doesn't support sending futures between threads)\n    Executor::spawn_local(async move {\n        // Simulate some async work\n        Executor::tick().await;\n        tx.send(42).expect(\"Failed to send result\");\n    });\n\n    // Wait for the task to complete\n    let result = rx.await.expect(\"Failed to receive result\");\n    assert_eq!(result, 42);\n}\n\n#[wasm_bindgen_test]\nasync fn test_wasm_bindgen_tick() {\n    // Initialize the wasm-bindgen executor if not already initialized\n    let _ = Executor::init_wasm_bindgen();\n\n    let flag = Arc::new(AtomicBool::new(false));\n    let flag_clone = flag.clone();\n\n    // Spawn a task that will set the flag\n    Executor::spawn_local(async move {\n        flag_clone.store(true, Ordering::SeqCst);\n    });\n\n    // Wait for a tick, which should allow the spawned task to run\n    Executor::tick().await;\n\n    // Verify the flag was set\n    assert!(flag.load(Ordering::SeqCst));\n}\n\n#[wasm_bindgen_test]\nasync fn test_multiple_wasm_bindgen_tasks() {\n    // Initialize once for all tests\n    let _ = Executor::init_wasm_bindgen();\n\n    // Create channels for multiple tasks\n    let (tx1, rx1) = oneshot::channel();\n    let (tx2, rx2) = oneshot::channel();\n\n    // Spawn multiple tasks\n    Executor::spawn_local(async move {\n        tx1.send(\"task1\").expect(\"Failed to send from task1\");\n    });\n\n    Executor::spawn_local(async move {\n        tx2.send(\"task2\").expect(\"Failed to send from task2\");\n    });\n\n    // Wait for both tasks to complete\n    let (result1, result2) = futures::join!(rx1, rx2);\n\n    assert_eq!(result1.unwrap(), \"task1\");\n    assert_eq!(result2.unwrap(), \"task2\");\n}\n\n// This test verifies that spawn (not local) fails on wasm as expected\n#[wasm_bindgen_test]\n#[should_panic]\nfn test_wasm_bindgen_spawn_errors() {\n    let _ = Executor::init_wasm_bindgen();\n\n    // Using should_panic to test that Executor::spawn panics in wasm\n    Executor::spawn(async {\n        // This should panic since wasm-bindgen doesn't support Send futures\n    });\n}\n"
  },
  {
    "path": "benchmarks/Cargo.toml",
    "content": "[package]\nname = \"benchmarks\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nl0410 = { package = \"leptos\", version = \"0.4.10\", features = [\n  \"nightly\",\n  \"ssr\",\n] }\nleptos = { path = \"../leptos\", features = [\"ssr\", \"nightly\"] }\nleptos_reactive = { path = \"../leptos_reactive\", features = [\"ssr\", \"nightly\"] }\ntachydom = { git = \"https://github.com/gbj/tachys\", features = [\n  \"nightly\",\n  \"leptos\",\n] }\ntachy_maccy = { git = \"https://github.com/gbj/tachys\", features = [\"nightly\"] }\nsycamore = { version = \"0.8.0\", features = [\"ssr\"] }\nyew = { version = \"0.20.0\", features = [\"ssr\"] }\ntokio-test = \"0.4.0\"\nminiserde = \"0.1.0\"\ngloo = \"0.8.0\"\nuuid = { version = \"1.0\", features = [\"serde\", \"v4\", \"wasm-bindgen\"] }\nwasm-bindgen = \"0.2.100\"\nlazy_static = \"1.0\"\nlog = \"0.4.0\"\nstrum = \"0.24.0\"\nstrum_macros = \"0.24.0\"\nserde = { version = \"1.0\", features = [\"derive\", \"rc\"] }\nserde_json = \"1.0\"\ntera = \"1.0\"\n\n[dependencies.web-sys]\nversion = \"0.3.0\"\nfeatures = [\"Window\", \"Document\", \"HtmlElement\", \"HtmlInputElement\"]\n"
  },
  {
    "path": "benchmarks/src/lib.rs",
    "content": "#![feature(test)]\n\nextern crate test;\n\nmod reactive;\nmod ssr;\nmod todomvc;\n"
  },
  {
    "path": "benchmarks/src/reactive.rs",
    "content": "use std::{cell::Cell, rc::Rc};\nuse test::Bencher;\n\n#[bench]\nfn leptos_deep_creation(b: &mut Bencher) {\n    use leptos::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        let signal = create_rw_signal(0);\n        let mut memos = Vec::<Memo<usize>>::new();\n        for _ in 0..1000usize {\n            let prev = memos.last().copied();\n            if let Some(prev) = prev {\n                memos.push(create_memo(move |_| prev.get() + 1));\n            } else {\n                memos.push(create_memo(move |_| signal.get() + 1));\n            }\n        }\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn leptos_deep_update(b: &mut Bencher) {\n    use leptos::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        let signal = create_rw_signal(0);\n        let mut memos = Vec::<Memo<usize>>::new();\n        for _ in 0..1000usize {\n            if let Some(prev) = memos.last().copied() {\n                memos.push(create_memo(move |_| prev.get() + 1));\n            } else {\n                memos.push(create_memo(move |_| signal.get() + 1));\n            }\n        }\n        signal.set(1);\n        assert_eq!(memos[999].get(), 1001);\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn leptos_narrowing_down(b: &mut Bencher) {\n    use leptos::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        let sigs = (0..1000).map(|n| create_signal(n)).collect::<Vec<_>>();\n        let reads = sigs.iter().map(|(r, _)| *r).collect::<Vec<_>>();\n        let writes = sigs.iter().map(|(_, w)| *w).collect::<Vec<_>>();\n        let memo =\n            create_memo(move |_| reads.iter().map(|r| r.get()).sum::<i32>());\n        assert_eq!(memo(), 499500);\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn leptos_fanning_out(b: &mut Bencher) {\n    use leptos::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        let sig = create_rw_signal(0);\n        let memos = (0..1000)\n            .map(|_| create_memo(move |_| sig.get()))\n            .collect::<Vec<_>>();\n        assert_eq!(memos.iter().map(|m| m.get()).sum::<i32>(), 0);\n        sig.set(1);\n        assert_eq!(memos.iter().map(|m| m.get()).sum::<i32>(), 1000);\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn leptos_narrowing_update(b: &mut Bencher) {\n    use leptos::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        let acc = Rc::new(Cell::new(0));\n        let sigs = (0..1000).map(|n| create_signal(n)).collect::<Vec<_>>();\n        let reads = sigs.iter().map(|(r, _)| *r).collect::<Vec<_>>();\n        let writes = sigs.iter().map(|(_, w)| *w).collect::<Vec<_>>();\n        let memo =\n            create_memo(move |_| reads.iter().map(|r| r.get()).sum::<i32>());\n        assert_eq!(memo(), 499500);\n        create_isomorphic_effect({\n            let acc = Rc::clone(&acc);\n            move |_| {\n                acc.set(memo());\n            }\n        });\n        assert_eq!(acc.get(), 499500);\n\n        writes[1].update(|n| *n += 1);\n        writes[10].update(|n| *n += 1);\n        writes[100].update(|n| *n += 1);\n\n        assert_eq!(acc.get(), 499503);\n        assert_eq!(memo(), 499503);\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn l0410_deep_creation(b: &mut Bencher) {\n    use l0410::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        create_scope(runtime, |cx| {\n            let signal = create_rw_signal(cx, 0);\n            let mut memos = Vec::<Memo<usize>>::new();\n            for _ in 0..1000usize {\n                if let Some(prev) = memos.last().copied() {\n                    memos.push(create_memo(cx, move |_| prev.get() + 1));\n                } else {\n                    memos.push(create_memo(cx, move |_| signal.get() + 1));\n                }\n            }\n        })\n        .dispose()\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn l0410_deep_update(b: &mut Bencher) {\n    use l0410::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        create_scope(runtime, |cx| {\n            let signal = create_rw_signal(cx, 0);\n            let mut memos = Vec::<Memo<usize>>::new();\n            for _ in 0..1000usize {\n                if let Some(prev) = memos.last().copied() {\n                    memos.push(create_memo(cx, move |_| prev.get() + 1));\n                } else {\n                    memos.push(create_memo(cx, move |_| signal.get() + 1));\n                }\n            }\n            signal.set(1);\n            assert_eq!(memos[999].get(), 1001);\n        })\n        .dispose()\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn l0410_narrowing_down(b: &mut Bencher) {\n    use l0410::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        create_scope(runtime, |cx| {\n            let acc = Rc::new(Cell::new(0));\n            let sigs =\n                (0..1000).map(|n| create_signal(cx, n)).collect::<Vec<_>>();\n            let reads = sigs.iter().map(|(r, _)| *r).collect::<Vec<_>>();\n            let writes = sigs.iter().map(|(_, w)| *w).collect::<Vec<_>>();\n            let memo = create_memo(cx, move |_| {\n                reads.iter().map(|r| r.get()).sum::<i32>()\n            });\n            assert_eq!(memo(), 499500);\n        })\n        .dispose()\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn l0410_fanning_out(b: &mut Bencher) {\n    use l0410::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        create_scope(runtime, |cx| {\n            let sig = create_rw_signal(cx, 0);\n            let memos = (0..1000)\n                .map(|_| create_memo(cx, move |_| sig.get()))\n                .collect::<Vec<_>>();\n            assert_eq!(memos.iter().map(|m| m.get()).sum::<i32>(), 0);\n            sig.set(1);\n            assert_eq!(memos.iter().map(|m| m.get()).sum::<i32>(), 1000);\n        })\n        .dispose()\n    });\n\n    runtime.dispose();\n}\n#[bench]\nfn l0410_narrowing_update(b: &mut Bencher) {\n    use l0410::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        create_scope(runtime, |cx| {\n            let acc = Rc::new(Cell::new(0));\n            let sigs =\n                (0..1000).map(|n| create_signal(cx, n)).collect::<Vec<_>>();\n            let reads = sigs.iter().map(|(r, _)| *r).collect::<Vec<_>>();\n            let writes = sigs.iter().map(|(_, w)| *w).collect::<Vec<_>>();\n            let memo = create_memo(cx, move |_| {\n                reads.iter().map(|r| r.get()).sum::<i32>()\n            });\n            assert_eq!(memo.get(), 499500);\n            create_isomorphic_effect(cx, {\n                let acc = Rc::clone(&acc);\n                move |_| {\n                    acc.set(memo.get());\n                }\n            });\n            assert_eq!(acc.get(), 499500);\n\n            writes[1].update(|n| *n += 1);\n            writes[10].update(|n| *n += 1);\n            writes[100].update(|n| *n += 1);\n\n            assert_eq!(acc.get(), 499503);\n            assert_eq!(memo.get(), 499503);\n        })\n        .dispose()\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn l0410_scope_creation_and_disposal(b: &mut Bencher) {\n    use l0410::*;\n    let runtime = create_runtime();\n\n    b.iter(|| {\n        let acc = Rc::new(Cell::new(0));\n        let disposers = (0..1000)\n            .map(|_| {\n                create_scope(runtime, {\n                    let acc = Rc::clone(&acc);\n                    move |cx| {\n                        let (r, w) = create_signal(cx, 0);\n                        create_isomorphic_effect(cx, {\n                            move |_| {\n                                acc.set(r.get());\n                            }\n                        });\n                        w.update(|n| *n += 1);\n                    }\n                })\n            })\n            .collect::<Vec<_>>();\n        for disposer in disposers {\n            disposer.dispose();\n        }\n    });\n\n    runtime.dispose();\n}\n\n#[bench]\nfn sycamore_narrowing_down(b: &mut Bencher) {\n    use sycamore::reactive::{\n        create_effect, create_memo, create_scope, create_signal,\n    };\n\n    b.iter(|| {\n        let d = create_scope(|cx| {\n            let acc = Rc::new(Cell::new(0));\n            let sigs = Rc::new(\n                (0..1000).map(|n| create_signal(cx, n)).collect::<Vec<_>>(),\n            );\n            let memo = create_memo(cx, {\n                let sigs = Rc::clone(&sigs);\n                move || sigs.iter().map(|r| *r.get()).sum::<i32>()\n            });\n            assert_eq!(*memo.get(), 499500);\n        });\n        unsafe { d.dispose() };\n    });\n}\n\n#[bench]\nfn sycamore_fanning_out(b: &mut Bencher) {\n    use sycamore::reactive::{\n        create_effect, create_memo, create_scope, create_signal,\n    };\n\n    b.iter(|| {\n        let d = create_scope(|cx| {\n            let sig = create_signal(cx, 0);\n            let memos = (0..1000)\n                .map(|_| create_memo(cx, move || sig.get()))\n                .collect::<Vec<_>>();\n            assert_eq!(memos.iter().map(|m| *(*m.get())).sum::<i32>(), 0);\n            sig.set(1);\n            assert_eq!(memos.iter().map(|m| *(*m.get())).sum::<i32>(), 1000);\n        });\n        unsafe { d.dispose() };\n    });\n}\n\n#[bench]\nfn sycamore_deep_creation(b: &mut Bencher) {\n    use sycamore::reactive::*;\n\n    b.iter(|| {\n        let d = create_scope(|cx| {\n            let signal = create_signal(cx, 0);\n            let mut memos = Vec::<&ReadSignal<usize>>::new();\n            for _ in 0..1000usize {\n                if let Some(prev) = memos.last().copied() {\n                    memos.push(create_memo(cx, move || *prev.get() + 1));\n                } else {\n                    memos.push(create_memo(cx, move || *signal.get() + 1));\n                }\n            }\n        });\n        unsafe { d.dispose() };\n    });\n}\n\n#[bench]\nfn sycamore_deep_update(b: &mut Bencher) {\n    use sycamore::reactive::*;\n\n    b.iter(|| {\n        let d = create_scope(|cx| {\n            let signal = create_signal(cx, 0);\n            let mut memos = Vec::<&ReadSignal<usize>>::new();\n            for _ in 0..1000usize {\n                if let Some(prev) = memos.last().copied() {\n                    memos.push(create_memo(cx, move || *prev.get() + 1));\n                } else {\n                    memos.push(create_memo(cx, move || *signal.get() + 1));\n                }\n            }\n            signal.set(1);\n            assert_eq!(*memos[999].get(), 1001);\n        });\n        unsafe { d.dispose() };\n    });\n}\n#[bench]\nfn sycamore_narrowing_update(b: &mut Bencher) {\n    use sycamore::reactive::{\n        create_effect, create_memo, create_scope, create_signal,\n    };\n\n    b.iter(|| {\n        let d = create_scope(|cx| {\n            let acc = Rc::new(Cell::new(0));\n            let sigs = Rc::new(\n                (0..1000).map(|n| create_signal(cx, n)).collect::<Vec<_>>(),\n            );\n            let memo = create_memo(cx, {\n                let sigs = Rc::clone(&sigs);\n                move || sigs.iter().map(|r| *r.get()).sum::<i32>()\n            });\n            assert_eq!(*memo.get(), 499500);\n            create_effect(cx, {\n                let acc = Rc::clone(&acc);\n                move || {\n                    acc.set(*memo.get());\n                }\n            });\n            assert_eq!(acc.get(), 499500);\n\n            sigs[1].set(*sigs[1].get() + 1);\n            sigs[10].set(*sigs[10].get() + 1);\n            sigs[100].set(*sigs[100].get() + 1);\n\n            assert_eq!(acc.get(), 499503);\n            assert_eq!(*memo.get(), 499503);\n        });\n        unsafe { d.dispose() };\n    });\n}\n\n#[bench]\nfn sycamore_scope_creation_and_disposal(b: &mut Bencher) {\n    use sycamore::reactive::{create_effect, create_scope, create_signal};\n\n    b.iter(|| {\n        let acc = Rc::new(Cell::new(0));\n        let disposers = (0..1000)\n            .map(|_| {\n                create_scope({\n                    let acc = Rc::clone(&acc);\n                    move |cx| {\n                        let s = create_signal(cx, 0);\n                        create_effect(cx, {\n                            move || {\n                                acc.set(*s.get());\n                            }\n                        });\n                        s.set(*s.get() + 1);\n                    }\n                })\n            })\n            .collect::<Vec<_>>();\n        for disposer in disposers {\n            unsafe {\n                disposer.dispose();\n            }\n        }\n    });\n}\n"
  },
  {
    "path": "benchmarks/src/ssr.rs",
    "content": "use test::Bencher;\n\n#[bench]\nfn leptos_ssr_bench(b: &mut Bencher) {\n\tuse leptos::*;\n\tlet r = create_runtime();\n    b.iter(|| {\n\t\t\tleptos::leptos_dom::HydrationCtx::reset_id();\n\t\t\t#[component]\n\t\t\tfn Counter(initial: i32) -> impl IntoView {\n\t\t\t\tlet (value, set_value) = create_signal(initial);\n\t\t\t\tview! {\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<button on:click=move |_| set_value.update(|value| *value -= 1)>\"-1\"</button>\n\t\t\t\t\t\t<span>\"Value: \" {move || value().to_string()} \"!\"</span>\n\t\t\t\t\t\t<button on:click=move |_| set_value.update(|value| *value += 1)>\"+1\"</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet rendered = view! {\n\t\t\t\t<main>\n\t\t\t\t\t<h1>\"Welcome to our benchmark page.\"</h1>\n\t\t\t\t\t<p>\"Here's some introductory text.\"</p>\n\t\t\t\t\t<Counter initial=1/>\n\t\t\t\t\t<Counter initial=2/>\n\t\t\t\t\t<Counter initial=3/>\n\t\t\t\t</main>\n\t\t\t}.into_view().render_to_string();\n\n\t\t\tassert_eq!(\n\t\t\t\trendered,\n\"<main data-hk=\\\"0-0-0-1\\\"><h1 data-hk=\\\"0-0-0-2\\\">Welcome to our benchmark page.</h1><p data-hk=\\\"0-0-0-3\\\">Here&#x27;s some introductory text.</p><div data-hk=\\\"0-0-0-5\\\"><button data-hk=\\\"0-0-0-6\\\">-1</button><span data-hk=\\\"0-0-0-7\\\">Value: <!>1<!--hk=0-0-0-8-->!</span><button data-hk=\\\"0-0-0-9\\\">+1</button></div><!--hk=0-0-0-4--><div data-hk=\\\"0-0-0-11\\\"><button data-hk=\\\"0-0-0-12\\\">-1</button><span data-hk=\\\"0-0-0-13\\\">Value: <!>2<!--hk=0-0-0-14-->!</span><button data-hk=\\\"0-0-0-15\\\">+1</button></div><!--hk=0-0-0-10--><div data-hk=\\\"0-0-0-17\\\"><button data-hk=\\\"0-0-0-18\\\">-1</button><span data-hk=\\\"0-0-0-19\\\">Value: <!>3<!--hk=0-0-0-20-->!</span><button data-hk=\\\"0-0-0-21\\\">+1</button></div><!--hk=0-0-0-16--></main>\"\t\t\t);\n\t});\n\tr.dispose();\n}\n\n#[bench]\nfn tachys_ssr_bench(b: &mut Bencher) {\n\tuse leptos::{create_runtime, create_signal, SignalGet, SignalUpdate};\n\tuse tachy_maccy::view;\n\tuse tachydom::view::{Render, RenderHtml};\n\tuse tachydom::html::element::ElementChild;\n\tuse tachydom::html::attribute::global::ClassAttribute;\n\tuse tachydom::html::attribute::global::GlobalAttributes;\n\tuse tachydom::html::attribute::global::OnAttribute;\n\tuse tachydom::renderer::dom::Dom;\n\tlet rt = create_runtime();\n    b.iter(|| {\n\t\tfn counter(initial: i32) -> impl Render<Dom> + RenderHtml<Dom> {\n\t\t\tlet (value, set_value) = create_signal(initial);\n\t\t\tview! {\n\t\t\t\t<div>\n\t\t\t\t\t<button on:click=move |_| set_value.update(|value| *value -= 1)>\"-1\"</button>\n\t\t\t\t\t<span>\"Value: \" {move || value().to_string()} \"!\"</span>\n\t\t\t\t\t<button on:click=move |_| set_value.update(|value| *value += 1)>\"+1\"</button>\n\t\t\t\t</div>\n\t\t\t}\n\t\t}\n\n\t\tlet rendered = view! {\n\t\t\t<main>\n\t\t\t\t<h1>\"Welcome to our benchmark page.\"</h1>\n\t\t\t\t<p>\"Here's some introductory text.\"</p>\n\t\t\t\t{counter(1)}\n\t\t\t\t{counter(2)}\n\t\t\t\t{counter(3)}\n\t\t\t</main>\n\t\t}.to_html();\n\t\tassert_eq!(\n\t\t\trendered,\n\t\t\t\"<main><h1>Welcome to our benchmark page.</h1><p>Here's some introductory text.</p><div><button>-1</button><span>Value: <!>1<!>!</span><button>+1</button></div><div><button>-1</button><span>Value: <!>2<!>!</span><button>+1</button></div><div><button>-1</button><span>Value: <!>3<!>!</span><button>+1</button></div></main>\"\n\t\t);\n\t});\n\trt.dispose();\n}\n\n#[bench]\nfn tera_ssr_bench(b: &mut Bencher) {\n    use serde::{Deserialize, Serialize};\n    use tera::*;\n\n    static TEMPLATE: &str = r#\"<main>\n\t<h1>Welcome to our benchmark page.</h1>\n\t<p>Here's some introductory text.</p>\n\t{% for counter in counters %}\n\t<div>\n\t\t<button>-1</button>\n\t\t<span>Value: {{ counter.value }}!</span>\n\t\t<button>+1</button>\n\t</div>\n\t{% endfor %}\n\t</main>\"#;\n\n\n    static  LazyCell<TERA>: Tera = LazyLock::new(|| {\n        let mut tera = Tera::default();\n        tera.add_raw_templates(vec![(\"template.html\", TEMPLATE)]).unwrap();\n        tera\n    });\n\n\n    #[derive(Serialize, Deserialize)]\n    struct Counter {\n        value: i32,\n    }\n\n    b.iter(|| {\n        let mut ctx = Context::new();\n        ctx.insert(\n            \"counters\",\n            &vec![\n                Counter { value: 0 },\n                Counter { value: 1 },\n                Counter { value: 2 },\n            ],\n        );\n\n        let _ = TERA.render(\"template.html\", &ctx).unwrap();\n    });\n}\n\n#[bench]\nfn sycamore_ssr_bench(b: &mut Bencher) {\n    use sycamore::prelude::*;\n    use sycamore::*;\n\n    b.iter(|| {\n\t\t_ = create_scope(|cx| {\n\t\t\t#[derive(Prop)]\n\t\t\tstruct CounterProps {\n\t\t\t\tinitial: i32\n\t\t\t}\n\n\n\t\t\t#[component]\n\t\t\tfn Counter<G: Html>(cx: Scope, props: CounterProps) -> View<G> {\n\t\t\t\tlet value = create_signal(cx, props.initial);\n\t\t\t\tview! {\n\t\t\t\t\tcx,\n\t\t\t\t\tdiv {\n\t\t\t\t\t\tbutton(on:click=|_| value.set(*value.get() - 1)) {\n\t\t\t\t\t\t\t\"-1\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\tspan {\n\t\t\t\t\t\t\t\"Value: \"\n\t\t\t\t\t\t\t(value.get().to_string())\n\t\t\t\t\t\t\t\"!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbutton(on:click=|_| value.set(*value.get() + 1)) {\n\t\t\t\t\t\t\t\"+1\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet rendered = render_to_string(|cx| view! {\n\t\t\t\tcx,\n\t\t\t\tmain {\n\t\t\t\t\th1 {\n\t\t\t\t\t\t\"Welcome to our benchmark page.\"\n\t\t\t\t\t}\n\t\t\t\t\tp {\n\t\t\t\t\t\t\"Here's some introductory text.\"\n\t\t\t\t\t}\n\t\t\t\t\tCounter(initial = 1)\n\t\t\t\t\tCounter(initial = 2)\n\t\t\t\t\tCounter(initial = 3)\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tassert_eq!(\n\t\t\t\trendered,\n\t\t\t\t\"<main data-hk=\\\"0.0\\\"><h1 data-hk=\\\"0.1\\\">Welcome to our benchmark page.</h1><p data-hk=\\\"0.2\\\">Here's some introductory text.</p><!--#--><div data-hk=\\\"1.0\\\"><button data-hk=\\\"1.1\\\">-1</button><span data-hk=\\\"1.2\\\">Value: <!--#-->1<!--/-->!</span><button data-hk=\\\"1.3\\\">+1</button></div><!--/--><!----><!--#--><div data-hk=\\\"2.0\\\"><button data-hk=\\\"2.1\\\">-1</button><span data-hk=\\\"2.2\\\">Value: <!--#-->2<!--/-->!</span><button data-hk=\\\"2.3\\\">+1</button></div><!--/--><!----><!--#--><div data-hk=\\\"3.0\\\"><button data-hk=\\\"3.1\\\">-1</button><span data-hk=\\\"3.2\\\">Value: <!--#-->3<!--/-->!</span><button data-hk=\\\"3.3\\\">+1</button></div><!--/--></main>\"\n\t\t\t);\n\t\t});\n\t});\n}\n\n#[bench]\nfn yew_ssr_bench(b: &mut Bencher) {\n    use yew::prelude::*;\n    use yew::ServerRenderer;\n\n    b.iter(|| {\n\t\t#[derive(Properties, PartialEq, Eq, Debug)]\n\t\tstruct CounterProps {\n\t\t\tinitial: i32\n\t\t}\n\n\t\t#[function_component(Counter)]\n\t\tfn counter(props: &CounterProps) -> Html {\n\t\t\tlet state = use_state(|| props.initial);\n\n\t\t\tlet incr_counter = {\n\t\t\t\tlet state = state.clone();\n\t\t\t\tCallback::from(move |_| state.set(&*state + 1))\n\t\t\t};\n\n\t\t\tlet decr_counter = {\n\t\t\t\tlet state = state.clone();\n\t\t\t\tCallback::from(move |_| state.set(&*state - 1))\n\t\t\t};\n\n\t\t\thtml! {\n\t\t\t\t<div>\n\t\t\t\t\t<h1>{\"Welcome to our benchmark page.\"}</h1>\n\t\t\t\t\t<p>{\"Here's some introductory text.\"}</p>\n\t\t\t\t\t<button onclick={decr_counter}> {\"-1\"} </button>\n\t\t\t\t\t<p> {\"Value: \"} {*state} {\"!\"} </p>\n\t\t\t\t\t<button onclick={incr_counter}> {\"+1\"} </button>\n\t\t\t\t</div>\n\t\t\t}\n\t\t}\n\n\t\t#[function_component]\n\t\tfn App() -> Html {\n\t\t\thtml! {\n\t\t\t\t<main>\n\t\t\t\t\t<Counter initial=1/>\n\t\t\t\t\t<Counter initial=2/>\n\t\t\t\t\t<Counter initial=3/>\n\t\t\t\t</main>\n\t\t\t}\n\t\t}\n\n\t\ttokio_test::block_on(async {\n\t\t\tlet renderer = ServerRenderer::<App>::new();\n\t\t\tlet rendered = renderer.render().await;\n\t\t\tassert_eq!(\n\t\t\t\trendered,\n\t\t\t\t\"<!--<[]>--><main><!--<[]>--><div><h1>Welcome to our benchmark page.</h1><p>Here's some introductory text.</p><button>-1</button><p>Value: 1!</p><button>+1</button></div><!--</[]>--><!--<[]>--><div><h1>Welcome to our benchmark page.</h1><p>Here's some introductory text.</p><button>-1</button><p>Value: 2!</p><button>+1</button></div><!--</[]>--><!--<[]>--><div><h1>Welcome to our benchmark page.</h1><p>Here's some introductory text.</p><button>-1</button><p>Value: 3!</p><button>+1</button></div><!--</[]>--></main><!--</[]>-->\"\n\t\t\t);\n\t\t});\n\t});\n}\n"
  },
  {
    "path": "benchmarks/src/todomvc/leptos.rs",
    "content": "pub use leptos::*;\nuse miniserde::*;\nuse wasm_bindgen::JsCast;\nuse web_sys::HtmlInputElement;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Todos(pub Vec<Todo>);\n\nconst STORAGE_KEY: &str = \"todos-leptos\";\n\nimpl Todos {\n    pub fn new() -> Self {\n        Self(vec![])\n    }\n\n    pub fn new_with_1000() -> Self {\n        let todos = (0..1000)\n            .map(|id| Todo::new(id, format!(\"Todo #{id}\")))\n            .collect();\n        Self(todos)\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    pub fn add(&mut self, todo: Todo) {\n        self.0.push(todo);\n    }\n\n    pub fn remove(&mut self, id: usize) {\n        self.0.retain(|todo| todo.id != id);\n    }\n\n    pub fn remaining(&self) -> usize {\n        self.0.iter().filter(|todo| !(todo.completed)()).count()\n    }\n\n    pub fn completed(&self) -> usize {\n        self.0.iter().filter(|todo| (todo.completed)()).count()\n    }\n\n    pub fn toggle_all(&self) {\n        // if all are complete, mark them all active instead\n        if self.remaining() == 0 {\n            for todo in &self.0 {\n                if todo.completed.get() {\n                    (todo.set_completed)(false);\n                }\n            }\n        }\n        // otherwise, mark them all complete\n        else {\n            for todo in &self.0 {\n                (todo.set_completed)(true);\n            }\n        }\n    }\n\n    fn clear_completed(&mut self) {\n        self.0.retain(|todo| !todo.completed.get());\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct Todo {\n    pub id: usize,\n    pub title: ReadSignal<String>,\n    pub set_title: WriteSignal<String>,\n    pub completed: ReadSignal<bool>,\n    pub set_completed: WriteSignal<bool>,\n}\n\nimpl Todo {\n    pub fn new(id: usize, title: String) -> Self {\n        Self::new_with_completed(id, title, false)\n    }\n\n    pub fn new_with_completed(\n        id: usize,\n        title: String,\n        completed: bool,\n    ) -> Self {\n        let (title, set_title) = create_signal(title);\n        let (completed, set_completed) = create_signal(completed);\n        Self {\n            id,\n            title,\n            set_title,\n            completed,\n            set_completed,\n        }\n    }\n\n    pub fn toggle(&self) {\n        self.set_completed\n            .update(|completed| *completed = !*completed);\n    }\n}\n\nconst ESCAPE_KEY: u32 = 27;\nconst ENTER_KEY: u32 = 13;\n\n#[component]\npub fn TodoMVC(todos: Todos) -> impl IntoView {\n    let mut next_id = todos\n        .0\n        .iter()\n        .map(|todo| todo.id)\n        .max()\n        .map(|last| last + 1)\n        .unwrap_or(0);\n\n    let (todos, set_todos) = create_signal(todos);\n    provide_context(set_todos);\n\n    let (mode, set_mode) = create_signal(Mode::All);\n\n    let add_todo = move |ev: web_sys::KeyboardEvent| {\n        let target = event_target::<HtmlInputElement>(&ev);\n        ev.stop_propagation();\n        let key_code = ev.unchecked_ref::<web_sys::KeyboardEvent>().key_code();\n        if key_code == ENTER_KEY {\n            let title = event_target_value(&ev);\n            let title = title.trim();\n            if !title.is_empty() {\n                let new = Todo::new(next_id, title.to_string());\n                set_todos.update(|t| t.add(new));\n                next_id += 1;\n                target.set_value(\"\");\n            }\n        }\n    };\n\n    let filtered_todos = create_memo::<Vec<Todo>>(move |_| {\n        todos.with(|todos| match mode.get() {\n            Mode::All => todos.0.to_vec(),\n            Mode::Active => todos\n                .0\n                .iter()\n                .filter(|todo| !todo.completed.get())\n                .cloned()\n                .collect(),\n            Mode::Completed => todos\n                .0\n                .iter()\n                .filter(|todo| todo.completed.get())\n                .cloned()\n                .collect(),\n        })\n    });\n\n    // effect to serialize to JSON\n    // this does reactive reads, so it will automatically serialize on any relevant change\n    create_effect(move |_| {\n        if let Ok(Some(storage)) = window().local_storage() {\n            let objs = todos\n                .get()\n                .0\n                .iter()\n                .map(TodoSerialized::from)\n                .collect::<Vec<_>>();\n            let json = json::to_string(&objs);\n            if storage.set_item(STORAGE_KEY, &json).is_err() {\n                log::error!(\"error while trying to set item in localStorage\");\n            }\n        }\n    });\n\n    view! { \n        <main>\n            <section class=\"todoapp\">\n                <header class=\"header\">\n                    <h1>\"todos\"</h1>\n                    <input\n                        class=\"new-todo\"\n                        placeholder=\"What needs to be done?\"\n                        autofocus=\"\"\n                        on:keydown=add_todo\n                    />\n                </header>\n                <section class=\"main\" class:hidden=move || todos.with(|t| t.is_empty())>\n                    <input\n                        id=\"toggle-all\"\n                        class=\"toggle-all\"\n                        type=\"checkbox\"\n                        prop:checked=move || todos.with(|t| t.remaining() > 0)\n                        on:input=move |_| set_todos.update(|t| t.toggle_all())\n                    />\n                    <label for=\"toggle-all\">\"Mark all as complete\"</label>\n                    <ul class=\"todo-list\">\n                        <For\n                            each=filtered_todos\n                            key=|todo| todo.id\n                            children=move |todo: Todo| {\n                                view! { <Todo todo=todo.clone()/> }\n                            }\n                        />\n                    </ul>\n                </section>\n                <footer class=\"footer\" class:hidden=move || todos.with(|t| t.is_empty())>\n                    <span class=\"todo-count\">\n                        <strong>{move || todos.with(|t| t.remaining().to_string())}</strong>\n                        {move || if todos.with(|t| t.remaining()) == 1 { \" item\" } else { \" items\" }}\n                        \" left\"\n                    </span>\n                    <ul class=\"filters\">\n                        <li>\n                            <a\n                                href=\"#/\"\n                                class=\"selected\"\n                                class:selected=move || mode() == Mode::All\n                            >\n                                \"All\"\n                            </a>\n                        </li>\n                        <li>\n                            <a href=\"#/active\" class:selected=move || mode() == Mode::Active>\n                                \"Active\"\n                            </a>\n                        </li>\n                        <li>\n                            <a href=\"#/completed\" class:selected=move || mode() == Mode::Completed>\n                                \"Completed\"\n                            </a>\n                        </li>\n                    </ul>\n                    <button\n                        class=\"clear-completed hidden\"\n                        class:hidden=move || todos.with(|t| t.completed() == 0)\n                        on:click=move |_| set_todos.update(|t| t.clear_completed())\n                    >\n                        \"Clear completed\"\n                    </button>\n                </footer>\n            </section>\n            <footer class=\"info\">\n                <p>\"Double-click to edit a todo\"</p>\n                <p>\"Created by \" <a href=\"http://todomvc.com\">\"Greg Johnston\"</a></p>\n                <p>\"Part of \" <a href=\"http://todomvc.com\">\"TodoMVC\"</a></p>\n            </footer>\n        </main>\n    }.into_view()\n}\n\n#[component]\npub fn Todo(todo: Todo) -> impl IntoView {\n    let (editing, set_editing) = create_signal(false);\n    let set_todos = use_context::<WriteSignal<Todos>>().unwrap();\n    //let input = NodeRef::new();\n\n    let save = move |value: &str| {\n        let value = value.trim();\n        if value.is_empty() {\n            set_todos.update(|t| t.remove(todo.id));\n        } else {\n            (todo.set_title)(value.to_string());\n        }\n        set_editing(false);\n    };\n\n    view! { \n        <li class=\"todo\" class:editing=editing class:completed=move || (todo.completed)()>\n            <div class=\"view\">\n                <input class=\"toggle\" type=\"checkbox\" prop:checked=move || (todo.completed)()/>\n                <label on:dblclick=move |_| set_editing(true)>{move || todo.title.get()}</label>\n                <button\n                    class=\"destroy\"\n                    on:click=move |_| set_todos.update(|t| t.remove(todo.id))\n                ></button>\n            </div>\n            {move || {\n                editing()\n                    .then(|| {\n                        view! { \n                            <input\n                                class=\"edit\"\n                                class:hidden=move || !(editing)()\n                                prop:value=move || todo.title.get()\n                                on:focusout=move |ev| save(&event_target_value(&ev))\n                                on:keyup=move |ev| {\n                                    let key_code = ev.unchecked_ref::<web_sys::KeyboardEvent>().key_code();\n                                    if key_code == ENTER_KEY {\n                                        save(&event_target_value(&ev));\n                                    } else if key_code == ESCAPE_KEY {\n                                        set_editing(false);\n                                    }\n                                }\n                            />\n                        }\n                    })\n            }}\n        </li>\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Mode {\n    Active,\n    Completed,\n    All,\n}\n\nimpl Default for Mode {\n    fn default() -> Self {\n        Mode::All\n    }\n}\n\npub fn route(hash: &str) -> Mode {\n    match hash {\n        \"/active\" => Mode::Active,\n        \"/completed\" => Mode::Completed,\n        _ => Mode::All,\n    }\n}\n\n#[derive(Serialize, Deserialize)]\npub struct TodoSerialized {\n    pub id: usize,\n    pub title: String,\n    pub completed: bool,\n}\n\nimpl TodoSerialized {\n    pub fn into_todo(self, ) -> Todo {\n        Todo::new_with_completed(self.id, self.title, self.completed)\n    }\n}\n\nimpl From<&Todo> for TodoSerialized {\n    fn from(todo: &Todo) -> Self {\n        Self {\n            id: todo.id,\n            title: todo.title.get(),\n            completed: (todo.completed)(),\n        }\n    }\n}\n"
  },
  {
    "path": "benchmarks/src/todomvc/mod.rs",
    "content": "use test::Bencher;\n\nmod leptos;\nmod sycamore;\nmod tachys;\nmod tera;\nmod yew;\n\n#[bench]\nfn leptos_todomvc_ssr(b: &mut Bencher) {\n    use ::leptos::*;\n    let runtime = create_runtime();\n    b.iter(|| {\n        use crate::todomvc::leptos::*;\n\n        let html = ::leptos::ssr::render_to_string(|| {\n            view! { <TodoMVC todos=Todos::new()/> }\n        });\n        assert!(html.len() > 1);\n    });\n    runtime.dispose();\n}\n\n#[bench]\nfn tachys_todomvc_ssr(b: &mut Bencher) {\n    use ::leptos::*;\n    let runtime = create_runtime();\n    b.iter(|| {\n        use crate::todomvc::tachys::*;\n        use tachydom::view::{Render, RenderHtml};\n\n        let rendered = TodoMVC(Todos::new()).to_html();\n        assert_eq!(\n            rendered,\n\"<main><section class=\\\"todoapp\\\"><header class=\\\"header\\\"><h1>todos</h1><input placeholder=\\\"What needs to be done?\\\" autofocus class=\\\"new-todo\\\"></header><section class=\\\"main hidden\\\"><input id=\\\"toggle-all\\\" type=\\\"checkbox\\\" class=\\\"toggle-all\\\"><label for=\\\"toggle-all\\\">Mark all as complete</label><ul class=\\\"todo-list\\\"></ul></section><footer class=\\\"footer hidden\\\"><span class=\\\"todo-count\\\"><strong>0</strong><!> items<!> left</span><ul class=\\\"filters\\\"><li><a href=\\\"#/\\\" class=\\\"selected selected\\\">All</a></li><li><a href=\\\"#/active\\\" class=\\\"\\\">Active</a></li><li><a href=\\\"#/completed\\\" class=\\\"\\\">Completed</a></li></ul><button class=\\\"clear-completed hidden hidden\\\">Clear completed</button></footer></section><footer class=\\\"info\\\"><p>Double-click to edit a todo</p><p>Created by <a href=\\\"http://todomvc.com\\\">Greg Johnston</a></p><p>Part of <a href=\\\"http://todomvc.com\\\">TodoMVC</a></p></footer></main>\"        );\n    });\n    runtime.dispose();\n}\n\n#[bench]\nfn sycamore_todomvc_ssr(b: &mut Bencher) {\n    use self::sycamore::*;\n    use ::sycamore::{prelude::*, *};\n\n    b.iter(|| {\n        _ = create_scope(|cx| {\n            let rendered = render_to_string(|cx| {\n                view! {\n                    cx,\n                    App()\n                }\n            });\n\n            assert!(rendered.len() > 1);\n        });\n    });\n}\n\n#[bench]\nfn yew_todomvc_ssr(b: &mut Bencher) {\n    use self::yew::*;\n    use ::yew::{prelude::*, ServerRenderer};\n\n    b.iter(|| {\n        tokio_test::block_on(async {\n            let renderer = ServerRenderer::<App>::new();\n            let rendered = renderer.render().await;\n            assert!(rendered.len() > 1);\n        });\n    });\n}\n\n#[bench]\nfn leptos_todomvc_ssr_with_1000(b: &mut Bencher) {\n    b.iter(|| {\n        use self::leptos::*;\n        use ::leptos::*;\n\n        let html = ::leptos::ssr::render_to_string(|| {\n            view! {\n                <TodoMVC todos=Todos::new_with_1000()/>\n            }\n        });\n        assert!(html.len() > 1);\n    });\n}\n\n#[bench]\nfn tachys_todomvc_ssr_with_1000(b: &mut Bencher) {\n    use ::leptos::*;\n    let runtime = create_runtime();\n    b.iter(|| {\n        use crate::todomvc::tachys::*;\n        use tachydom::view::{Render, RenderHtml};\n\n        let rendered = TodoMVC(Todos::new_with_1000()).to_html();\n        assert!(rendered.len() > 20_000)\n    });\n    runtime.dispose();\n}\n\n#[bench]\nfn sycamore_todomvc_ssr_with_1000(b: &mut Bencher) {\n    use self::sycamore::*;\n    use ::sycamore::{prelude::*, *};\n\n    b.iter(|| {\n        _ = create_scope(|cx| {\n            let rendered = render_to_string(|cx| {\n                view! {\n                    cx,\n                    AppWith1000()\n                }\n            });\n\n            assert!(rendered.len() > 1);\n        });\n    });\n}\n\n#[bench]\nfn yew_todomvc_ssr_with_1000(b: &mut Bencher) {\n    use self::yew::*;\n    use ::yew::{prelude::*, ServerRenderer};\n\n    b.iter(|| {\n        tokio_test::block_on(async {\n            let renderer = ServerRenderer::<AppWith1000>::new();\n            let rendered = renderer.render().await;\n            assert!(rendered.len() > 1);\n        });\n    });\n}\n\n#[bench]\nfn tera_todomvc_ssr(b: &mut Bencher) {\n    use ::leptos::*;\n    let runtime = create_runtime();\n    b.iter(|| {\n        use crate::todomvc::leptos::*;\n\n        let html = ::leptos::ssr::render_to_string(|| {\n            view! { <TodoMVC todos=Todos::new()/> }\n        });\n        assert!(html.len() > 1);\n    });\n    runtime.dispose();\n}\n"
  },
  {
    "path": "benchmarks/src/todomvc/sycamore.rs",
    "content": "use serde::{Deserialize, Serialize};\nuse sycamore::prelude::*;\nuse uuid::Uuid;\nuse wasm_bindgen::JsCast;\nuse web_sys::{Event, HtmlInputElement, KeyboardEvent};\n\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]\npub struct Todo {\n    title: String,\n    completed: bool,\n    id: usize,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Filter {\n    All,\n    Active,\n    Completed,\n}\n\nimpl Default for Filter {\n    fn default() -> Self {\n        Self::All\n    }\n}\n\nimpl Filter {\n    fn url(self) -> &'static str {\n        match self {\n            Filter::All => \"#\",\n            Filter::Active => \"#/active\",\n            Filter::Completed => \"#/completed\",\n        }\n    }\n\n    fn get_filter_from_hash() -> Self {\n        let hash = web_sys::window().unwrap().location().hash().unwrap();\n\n        match hash.as_str() {\n            \"#/active\" => Filter::Active,\n            \"#/completed\" => Filter::Completed,\n            _ => Filter::All,\n        }\n    }\n}\n\n#[derive(Debug, Default, Clone)]\npub struct AppState {\n    pub todos: RcSignal<Vec<RcSignal<Todo>>>,\n    pub filter: RcSignal<Filter>,\n}\n\nimpl AppState {\n    fn add_todo(&self, title: String, id: usize) {\n        self.todos.modify().push(create_rc_signal(Todo {\n            title,\n            completed: false,\n            id,\n        }))\n    }\n\n    fn remove_todo(&self, id: usize) {\n        self.todos.modify().retain(|todo| todo.get().id != id);\n    }\n\n    fn todos_left(&self) -> usize {\n        self.todos.get().iter().fold(\n            0,\n            |acc, todo| if todo.get().completed { acc } else { acc + 1 },\n        )\n    }\n\n    fn toggle_complete_all(&self) {\n        if self.todos_left() == 0 {\n            // make all todos active\n            for todo in self.todos.get().iter() {\n                if todo.get().completed {\n                    todo.set(Todo {\n                        completed: false,\n                        ..todo.get().as_ref().clone()\n                    })\n                }\n            }\n        } else {\n            // make all todos completed\n            for todo in self.todos.get().iter() {\n                if !todo.get().completed {\n                    todo.set(Todo {\n                        completed: true,\n                        ..todo.get().as_ref().clone()\n                    })\n                }\n            }\n        }\n    }\n\n    fn clear_completed(&self) {\n        self.todos.modify().retain(|todo| !todo.get().completed);\n    }\n}\n\nconst KEY: &str = \"todos-sycamore\";\n\n#[component]\npub fn App<G: Html>(cx: Scope) -> View<G> {\n    // Initialize application state\n    let todos = create_rc_signal(Vec::new());\n    let app_state = AppState {\n        todos,\n        filter: create_rc_signal(Filter::All),\n    };\n    provide_context(cx, app_state);\n\n    view! { cx,\n        div(class=\"todomvc-wrapper\") {\n            section(class=\"todoapp\") {\n                Header {}\n                List {}\n                Footer {}\n            }\n            Copyright {}\n        }\n    }\n}\n\n#[component]\npub fn AppWith1000<G: Html>(cx: Scope) -> View<G> {\n    // Initialize application state\n    let todos = (0..1000)\n        .map(|id| {\n            create_rc_signal(Todo {\n                title: format!(\"Todo #{id}\"),\n                completed: false,\n                id,\n            })\n        })\n        .collect();\n    let todos = create_rc_signal(todos);\n    let app_state = AppState {\n        todos,\n        filter: create_rc_signal(Filter::All),\n    };\n    provide_context(cx, app_state);\n\n    view! { cx,\n        div(class=\"todomvc-wrapper\") {\n            section(class=\"todoapp\") {\n                Header {}\n                List {}\n                Footer {}\n            }\n            Copyright {}\n        }\n    }\n}\n\n#[component]\npub fn Copyright<G: Html>(cx: Scope) -> View<G> {\n    view! { cx,\n        footer(class=\"info\") {\n            p { \"Double click to edit a todo\" }\n            p {\n                \"Created by \"\n                a(href=\"https://github.com/lukechu10\", target=\"_blank\") { \"lukechu10\" }\n            }\n            p {\n                \"Part of \"\n                a(href=\"http://todomvc.com\") { \"TodoMVC\" }\n            }\n        }\n    }\n}\n\n#[component]\npub fn Header<G: Html>(cx: Scope) -> View<G> {\n    let app_state = use_context::<AppState>(cx);\n    let value = create_signal(cx, String::new());\n    let input_ref = create_node_ref(cx);\n\n    let handle_submit = |event: Event| {\n        let event: KeyboardEvent = event.unchecked_into();\n\n        if event.key() == \"Enter\" {\n            let mut task = value.get().as_ref().clone();\n            task = task.trim().to_string();\n\n            if !task.is_empty() {\n                app_state.add_todo(task, 0);\n                value.set(\"\".to_string());\n                input_ref\n                    .get::<DomNode>()\n                    .unchecked_into::<HtmlInputElement>()\n                    .set_value(\"\");\n            }\n        }\n    };\n\n    view! { cx,\n        header(class=\"header\") {\n            h1 { \"todos\" }\n            input(ref=input_ref,\n                class=\"new-todo\",\n                placeholder=\"What needs to be done?\",\n                bind:value=value,\n                on:keyup=handle_submit,\n            )\n        }\n    }\n}\n\n#[component(inline_props)]\npub fn Item<G: Html>(cx: Scope, todo: RcSignal<Todo>) -> View<G> {\n    let app_state = use_context::<AppState>(cx);\n    // Make `todo` live as long as the scope.\n    let todo = create_ref(cx, todo);\n\n    let title = || todo.get().title.clone();\n    let completed = create_selector(cx, || todo.get().completed);\n    let id = todo.get().id;\n\n    let editing = create_signal(cx, false);\n    let input_ref = create_node_ref(cx);\n    let value = create_signal(cx, \"\".to_string());\n\n    let handle_input = |event: Event| {\n        let target: HtmlInputElement = event.target().unwrap().unchecked_into();\n        value.set(target.value());\n    };\n\n    let toggle_completed = |_| {\n        todo.set(Todo {\n            completed: !todo.get().completed,\n            ..todo.get().as_ref().clone()\n        });\n    };\n\n    let handle_dblclick = move |_| {\n        editing.set(true);\n        input_ref\n            .get::<DomNode>()\n            .unchecked_into::<HtmlInputElement>()\n            .focus()\n            .unwrap();\n        value.set(title());\n    };\n\n    let handle_blur = move || {\n        editing.set(false);\n\n        let mut value = value.get().as_ref().clone();\n        value = value.trim().to_string();\n\n        if value.is_empty() {\n            app_state.remove_todo(id);\n        } else {\n            todo.set(Todo {\n                title: value,\n                ..todo.get().as_ref().clone()\n            })\n        }\n    };\n\n    let handle_submit = move |event: Event| {\n        let event: KeyboardEvent = event.unchecked_into();\n        match event.key().as_str() {\n            \"Enter\" => handle_blur(),\n            \"Escape\" => {\n                input_ref\n                    .get::<DomNode>()\n                    .unchecked_into::<HtmlInputElement>()\n                    .set_value(&title());\n                editing.set(false);\n            }\n            _ => {}\n        }\n    };\n\n    let handle_destroy = move |_| {\n        app_state.remove_todo(id);\n    };\n\n    // We need a separate signal for checked because clicking the checkbox will detach the binding\n    // between the attribute and the view.\n    let checked = create_signal(cx, false);\n    create_effect(cx, || {\n        // Calling checked.set will also update the `checked` property on the input element.\n        checked.set(*completed.get())\n    });\n\n    let class = || {\n        format!(\n            \"{} {}\",\n            if *completed.get() { \"completed\" } else { \"\" },\n            if *editing.get() { \"editing\" } else { \"\" }\n        )\n    };\n\n    view! { cx,\n        li(class=class()) {\n            div(class=\"view\") {\n                input(\n                    class=\"toggle\",\n                    type=\"checkbox\",\n                    on:input=toggle_completed,\n                    bind:checked=checked\n                )\n                label(on:dblclick=handle_dblclick) {\n                    (title())\n                }\n                button(class=\"destroy\", on:click=handle_destroy)\n            }\n\n            (if *editing.get() {\n                view! { cx,\n                    input(ref=input_ref,\n                        class=\"edit\",\n                        prop:value=&todo.get().title,\n                        on:blur=move |_| handle_blur(),\n                        on:keyup=handle_submit,\n                        on:input=handle_input,\n                    )\n                }\n            } else {\n                View::empty()\n            })\n        }\n    }\n}\n\n#[component]\npub fn List<G: Html>(cx: Scope) -> View<G> {\n    let app_state = use_context::<AppState>(cx);\n    let todos_left = create_selector(cx, || app_state.todos_left());\n\n    let filtered_todos = create_memo(cx, || {\n        app_state\n            .todos\n            .get()\n            .iter()\n            .filter(|todo| match *app_state.filter.get() {\n                Filter::All => true,\n                Filter::Active => !todo.get().completed,\n                Filter::Completed => todo.get().completed,\n            })\n            .cloned()\n            .collect::<Vec<_>>()\n    });\n\n    // We need a separate signal for checked because clicking the checkbox will detach the binding\n    // between the attribute and the view.\n    let checked = create_signal(cx, false);\n    create_effect(cx, || {\n        // Calling checked.set will also update the `checked` property on the input element.\n        checked.set(*todos_left.get() == 0)\n    });\n\n    view! { cx,\n        section(class=\"main\") {\n            input(\n                id=\"toggle-all\",\n                class=\"toggle-all\",\n                type=\"checkbox\",\n                readonly=true,\n                bind:checked=checked,\n                on:input=|_| app_state.toggle_complete_all()\n            )\n            label(for=\"toggle-all\")\n\n            ul(class=\"todo-list\") {\n                Keyed(\n                    iterable=filtered_todos,\n                    view=|cx, todo| view! { cx,\n                        Item(todo=todo)\n                    },\n                    key=|todo| todo.get().id,\n                )\n            }\n        }\n    }\n}\n\n#[component(inline_props)]\npub fn TodoFilter<G: Html>(cx: Scope, filter: Filter) -> View<G> {\n    let app_state = use_context::<AppState>(cx);\n    let selected = move || filter == *app_state.filter.get();\n    let set_filter = |filter| app_state.filter.set(filter);\n\n    view! { cx,\n        li {\n            a(\n                class=if selected() { \"selected\" } else { \"\" },\n                href=filter.url(),\n                on:click=move |_| set_filter(filter),\n            ) {\n                (format!(\"{filter:?}\"))\n            }\n        }\n    }\n}\n\n#[component]\npub fn Footer<G: Html>(cx: Scope) -> View<G> {\n    let app_state = use_context::<AppState>(cx);\n\n    let items_text = || match app_state.todos_left() {\n        1 => \"item\",\n        _ => \"items\",\n    };\n\n    let has_completed_todos =\n        create_selector(cx, || app_state.todos_left() < app_state.todos.get().len());\n\n    let handle_clear_completed = |_| app_state.clear_completed();\n\n    view! { cx,\n        footer(class=\"footer\") {\n            span(class=\"todo-count\") {\n                strong { (app_state.todos_left()) }\n                span { \" \" (items_text()) \" left\" }\n            }\n            ul(class=\"filters\") {\n                TodoFilter(filter=Filter::All)\n                TodoFilter(filter=Filter::Active)\n                TodoFilter(filter=Filter::Completed)\n            }\n\n            (if *has_completed_todos.get() {\n                view! { cx,\n                    button(class=\"clear-completed\", on:click=handle_clear_completed) {\n                        \"Clear completed\"\n                    }\n                }\n            } else {\n                view! { cx, }\n            })\n        }\n    }\n}\n"
  },
  {
    "path": "benchmarks/src/todomvc/tachys.rs",
    "content": "pub use leptos_reactive::*;\nuse miniserde::*;\nuse tachy_maccy::view;\nuse tachydom::{\n    html::{\n        attribute::global::{ClassAttribute, GlobalAttributes, OnAttribute},\n        element::ElementChild,\n    },\n    renderer::dom::Dom,\n    view::{keyed::keyed, Render, RenderHtml},\n};\nuse wasm_bindgen::JsCast;\nuse web_sys::HtmlInputElement;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Todos(pub Vec<Todo>);\n\nconst STORAGE_KEY: &str = \"todos-leptos\";\n\nimpl Todos {\n    pub fn new() -> Self {\n        Self(vec![])\n    }\n\n    pub fn new_with_1000() -> Self {\n        let todos = (0..1000)\n            .map(|id| Todo::new(id, format!(\"Todo #{id}\")))\n            .collect();\n        Self(todos)\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    pub fn add(&mut self, todo: Todo) {\n        self.0.push(todo);\n    }\n\n    pub fn remove(&mut self, id: usize) {\n        self.0.retain(|todo| todo.id != id);\n    }\n\n    pub fn remaining(&self) -> usize {\n        self.0.iter().filter(|todo| !(todo.completed)()).count()\n    }\n\n    pub fn completed(&self) -> usize {\n        self.0.iter().filter(|todo| (todo.completed)()).count()\n    }\n\n    pub fn toggle_all(&self) {\n        // if all are complete, mark them all active instead\n        if self.remaining() == 0 {\n            for todo in &self.0 {\n                if todo.completed.get() {\n                    (todo.set_completed)(false);\n                }\n            }\n        }\n        // otherwise, mark them all complete\n        else {\n            for todo in &self.0 {\n                (todo.set_completed)(true);\n            }\n        }\n    }\n\n    fn clear_completed(&mut self) {\n        self.0.retain(|todo| !todo.completed.get());\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct Todo {\n    pub id: usize,\n    pub title: ReadSignal<String>,\n    pub set_title: WriteSignal<String>,\n    pub completed: ReadSignal<bool>,\n    pub set_completed: WriteSignal<bool>,\n}\n\nimpl Todo {\n    pub fn new(id: usize, title: String) -> Self {\n        Self::new_with_completed(id, title, false)\n    }\n\n    pub fn new_with_completed(\n        id: usize,\n        title: String,\n        completed: bool,\n    ) -> Self {\n        let (title, set_title) = create_signal(title);\n        let (completed, set_completed) = create_signal(completed);\n        Self {\n            id,\n            title,\n            set_title,\n            completed,\n            set_completed,\n        }\n    }\n\n    pub fn toggle(&self) {\n        self.set_completed\n            .update(|completed| *completed = !*completed);\n    }\n}\n\nconst ESCAPE_KEY: u32 = 27;\nconst ENTER_KEY: u32 = 13;\n\npub fn TodoMVC(todos: Todos) -> impl Render<Dom> + RenderHtml<Dom> {\n    let mut next_id = todos\n        .0\n        .iter()\n        .map(|todo| todo.id)\n        .max()\n        .map(|last| last + 1)\n        .unwrap_or(0);\n\n    let (todos, set_todos) = create_signal(todos);\n    provide_context(set_todos);\n\n    let (mode, set_mode) = create_signal(Mode::All);\n\n    let add_todo = move |ev: web_sys::KeyboardEvent| {\n        todo!()\n        /* let target = event_target::<HtmlInputElement>(&ev);\n        ev.stop_propagation();\n        let key_code = ev.unchecked_ref::<web_sys::KeyboardEvent>().key_code();\n        if key_code == ENTER_KEY {\n            let title = event_target_value(&ev);\n            let title = title.trim();\n            if !title.is_empty() {\n                let new = Todo::new(next_id, title.to_string());\n                set_todos.update(|t| t.add(new));\n                next_id += 1;\n                target.set_value(\"\");\n            }\n        } */\n    };\n\n    let filtered_todos = create_memo::<Vec<Todo>>(move |_| {\n        todos.with(|todos| match mode.get() {\n            Mode::All => todos.0.to_vec(),\n            Mode::Active => todos\n                .0\n                .iter()\n                .filter(|todo| !todo.completed.get())\n                .cloned()\n                .collect(),\n            Mode::Completed => todos\n                .0\n                .iter()\n                .filter(|todo| todo.completed.get())\n                .cloned()\n                .collect(),\n        })\n    });\n\n    // effect to serialize to JSON\n    // this does reactive reads, so it will automatically serialize on any relevant change\n    create_effect(move |_| {\n        ()\n        /* if let Ok(Some(storage)) = window().local_storage() {\n            let objs = todos\n                .get()\n                .0\n                .iter()\n                .map(TodoSerialized::from)\n                .collect::<Vec<_>>();\n            let json = json::to_string(&objs);\n            if storage.set_item(STORAGE_KEY, &json).is_err() {\n                log::error!(\"error while trying to set item in localStorage\");\n            }\n        } */\n    });\n\n    view! {\n        <main>\n            <section class=\"todoapp\">\n                <header class=\"header\">\n                    <h1>\"todos\"</h1>\n                    <input\n                        class=\"new-todo\"\n                        placeholder=\"What needs to be done?\"\n                        autofocus\n                    />\n                </header>\n                <section class=\"main\" class:hidden=move || todos.with(|t| t.is_empty())>\n                    <input\n                        id=\"toggle-all\"\n                        class=\"toggle-all\"\n                        r#type=\"checkbox\"\n                        //prop:checked=move || todos.with(|t| t.remaining() > 0)\n                        on:input=move |_| set_todos.update(|t| t.toggle_all())\n                    />\n                    <label r#for=\"toggle-all\">\"Mark all as complete\"</label>\n                    <ul class=\"todo-list\">\n                        {move || {\n                            keyed(filtered_todos.get(), |todo| todo.id, Todo)\n                        }}\n                    </ul>\n                </section>\n                <footer class=\"footer\" class:hidden=move || todos.with(|t| t.is_empty())>\n                    <span class=\"todo-count\">\n                        <strong>{move || todos.with(|t| t.remaining().to_string())}</strong>\n                        {move || if todos.with(|t| t.remaining()) == 1 { \" item\" } else { \" items\" }}\n                        \" left\"\n                    </span>\n                    <ul class=\"filters\">\n                        <li>\n                            <a\n                                href=\"#/\"\n                                class=\"selected\"\n                                class:selected=move || mode() == Mode::All\n                            >\n                                \"All\"\n                            </a>\n                        </li>\n                        <li>\n                            <a href=\"#/active\" class:selected=move || mode() == Mode::Active>\n                                \"Active\"\n                            </a>\n                        </li>\n                        <li>\n                            <a href=\"#/completed\" class:selected=move || mode() == Mode::Completed>\n                                \"Completed\"\n                            </a>\n                        </li>\n                    </ul>\n                    <button\n                        class=\"clear-completed hidden\"\n                        class:hidden=move || todos.with(|t| t.completed() == 0)\n                        on:click=move |_| set_todos.update(|t| t.clear_completed())\n                    >\n                        \"Clear completed\"\n                    </button>\n                </footer>\n            </section>\n            <footer class=\"info\">\n                <p>\"Double-click to edit a todo\"</p>\n                <p>\"Created by \" <a href=\"http://todomvc.com\">\"Greg Johnston\"</a></p>\n                <p>\"Part of \" <a href=\"http://todomvc.com\">\"TodoMVC\"</a></p>\n            </footer>\n        </main>\n    }\n}\n\npub fn Todo(todo: Todo) -> impl Render<Dom> + RenderHtml<Dom> {\n    let (editing, set_editing) = create_signal(false);\n    let set_todos = use_context::<WriteSignal<Todos>>().unwrap();\n    //let input = NodeRef::new();\n\n    let save = move |value: &str| {\n        let value = value.trim();\n        if value.is_empty() {\n            set_todos.update(|t| t.remove(todo.id));\n        } else {\n            (todo.set_title)(value.to_string());\n        }\n        set_editing(false);\n    };\n\n    view! {\n        <li class=\"todo\" class:editing=editing class:completed=move || (todo.completed)()>\n            /* <div class=\"view\">\n                <input class=\"toggle\" r#type=\"checkbox\"/>\n                <label on:dblclick=move |_| set_editing(true)>{move || todo.title.get()}</label>\n                <button\n                    class=\"destroy\"\n                    on:click=move |_| set_todos.update(|t| t.remove(todo.id))\n                ></button>\n            </div>\n            {move || {\n                editing()\n                    .then(|| {\n                        view! {\n                            <input\n                                class=\"edit\"\n                                class:hidden=move || !(editing)()\n                            />\n                        }\n                    })\n            }} */\n        </li>\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Mode {\n    Active,\n    Completed,\n    All,\n}\n\nimpl Default for Mode {\n    fn default() -> Self {\n        Mode::All\n    }\n}\n\npub fn route(hash: &str) -> Mode {\n    match hash {\n        \"/active\" => Mode::Active,\n        \"/completed\" => Mode::Completed,\n        _ => Mode::All,\n    }\n}\n\n#[derive(Serialize, Deserialize)]\npub struct TodoSerialized {\n    pub id: usize,\n    pub title: String,\n    pub completed: bool,\n}\n\nimpl TodoSerialized {\n    pub fn into_todo(self) -> Todo {\n        Todo::new_with_completed(self.id, self.title, self.completed)\n    }\n}\n\nimpl From<&Todo> for TodoSerialized {\n    fn from(todo: &Todo) -> Self {\n        Self {\n            id: todo.id,\n            title: todo.title.get(),\n            completed: (todo.completed)(),\n        }\n    }\n}\n"
  },
  {
    "path": "benchmarks/src/todomvc/tera.rs",
    "content": "use test::Bencher;\n\nstatic TEMPLATE: &str = r#\"<main>\n            <section class=\"todoapp\">\n                <header class=\"header\">\n                    <h1>\"todos\"</h1>\n                    <input class=\"new-todo\" placeholder=\"What needs to be done? />\n                </header>\n                <section class=\"main\" class={{ main_class }}>\n                    <input id=\"toggle-all\" class=\"toggle-all\" type=\"checkbox\"\n\t\t\t\t\t\tchecked={{ toggle_checked }}\n                    />\n                    <label for=\"toggle-all\">\"Mark all as complete\"</label>\n                    <ul class=\"todo-list\">\n                        {% for todo in todos %}\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tclass={{ todo.class }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div class=\"view\">\n\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\tclass=\"toggle\"\n\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\tchecked={{ todo.completed }}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t\t{{ todo.label }}\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t<button class=\"destroy\"/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{% if todo.editing %}\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\tclass=\"edit\"\n\t\t\t\t\t\t\t\tvalue={{ todo.label }}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{% endif %}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t{% endfor %}\n                    </ul>\n                </section>\n\t\t\t\t{% if todos_empty %}\n\t\t\t\t{% else %}\n                <footer class=\"footer\">\n                    <span class=\"todo-count\">\n                        <strong>{{ todos_remaining }}</strong>\n\t\t\t\t\t\t{% if todos_remaining == 1 %}\n\t\t\t\t\t\titem\n\t\t\t\t\t\t{% else %}\n\t\t\t\t\t\titems\n\t\t\t\t\t\t{% endif %}\n\t\t\t\t\t\tleft\n                    </span>\n                    <ul class=\"filters\">\n\t\t\t\t\t\t{% if mode_all %}\n                        <li><a href=\"/\" class=\"selected\">All</a></li>\n\t\t\t\t\t\t{% else %}\n\t\t\t\t\t\t <li><a href=\"/\">All</a></li>\n\t\t\t\t\t\t{% endif %}\n\n\t\t\t\t\t\t{% if mode_active %}\n                        <li><a href=\"/active\" class=\"selected\">Active</a></li>\n\t\t\t\t\t\t{% else %}\n\t\t\t\t\t\t <li><a href=\"/active\">Active</a></li>\n\t\t\t\t\t\t{% endif %}\n\n\t\t\t\t\t\t{% if mode_completed %}\n                        <li><a href=\"/completed\" class=\"selected\">Completed</a></li>\n\t\t\t\t\t\t{% else %}\n\t\t\t\t\t\t<li><a href=\"/completed\">Completed</a></li>\n\t\t\t\t\t\t{% endif %}\n                    </ul>\n\n\t\t\t\t\t{% if todos_completed > 0 %}\n                    <button\n                        class=\"clear-completed hidden\"\n                    >\n                        Clear completed\n                    </button>\n\t\t\t\t\t{% endif %}\n                </footer>\n\t\t\t\t{% endif %}\n            </section>\n            <footer class=\"info\">\n                <p>\"Double-click to edit a todo\"</p>\n                <p>\"Created by \"<a href=\"http://todomvc.com\">\"Greg Johnston\"</a></p>\n                <p>\"Part of \"<a href=\"http://todomvc.com\">\"TodoMVC\"</a></p>\n            </footer>\n        </main>\"#;\n\n#[bench]\nfn tera_todomvc_ssr(b: &mut Bencher) {\n    use serde::{Deserialize, Serialize};\n    use tera::*;\n\n\n        static LazyLock<TERA>: Tera = LazyLock( || {\n            let mut tera = Tera::default();\n            tera.add_raw_templates(vec![(\"template.html\", TEMPLATE)]).unwrap();\n            tera\n        });\n\n\n    #[derive(Serialize, Deserialize)]\n    struct Todo {\n        label: String,\n        completed: bool,\n        editing: bool,\n        class: String,\n    }\n\n    b.iter(|| {\n        let mut ctx = Context::new();\n        let todos = Vec::<Todo>::new();\n        let remaining = todos.iter().filter(|todo| !todo.completed).count();\n        let completed = todos.iter().filter(|todo| todo.completed).count();\n        ctx.insert(\"todos\", &todos);\n        ctx.insert(\"main_class\", &if todos.is_empty() { \"hidden\" } else { \"\" });\n        ctx.insert(\"toggle_checked\", &(remaining > 0));\n        ctx.insert(\"todos_remaining\", &remaining);\n        ctx.insert(\"todos_completed\", &completed);\n        ctx.insert(\"todos_empty\", &todos.is_empty());\n        ctx.insert(\"mode_all\", &true);\n        ctx.insert(\"mode_active\", &false);\n        ctx.insert(\"mode_selected\", &false);\n\n        let _ = TERA.render(\"template.html\", &ctx).unwrap();\n    });\n}\n\n#[bench]\nfn tera_todomvc_ssr_1000(b: &mut Bencher) {\n    use serde::{Deserialize, Serialize};\n    use tera::*;\n\n\n    static  TERA: LazyLock<Tera> = LazyLock::new(|| {\n        let mut tera = Tera::default();\n        tera.add_raw_templates(vec![(\"template.html\", TEMPLATE)]).unwrap();\n        tera\n    });\n\n\n    #[derive(Serialize, Deserialize)]\n    struct Todo {\n        id: usize,\n        label: String,\n        completed: bool,\n        editing: bool,\n        class: String,\n    }\n\n    b.iter(|| {\n        let mut ctx = Context::new();\n        let todos = (0..1000)\n            .map(|id| Todo {\n                id,\n                label: format!(\"Todo #{id}\"),\n                completed: false,\n                editing: false,\n                class: \"todo\".to_string(),\n            })\n            .collect::<Vec<_>>();\n\n        let remaining = todos.iter().filter(|todo| !todo.completed).count();\n        let completed = todos.iter().filter(|todo| todo.completed).count();\n        ctx.insert(\"todos\", &todos);\n        ctx.insert(\"main_class\", &if todos.is_empty() { \"hidden\" } else { \"\" });\n        ctx.insert(\"toggle_checked\", &(remaining > 0));\n        ctx.insert(\"todos_remaining\", &remaining);\n        ctx.insert(\"todos_completed\", &completed);\n        ctx.insert(\"todos_empty\", &todos.is_empty());\n        ctx.insert(\"mode_all\", &true);\n        ctx.insert(\"mode_active\", &false);\n        ctx.insert(\"mode_selected\", &false);\n\n        let _ = TERA.render(\"template.html\", &ctx).unwrap();\n    });\n}\n"
  },
  {
    "path": "benchmarks/src/todomvc/yew.rs",
    "content": "use gloo::storage::{LocalStorage, Storage};\nuse strum::IntoEnumIterator;\nuse web_sys::HtmlInputElement as InputElement;\nuse yew::events::{FocusEvent, KeyboardEvent};\nuse yew::html::Scope;\nuse yew::{classes, html, Classes, Component, Context, Html, NodeRef, TargetCast};\n\nconst KEY: &str = \"yew.todomvc.self\";\n\npub enum Msg {\n    Add(String),\n    Edit((usize, String)),\n    Remove(usize),\n    SetFilter(Filter),\n    ToggleAll,\n    ToggleEdit(usize),\n    Toggle(usize),\n    ClearCompleted,\n    Focus,\n}\n\npub struct App {\n    state: State,\n    focus_ref: NodeRef,\n}\n\nimpl Component for App {\n    type Message = Msg;\n    type Properties = ();\n\n    fn create(_ctx: &Context<Self>) -> Self {\n        let entries = vec![]; //LocalStorage::get(KEY).unwrap_or_else(|_| Vec::new());\n        let state = State {\n            entries,\n            filter: Filter::All,\n            edit_value: \"\".into(),\n        };\n        let focus_ref = NodeRef::default();\n        Self { state, focus_ref }\n    }\n\n    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {\n        match msg {\n            Msg::Add(description) => {\n                if !description.is_empty() {\n                    let entry = Entry {\n                        description: description.trim().to_string(),\n                        completed: false,\n                        editing: false,\n                    };\n                    self.state.entries.push(entry);\n                }\n            }\n            Msg::Edit((idx, edit_value)) => {\n                self.state.complete_edit(idx, edit_value.trim().to_string());\n                self.state.edit_value = \"\".to_string();\n            }\n            Msg::Remove(idx) => {\n                self.state.remove(idx);\n            }\n            Msg::SetFilter(filter) => {\n                self.state.filter = filter;\n            }\n            Msg::ToggleEdit(idx) => {\n                let entry = self\n                    .state\n                    .entries\n                    .iter()\n                    .filter(|e| self.state.filter.fits(e))\n                    .nth(idx)\n                    .unwrap();\n                self.state.edit_value = entry.description.clone();\n                self.state.clear_all_edit();\n                self.state.toggle_edit(idx);\n            }\n            Msg::ToggleAll => {\n                let status = !self.state.is_all_completed();\n                self.state.toggle_all(status);\n            }\n            Msg::Toggle(idx) => {\n                self.state.toggle(idx);\n            }\n            Msg::ClearCompleted => {\n                self.state.clear_completed();\n            }\n            Msg::Focus => {\n                if let Some(input) = self.focus_ref.cast::<InputElement>() {\n                    input.focus().unwrap();\n                }\n            }\n        }\n        LocalStorage::set(KEY, &self.state.entries).expect(\"failed to set\");\n        true\n    }\n\n    fn view(&self, ctx: &Context<Self>) -> Html {\n        let hidden_class = if self.state.entries.is_empty() {\n            \"hidden\"\n        } else {\n            \"\"\n        };\n        html! {\n            <div class=\"todomvc-wrapper\">\n                <section class=\"todoapp\">\n                    <header class=\"header\">\n                        <h1>{ \"todos\" }</h1>\n                        { self.view_input(ctx.link()) }\n                    </header>\n                    <section class={classes!(\"main\", hidden_class)}>\n                        <input\n                            type=\"checkbox\"\n                            class=\"toggle-all\"\n                            id=\"toggle-all\"\n                            checked={self.state.is_all_completed()}\n                            onclick={ctx.link().callback(|_| Msg::ToggleAll)}\n                        />\n                        <label for=\"toggle-all\" />\n                        <ul class=\"todo-list\">\n                            { for self.state.entries.iter().filter(|e| self.state.filter.fits(e)).enumerate().map(|e| self.view_entry(e, ctx.link())) }\n                        </ul>\n                    </section>\n                    <footer class={classes!(\"footer\", hidden_class)}>\n                        <span class=\"todo-count\">\n                            <strong>{ self.state.total() }</strong>\n                            { \" item(s) left\" }\n                        </span>\n                        <ul class=\"filters\">\n                            { for Filter::iter().map(|flt| self.view_filter(flt, ctx.link())) }\n                        </ul>\n                        <button class=\"clear-completed\" onclick={ctx.link().callback(|_| Msg::ClearCompleted)}>\n                            { format!(\"Clear completed ({})\", self.state.total_completed()) }\n                        </button>\n                    </footer>\n                </section>\n                <footer class=\"info\">\n                    <p>{ \"Double-click to edit a todo\" }</p>\n                    <p>{ \"Written by \" }<a href=\"https://github.com/DenisKolodin/\" target=\"_blank\">{ \"Denis Kolodin\" }</a></p>\n                    <p>{ \"Part of \" }<a href=\"http://todomvc.com/\" target=\"_blank\">{ \"TodoMVC\" }</a></p>\n                </footer>\n            </div>\n        }\n    }\n}\n\nimpl App {\n    fn view_filter(&self, filter: Filter, link: &Scope<Self>) -> Html {\n        let cls = if self.state.filter == filter {\n            \"selected\"\n        } else {\n            \"not-selected\"\n        };\n        html! {\n            <li>\n                <a class={cls}\n                   href={filter.as_href()}\n                   onclick={link.callback(move |_| Msg::SetFilter(filter))}\n                >\n                    { filter }\n                </a>\n            </li>\n        }\n    }\n\n    fn view_input(&self, link: &Scope<Self>) -> Html {\n        let onkeypress = link.batch_callback(|e: KeyboardEvent| {\n            if e.key() == \"Enter\" {\n                let input: InputElement = e.target_unchecked_into();\n                let value = input.value();\n                input.set_value(\"\");\n                Some(Msg::Add(value))\n            } else {\n                None\n            }\n        });\n        html! {\n            // You can use standard Rust comments. One line:\n            // <li></li>\n            <input\n                class=\"new-todo\"\n                placeholder=\"What needs to be done?\"\n                {onkeypress}\n            />\n            /* Or multiline:\n            <ul>\n                <li></li>\n            </ul>\n            */\n        }\n    }\n\n    fn view_entry(&self, (idx, entry): (usize, &Entry), link: &Scope<Self>) -> Html {\n        let mut class = Classes::from(\"todo\");\n        if entry.editing {\n            class.push(\" editing\");\n        }\n        if entry.completed {\n            class.push(\" completed\");\n        }\n        html! {\n            <li {class}>\n                <div class=\"view\">\n                    <input\n                        type=\"checkbox\"\n                        class=\"toggle\"\n                        checked={entry.completed}\n                        onclick={link.callback(move |_| Msg::Toggle(idx))}\n                    />\n                    <label ondblclick={link.callback(move |_| Msg::ToggleEdit(idx))}>{ &entry.description }</label>\n                    <button class=\"destroy\" onclick={link.callback(move |_| Msg::Remove(idx))} />\n                </div>\n                { self.view_entry_edit_input((idx, entry), link) }\n            </li>\n        }\n    }\n\n    fn view_entry_edit_input(&self, (idx, entry): (usize, &Entry), link: &Scope<Self>) -> Html {\n        let edit = move |input: InputElement| {\n            let value = input.value();\n            input.set_value(\"\");\n            Msg::Edit((idx, value))\n        };\n\n        let onblur = link.callback(move |e: FocusEvent| edit(e.target_unchecked_into()));\n\n        let onkeypress = link.batch_callback(move |e: KeyboardEvent| {\n            (e.key() == \"Enter\").then(|| edit(e.target_unchecked_into()))\n        });\n\n        if entry.editing {\n            html! {\n                <input\n                    class=\"edit\"\n                    type=\"text\"\n                    ref={self.focus_ref.clone()}\n                    value={self.state.edit_value.clone()}\n                    onmouseover={link.callback(|_| Msg::Focus)}\n                    {onblur}\n                    {onkeypress}\n                />\n            }\n        } else {\n            html! { <input type=\"hidden\" /> }\n        }\n    }\n}\n\npub struct AppWith1000 {\n    state: State,\n    focus_ref: NodeRef,\n}\n\nimpl Component for AppWith1000 {\n    type Message = Msg;\n    type Properties = ();\n\n    fn create(_ctx: &Context<Self>) -> Self {\n        let entries = (0..1000)\n            .map(|id| Entry {\n                description: format!(\"Todo #{id}\"),\n                completed: false,\n                editing: false,\n            })\n            .collect();\n        let state = State {\n            entries,\n            filter: Filter::All,\n            edit_value: \"\".into(),\n        };\n        let focus_ref = NodeRef::default();\n        Self { state, focus_ref }\n    }\n\n    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {\n        match msg {\n            Msg::Add(description) => {\n                if !description.is_empty() {\n                    let entry = Entry {\n                        description: description.trim().to_string(),\n                        completed: false,\n                        editing: false,\n                    };\n                    self.state.entries.push(entry);\n                }\n            }\n            Msg::Edit((idx, edit_value)) => {\n                self.state.complete_edit(idx, edit_value.trim().to_string());\n                self.state.edit_value = \"\".to_string();\n            }\n            Msg::Remove(idx) => {\n                self.state.remove(idx);\n            }\n            Msg::SetFilter(filter) => {\n                self.state.filter = filter;\n            }\n            Msg::ToggleEdit(idx) => {\n                let entry = self\n                    .state\n                    .entries\n                    .iter()\n                    .filter(|e| self.state.filter.fits(e))\n                    .nth(idx)\n                    .unwrap();\n                self.state.edit_value = entry.description.clone();\n                self.state.clear_all_edit();\n                self.state.toggle_edit(idx);\n            }\n            Msg::ToggleAll => {\n                let status = !self.state.is_all_completed();\n                self.state.toggle_all(status);\n            }\n            Msg::Toggle(idx) => {\n                self.state.toggle(idx);\n            }\n            Msg::ClearCompleted => {\n                self.state.clear_completed();\n            }\n            Msg::Focus => {\n                if let Some(input) = self.focus_ref.cast::<InputElement>() {\n                    input.focus().unwrap();\n                }\n            }\n        }\n        LocalStorage::set(KEY, &self.state.entries).expect(\"failed to set\");\n        true\n    }\n\n    fn view(&self, ctx: &Context<Self>) -> Html {\n        let hidden_class = if self.state.entries.is_empty() {\n            \"hidden\"\n        } else {\n            \"\"\n        };\n        html! {\n            <div class=\"todomvc-wrapper\">\n                <section class=\"todoapp\">\n                    <header class=\"header\">\n                        <h1>{ \"todos\" }</h1>\n                        { self.view_input(ctx.link()) }\n                    </header>\n                    <section class={classes!(\"main\", hidden_class)}>\n                        <input\n                            type=\"checkbox\"\n                            class=\"toggle-all\"\n                            id=\"toggle-all\"\n                            checked={self.state.is_all_completed()}\n                            onclick={ctx.link().callback(|_| Msg::ToggleAll)}\n                        />\n                        <label for=\"toggle-all\" />\n                        <ul class=\"todo-list\">\n                            { for self.state.entries.iter().filter(|e| self.state.filter.fits(e)).enumerate().map(|e| self.view_entry(e, ctx.link())) }\n                        </ul>\n                    </section>\n                    <footer class={classes!(\"footer\", hidden_class)}>\n                        <span class=\"todo-count\">\n                            <strong>{ self.state.total() }</strong>\n                            { \" item(s) left\" }\n                        </span>\n                        <ul class=\"filters\">\n                            { for Filter::iter().map(|flt| self.view_filter(flt, ctx.link())) }\n                        </ul>\n                        <button class=\"clear-completed\" onclick={ctx.link().callback(|_| Msg::ClearCompleted)}>\n                            { format!(\"Clear completed ({})\", self.state.total_completed()) }\n                        </button>\n                    </footer>\n                </section>\n                <footer class=\"info\">\n                    <p>{ \"Double-click to edit a todo\" }</p>\n                    <p>{ \"Written by \" }<a href=\"https://github.com/DenisKolodin/\" target=\"_blank\">{ \"Denis Kolodin\" }</a></p>\n                    <p>{ \"Part of \" }<a href=\"http://todomvc.com/\" target=\"_blank\">{ \"TodoMVC\" }</a></p>\n                </footer>\n            </div>\n        }\n    }\n}\n\nimpl AppWith1000 {\n    fn view_filter(&self, filter: Filter, link: &Scope<Self>) -> Html {\n        let cls = if self.state.filter == filter {\n            \"selected\"\n        } else {\n            \"not-selected\"\n        };\n        html! {\n            <li>\n                <a class={cls}\n                   href={filter.as_href()}\n                   onclick={link.callback(move |_| Msg::SetFilter(filter))}\n                >\n                    { filter }\n                </a>\n            </li>\n        }\n    }\n\n    fn view_input(&self, link: &Scope<Self>) -> Html {\n        let onkeypress = link.batch_callback(|e: KeyboardEvent| {\n            if e.key() == \"Enter\" {\n                let input: InputElement = e.target_unchecked_into();\n                let value = input.value();\n                input.set_value(\"\");\n                Some(Msg::Add(value))\n            } else {\n                None\n            }\n        });\n        html! {\n            // You can use standard Rust comments. One line:\n            // <li></li>\n            <input\n                class=\"new-todo\"\n                placeholder=\"What needs to be done?\"\n                {onkeypress}\n            />\n            /* Or multiline:\n            <ul>\n                <li></li>\n            </ul>\n            */\n        }\n    }\n\n    fn view_entry(&self, (idx, entry): (usize, &Entry), link: &Scope<Self>) -> Html {\n        let mut class = Classes::from(\"todo\");\n        if entry.editing {\n            class.push(\" editing\");\n        }\n        if entry.completed {\n            class.push(\" completed\");\n        }\n        html! {\n            <li {class}>\n                <div class=\"view\">\n                    <input\n                        type=\"checkbox\"\n                        class=\"toggle\"\n                        checked={entry.completed}\n                        onclick={link.callback(move |_| Msg::Toggle(idx))}\n                    />\n                    <label ondblclick={link.callback(move |_| Msg::ToggleEdit(idx))}>{ &entry.description }</label>\n                    <button class=\"destroy\" onclick={link.callback(move |_| Msg::Remove(idx))} />\n                </div>\n                { self.view_entry_edit_input((idx, entry), link) }\n            </li>\n        }\n    }\n\n    fn view_entry_edit_input(&self, (idx, entry): (usize, &Entry), link: &Scope<Self>) -> Html {\n        let edit = move |input: InputElement| {\n            let value = input.value();\n            input.set_value(\"\");\n            Msg::Edit((idx, value))\n        };\n\n        let onblur = link.callback(move |e: FocusEvent| edit(e.target_unchecked_into()));\n\n        let onkeypress = link.batch_callback(move |e: KeyboardEvent| {\n            (e.key() == \"Enter\").then(|| edit(e.target_unchecked_into()))\n        });\n\n        if entry.editing {\n            html! {\n                <input\n                    class=\"edit\"\n                    type=\"text\"\n                    ref={self.focus_ref.clone()}\n                    value={self.state.edit_value.clone()}\n                    onmouseover={link.callback(|_| Msg::Focus)}\n                    {onblur}\n                    {onkeypress}\n                />\n            }\n        } else {\n            html! { <input type=\"hidden\" /> }\n        }\n    }\n}\n\nuse serde::{Deserialize, Serialize};\nuse strum_macros::{Display, EnumIter};\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct State {\n    pub entries: Vec<Entry>,\n    pub filter: Filter,\n    pub edit_value: String,\n}\n\nimpl State {\n    pub fn total(&self) -> usize {\n        self.entries.len()\n    }\n\n    pub fn total_completed(&self) -> usize {\n        self.entries\n            .iter()\n            .filter(|e| Filter::Completed.fits(e))\n            .count()\n    }\n\n    pub fn is_all_completed(&self) -> bool {\n        let mut filtered_iter = self\n            .entries\n            .iter()\n            .filter(|e| self.filter.fits(e))\n            .peekable();\n\n        if filtered_iter.peek().is_none() {\n            return false;\n        }\n\n        filtered_iter.all(|e| e.completed)\n    }\n\n    pub fn clear_completed(&mut self) {\n        let entries = self\n            .entries\n            .drain(..)\n            .filter(|e| Filter::Active.fits(e))\n            .collect();\n        self.entries = entries;\n    }\n\n    pub fn toggle(&mut self, idx: usize) {\n        let filter = self.filter;\n        let entry = self\n            .entries\n            .iter_mut()\n            .filter(|e| filter.fits(e))\n            .nth(idx)\n            .unwrap();\n        entry.completed = !entry.completed;\n    }\n\n    pub fn toggle_all(&mut self, value: bool) {\n        for entry in &mut self.entries {\n            if self.filter.fits(entry) {\n                entry.completed = value;\n            }\n        }\n    }\n\n    pub fn toggle_edit(&mut self, idx: usize) {\n        let filter = self.filter;\n        let entry = self\n            .entries\n            .iter_mut()\n            .filter(|e| filter.fits(e))\n            .nth(idx)\n            .unwrap();\n        entry.editing = !entry.editing;\n    }\n\n    pub fn clear_all_edit(&mut self) {\n        for entry in &mut self.entries {\n            entry.editing = false;\n        }\n    }\n\n    pub fn complete_edit(&mut self, idx: usize, val: String) {\n        if val.is_empty() {\n            self.remove(idx);\n        } else {\n            let filter = self.filter;\n            let entry = self\n                .entries\n                .iter_mut()\n                .filter(|e| filter.fits(e))\n                .nth(idx)\n                .unwrap();\n            entry.description = val;\n            entry.editing = !entry.editing;\n        }\n    }\n\n    pub fn remove(&mut self, idx: usize) {\n        let idx = {\n            let entries = self\n                .entries\n                .iter()\n                .enumerate()\n                .filter(|&(_, e)| self.filter.fits(e))\n                .collect::<Vec<_>>();\n            let &(idx, _) = entries.get(idx).unwrap();\n            idx\n        };\n        self.entries.remove(idx);\n    }\n}\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct Entry {\n    pub description: String,\n    pub completed: bool,\n    pub editing: bool,\n}\n\n#[derive(Clone, Copy, Debug, EnumIter, Display, PartialEq, Serialize, Deserialize, Eq)]\npub enum Filter {\n    All,\n    Active,\n    Completed,\n}\nimpl Filter {\n    pub fn fits(&self, entry: &Entry) -> bool {\n        match *self {\n            Filter::All => true,\n            Filter::Active => !entry.completed,\n            Filter::Completed => entry.completed,\n        }\n    }\n\n    pub fn as_href(&self) -> &'static str {\n        match self {\n            Filter::All => \"#/\",\n            Filter::Active => \"#/active\",\n            Filter::Completed => \"#/completed\",\n        }\n    }\n}\n"
  },
  {
    "path": "cargo-make/check-minimal-versions.toml",
    "content": "[tasks.check-minimal-versions]\ncondition = { channels = [\"nightly\"] }\ncommand = \"cargo\"\nargs = [\n  \"all-features\",\n  \"minimal-versions\",\n  \"check\",\n  \"--ignore-private\",\n  \"--detach-path-deps\",\n  \"--direct\",\n]\ninstall_script = '''\ncargo install cargo-all-features\n'''\n"
  },
  {
    "path": "cargo-make/lint.toml",
    "content": "[tasks.lint]\ndependencies = [\"check-format-flow\", \"clippy-each-feature\"]\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../\" }\nargs = [\"fmt\", \"--\", \"--check\", \"--config-path\", \"${LEPTOS_PROJECT_DIRECTORY}\"]\n\n[tasks.clippy-each-feature]\ncommand = \"cargo\"\nargs = [\n  \"all-features\",\n  \"clippy\",\n  \"--no-deps\",\n  \"--\",\n  \"-D\",\n  \"clippy::print_stdout\",\n]\ninstall_script = '''\ncargo install cargo-all-features\n'''\n"
  },
  {
    "path": "cargo-make/main.toml",
    "content": "extend = [\n  { path = \"./lint.toml\" },\n  { path = \"./test.toml\" },\n  { path = \"./check-minimal-versions.toml\" },\n]\n\n[env]\nRUSTFLAGS = \"\"\nLEPTOS_OUTPUT_NAME = \"ci\" # allows examples to check/build without cargo-leptos\n\n[env.github-actions]\nRUSTFLAGS = \"-D warnings\"\n\n[tasks.ci]\ndependencies = [\"lint\", \"test-each-feature\", \"doctests\"]\n"
  },
  {
    "path": "cargo-make/test.toml",
    "content": "[tasks.test-each-feature]\nenv = { \"NEXTEST_NO_TESTS\" = \"warn\" }\ncommand = \"cargo\"\nargs = [\"all-features\", \"nextest\", \"run\", \"--all-targets\"]\ninstall_script = '''\ncargo install cargo-all-features\n'''\n\n# This can be removed once doctests is supported in nextest\n# https://github.com/nextest-rs/nextest/issues/16\n[tasks.doctests]\ncommand = \"cargo\"\nargs = [\"all-features\", \"test\", \"--doc\"]\ninstall_script = '''\ncargo install cargo-all-features\n'''\n"
  },
  {
    "path": "cargo-make/wasm-test.toml",
    "content": "[tasks.post-test]\ndependencies = [\"test-wasm\"]\n\n[tasks.test-wasm]\nenv = { CARGO_MAKE_WASM_TEST_ARGS = \"--headless --chrome --features=wasm-bindgen\" }\ncommand = \"cargo\"\nargs = [\"make\", \"wasm-pack-test\"]\n"
  },
  {
    "path": "const_str_slice_concat/Cargo.toml",
    "content": "[package]\nname = \"const_str_slice_concat\"\nversion = \"0.1.0\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Utilities for const concatenation of string slices.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\n"
  },
  {
    "path": "const_str_slice_concat/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "const_str_slice_concat/src/lib.rs",
    "content": "#![no_std]\n#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\n//! Utilities for const concatenation of string slices.\n\npub(crate) const MAX_TEMPLATE_SIZE: usize = 4096;\n\n/// Converts a zero-terminated buffer of bytes into a UTF-8 string.\npub const fn str_from_buffer(buf: &[u8; MAX_TEMPLATE_SIZE]) -> &str {\n    match core::ffi::CStr::from_bytes_until_nul(buf) {\n        Ok(cstr) => match cstr.to_str() {\n            Ok(str) => str,\n            Err(_) => panic!(\"TEMPLATE FAILURE\"),\n        },\n        Err(_) => panic!(\"TEMPLATE FAILURE\"),\n    }\n}\n\n/// Concatenates any number of static strings into a single array.\n// credit to Rainer Stropek, \"Constant fun,\" Rust Linz, June 2022\npub const fn const_concat(\n    strs: &'static [&'static str],\n) -> [u8; MAX_TEMPLATE_SIZE] {\n    let mut buffer = [0; MAX_TEMPLATE_SIZE];\n    let mut position = 0;\n    let mut remaining = strs;\n\n    while let [current, tail @ ..] = remaining {\n        let x = current.as_bytes();\n        let mut i = 0;\n\n        // have it iterate over bytes manually, because, again,\n        // no mutable references in const fns\n        while i < x.len() {\n            buffer[position] = x[i];\n            position += 1;\n            i += 1;\n        }\n\n        remaining = tail;\n    }\n\n    buffer\n}\n\n/// Converts a zero-terminated buffer of bytes into a UTF-8 string with the given prefix.\npub const fn const_concat_with_prefix(\n    strs: &'static [&'static str],\n    prefix: &'static str,\n    suffix: &'static str,\n) -> [u8; MAX_TEMPLATE_SIZE] {\n    let mut buffer = [0; MAX_TEMPLATE_SIZE];\n    let mut position = 0;\n    let mut remaining = strs;\n\n    while let [current, tail @ ..] = remaining {\n        let x = current.as_bytes();\n        let mut i = 0;\n\n        // have it iterate over bytes manually, because, again,\n        // no mutable references in const fns\n        while i < x.len() {\n            buffer[position] = x[i];\n            position += 1;\n            i += 1;\n        }\n\n        remaining = tail;\n    }\n\n    if buffer[0] == 0 {\n        buffer\n    } else {\n        let mut new_buf = [0; MAX_TEMPLATE_SIZE];\n        let prefix = prefix.as_bytes();\n        let suffix = suffix.as_bytes();\n        let mut position = 0;\n        let mut i = 0;\n        while i < prefix.len() {\n            new_buf[position] = prefix[i];\n            position += 1;\n            i += 1;\n        }\n        i = 0;\n        while i < buffer.len() {\n            if buffer[i] == 0 {\n                break;\n            }\n            new_buf[position] = buffer[i];\n            position += 1;\n            i += 1;\n        }\n        i = 0;\n        while i < suffix.len() {\n            new_buf[position] = suffix[i];\n            position += 1;\n            i += 1;\n        }\n\n        new_buf\n    }\n}\n\n/// Converts any number of strings into a UTF-8 string, separated by the given string.\npub const fn const_concat_with_separator(\n    strs: &[&str],\n    separator: &'static str,\n) -> [u8; MAX_TEMPLATE_SIZE] {\n    let mut buffer = [0; MAX_TEMPLATE_SIZE];\n    let mut position = 0;\n    let mut remaining = strs;\n\n    while let [current, tail @ ..] = remaining {\n        let x = current.as_bytes();\n        let mut i = 0;\n\n        // have it iterate over bytes manually, because, again,\n        // no mutable references in const fns\n        while i < x.len() {\n            buffer[position] = x[i];\n            position += 1;\n            i += 1;\n        }\n        if !x.is_empty() {\n            let mut position = 0;\n            let separator = separator.as_bytes();\n            while i < separator.len() {\n                buffer[position] = separator[i];\n                position += 1;\n                i += 1;\n            }\n        }\n\n        remaining = tail;\n    }\n\n    buffer\n}\n"
  },
  {
    "path": "docs/COMMON_BUGS.md",
    "content": "# Leptos Gotchas: Common Bugs\n\nThis document is intended as a running list of common issues, with example code and solutions.\n\n## Reactivity\n\n### Avoid writing to a signal from an effect\n\n**Issue**: Sometimes you want to update a reactive signal in a way that depends on another signal.\n\n```rust\nlet (a, set_a) = signal(0);\nlet (b, set_b) = signal(false);\n\nEffect::new(move |_| {\n\tif a.get() > 5 {\n\t\tset_b.set(true);\n\t}\n});\n```\n\nThis creates an inefficient chain of updates, and can easily lead to infinite loops in more complex applications.\n\n**Solution**: Follow the rule, _What can be derived, should be derived._ In this case, this has the benefit of massively reducing the code size, too!\n\n```rust\nlet (a, set_a) = signal(0);\nlet b = move || a.get() > 5;\n```\n\n## Templates and the DOM\n\n### `<input value=...>` doesn't update or stops updating\n\nMany DOM attributes can be updated either by setting an attribute on the DOM node, or by setting an object property directly on it. In general, `setAttribute()` stops working once the property has been set.\n\nThis means that in practice, attributes like `value` or `checked` on an `<input/>` element only update the _default_ value for the `<input/>`. If you want to reactively update the value, you should use `prop:value` instead to set the `value` property.\n\n```rust\nlet (a, set_a) = signal(\"Starting value\".to_string());\nlet on_input = move |ev| set_a.set(event_target_value(&ev));\n\nview! {\n\n\t// ❌ reactivity doesn't work as expected: typing only updates the default\n\t//    of each input, so if you start typing in the second input, it won't\n\t//    update the first one\n\t<input value=a on:input=on_input />\n\t<input value=a on:input=on_input />\n}\n```\n\n```rust\nlet (a, set_a) = signal(\"Starting value\".to_string());\nlet on_input = move |ev| set_a.set(event_target_value(&ev));\n\nview! {\n\n\t// ✅ works as intended by setting the value *property*\n\t<input prop:value=a on:input=on_input />\n\t<input prop:value=a on:input=on_input />\n}\n```\n\n## Build configuration\n\n### Cargo feature resolution in workspaces\n\nA new [version](https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions) of Cargo's feature resolver was introduced for the 2021 edition of Rust.\nFor single crate projects it will select a resolver version based on the Rust edition in `Cargo.toml`. As there is no Rust edition present for `Cargo.toml` in a workspace, Cargo will default to the pre 2021 edition resolver.\nThis can cause issues resulting in non WASM compatible code being built for a WASM target. Seeing `mio` failing to build is often a sign that none WASM compatible code is being included in the build.\n\nThe resolver version can be set in the workspace `Cargo.toml` to remedy this issue.\n\n```toml\n[workspace]\nmembers = [\"member1\", \"member2\"]\nresolver = \"2\"\n```\n"
  },
  {
    "path": "docs/book_ru/.gitignore",
    "content": "book"
  },
  {
    "path": "docs/book_ru/README.md",
    "content": "Перевод в процессе, книга скоро будет доступна\n> Translation underway, book will be available soon"
  },
  {
    "path": "docs/book_ru/book.toml",
    "content": "[output.html]\nadditional-css = [\"./mdbook-admonish.css\"]\n[output.html.playground]\nrunnable = false\n\n[preprocessor]\n\n[preprocessor.admonish]\ncommand = \"mdbook-admonish\"\nassets_version = \"3.0.1\" # не редактировать: управляется `mdbook-admonish install`\n"
  },
  {
    "path": "docs/book_ru/mdbook-admonish.css",
    "content": "@charset \"UTF-8\";\n:root {\n  --md-admonition-icon--admonish-note: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>\");\n  --md-admonition-icon--admonish-abstract: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>\");\n  --md-admonition-icon--admonish-info: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>\");\n  --md-admonition-icon--admonish-tip: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>\");\n  --md-admonition-icon--admonish-success: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>\");\n  --md-admonition-icon--admonish-question: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>\");\n  --md-admonition-icon--admonish-warning: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>\");\n  --md-admonition-icon--admonish-failure: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>\");\n  --md-admonition-icon--admonish-danger: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>\");\n  --md-admonition-icon--admonish-bug: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>\");\n  --md-admonition-icon--admonish-example: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>\");\n  --md-admonition-icon--admonish-quote: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>\");\n  --md-details-icon: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>\");\n}\n\n:is(.admonition) {\n  display: flow-root;\n  margin: 1.5625em 0;\n  padding: 0 1.2rem;\n  color: var(--fg);\n  page-break-inside: avoid;\n  background-color: var(--bg);\n  border: 0 solid black;\n  border-inline-start-width: 0.4rem;\n  border-radius: 0.2rem;\n  box-shadow: 0 0.2rem 1rem rgba(0, 0, 0, 0.05), 0 0 0.1rem rgba(0, 0, 0, 0.1);\n}\n@media print {\n  :is(.admonition) {\n    box-shadow: none;\n  }\n}\n:is(.admonition) > * {\n  box-sizing: border-box;\n}\n:is(.admonition) :is(.admonition) {\n  margin-top: 1em;\n  margin-bottom: 1em;\n}\n:is(.admonition) > .tabbed-set:only-child {\n  margin-top: 0;\n}\nhtml :is(.admonition) > :last-child {\n  margin-bottom: 1.2rem;\n}\n\na.admonition-anchor-link {\n  display: none;\n  position: absolute;\n  left: -1.2rem;\n  padding-right: 1rem;\n}\na.admonition-anchor-link:link, a.admonition-anchor-link:visited {\n  color: var(--fg);\n}\na.admonition-anchor-link:link:hover, a.admonition-anchor-link:visited:hover {\n  text-decoration: none;\n}\na.admonition-anchor-link::before {\n  content: \"§\";\n}\n\n:is(.admonition-title, summary.admonition-title) {\n  position: relative;\n  min-height: 4rem;\n  margin-block: 0;\n  margin-inline: -1.6rem -1.2rem;\n  padding-block: 0.8rem;\n  padding-inline: 4.4rem 1.2rem;\n  font-weight: 700;\n  background-color: rgba(68, 138, 255, 0.1);\n  print-color-adjust: exact;\n  -webkit-print-color-adjust: exact;\n  display: flex;\n}\n:is(.admonition-title, summary.admonition-title) p {\n  margin: 0;\n}\nhtml :is(.admonition-title, summary.admonition-title):last-child {\n  margin-bottom: 0;\n}\n:is(.admonition-title, summary.admonition-title)::before {\n  position: absolute;\n  top: 0.625em;\n  inset-inline-start: 1.6rem;\n  width: 2rem;\n  height: 2rem;\n  background-color: #448aff;\n  print-color-adjust: exact;\n  -webkit-print-color-adjust: exact;\n  mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"></svg>');\n  -webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"></svg>');\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-size: contain;\n  content: \"\";\n}\n:is(.admonition-title, summary.admonition-title):hover a.admonition-anchor-link {\n  display: initial;\n}\n\ndetails.admonition > summary.admonition-title::after {\n  position: absolute;\n  top: 0.625em;\n  inset-inline-end: 1.6rem;\n  height: 2rem;\n  width: 2rem;\n  background-color: currentcolor;\n  mask-image: var(--md-details-icon);\n  -webkit-mask-image: var(--md-details-icon);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-size: contain;\n  content: \"\";\n  transform: rotate(0deg);\n  transition: transform 0.25s;\n}\ndetails[open].admonition > summary.admonition-title::after {\n  transform: rotate(90deg);\n}\n\n:is(.admonition):is(.admonish-note) {\n  border-color: #448aff;\n}\n\n:is(.admonish-note) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(68, 138, 255, 0.1);\n}\n:is(.admonish-note) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #448aff;\n  mask-image: var(--md-admonition-icon--admonish-note);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-note);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-abstract, .admonish-summary, .admonish-tldr) {\n  border-color: #00b0ff;\n}\n\n:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(0, 176, 255, 0.1);\n}\n:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #00b0ff;\n  mask-image: var(--md-admonition-icon--admonish-abstract);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-abstract);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-info, .admonish-todo) {\n  border-color: #00b8d4;\n}\n\n:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(0, 184, 212, 0.1);\n}\n:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #00b8d4;\n  mask-image: var(--md-admonition-icon--admonish-info);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-info);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-tip, .admonish-hint, .admonish-important) {\n  border-color: #00bfa5;\n}\n\n:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(0, 191, 165, 0.1);\n}\n:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #00bfa5;\n  mask-image: var(--md-admonition-icon--admonish-tip);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-tip);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-success, .admonish-check, .admonish-done) {\n  border-color: #00c853;\n}\n\n:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(0, 200, 83, 0.1);\n}\n:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #00c853;\n  mask-image: var(--md-admonition-icon--admonish-success);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-success);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-question, .admonish-help, .admonish-faq) {\n  border-color: #64dd17;\n}\n\n:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(100, 221, 23, 0.1);\n}\n:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #64dd17;\n  mask-image: var(--md-admonition-icon--admonish-question);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-question);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-warning, .admonish-caution, .admonish-attention) {\n  border-color: #ff9100;\n}\n\n:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(255, 145, 0, 0.1);\n}\n:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #ff9100;\n  mask-image: var(--md-admonition-icon--admonish-warning);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-warning);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-failure, .admonish-fail, .admonish-missing) {\n  border-color: #ff5252;\n}\n\n:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(255, 82, 82, 0.1);\n}\n:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #ff5252;\n  mask-image: var(--md-admonition-icon--admonish-failure);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-failure);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-danger, .admonish-error) {\n  border-color: #ff1744;\n}\n\n:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(255, 23, 68, 0.1);\n}\n:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #ff1744;\n  mask-image: var(--md-admonition-icon--admonish-danger);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-danger);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-bug) {\n  border-color: #f50057;\n}\n\n:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(245, 0, 87, 0.1);\n}\n:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #f50057;\n  mask-image: var(--md-admonition-icon--admonish-bug);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-bug);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-example) {\n  border-color: #7c4dff;\n}\n\n:is(.admonish-example) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(124, 77, 255, 0.1);\n}\n:is(.admonish-example) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #7c4dff;\n  mask-image: var(--md-admonition-icon--admonish-example);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-example);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n:is(.admonition):is(.admonish-quote, .admonish-cite) {\n  border-color: #9e9e9e;\n}\n\n:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title) {\n  background-color: rgba(158, 158, 158, 0.1);\n}\n:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title)::before {\n  background-color: #9e9e9e;\n  mask-image: var(--md-admonition-icon--admonish-quote);\n  -webkit-mask-image: var(--md-admonition-icon--admonish-quote);\n  mask-repeat: no-repeat;\n  -webkit-mask-repeat: no-repeat;\n  mask-size: contain;\n  -webkit-mask-repeat: no-repeat;\n}\n\n.navy :is(.admonition) {\n  background-color: var(--sidebar-bg);\n}\n\n.ayu :is(.admonition),\n.coal :is(.admonition) {\n  background-color: var(--theme-hover);\n}\n\n.rust :is(.admonition) {\n  background-color: var(--sidebar-bg);\n  color: var(--sidebar-fg);\n}\n.rust .admonition-anchor-link:link, .rust .admonition-anchor-link:visited {\n  color: var(--sidebar-fg);\n}\n"
  },
  {
    "path": "docs/book_ru/src/01_introduction.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/\">\n"
  },
  {
    "path": "docs/book_ru/src/15_global_state.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/15_global_state.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/15_global_state.html\">\n"
  },
  {
    "path": "docs/book_ru/src/SUMMARY.md",
    "content": "# Оглавление\n\n- [Вступление](./01_introduction.md)\n- [Начало работы](./getting_started/README.md)\n  - [Leptos DX](./getting_started/leptos_dx.md)\n  - [Сообщество Leptos и leptos-* Крейты](./getting_started/community_crates.md)\n- [Часть 1: Построение UI](./view/README.md)\n  - [Простой компонент](./view/01_basic_component.md)\n  - [Динамические атрибуты](./view/02_dynamic_attributes.md)\n  - [Компоненты и свойства](./view/03_components.md)\n  - [Итерирование](./view/04_iteration.md)\n  - [Итерирование более сложных структур через `<For>`](./view/04b_iteration.md)\n  - [Формы и поля ввода](./view/05_forms.md)\n  - [Порядок выполнения](./view/06_control_flow.md)\n  - [Обработка ошибок](./view/07_errors.md)\n  - [Общение Родитель-Ребёнок в дереве компонентов](./view/08_parent_child.md)\n  - [Передача Детей другим компонентам](./view/09_component_children.md)\n  - [Без макросов: синтаксис билдера View](./view/builder.md)\n- [Реактивность](./reactivity/README.md)\n  - [Работа с сигналами](./reactivity/working_with_signals.md)\n  - [Реагирование на изменения с помощью `create_effect`](./reactivity/14_create_effect.md)\n  - [Примечание: Реактивность и функции](./reactivity/interlude_functions.md)\n- [Тестирование](./testing.md)\n- [Асинхронность](./async/README.md)\n  - [Подгрузка данных с помощью ресурсов (Resource)](./async/10_resources.md)\n  - [Ожидания (Suspense)](./async/11_suspense.md)\n  - [Переходы (Transition)](./async/12_transition.md)\n  - [Действия (Action)](./async/13_actions.md)\n- [Примечание: Пробрасывание дочерних элементов](./interlude_projecting_children.md)\n- [Управление глобальным состоянием](./15_global_state.md)\n- [Маршрутизатор URL](./router/README.md)\n  - [Определение `<Routes/>`](./router/16_routes.md)\n  - [Вложенная маршрутизация](./router/17_nested_routing.md)\n  - [Параметры в пути и в строке запроса](./router/18_params_and_queries.md)\n  - [`<A/>`](./router/19_a.md)\n  - [`<Form/>`](./router/20_form.md)\n- [Примечание: Стили](./interlude_styling.md)\n- [Метаданные](./metadata.md)\n- [Рендеринг на стороне клиента (CSR): Заключение](./csr_wrapping_up.md)\n- [Часть 2: Рендеринг на стороне сервера (SSR)](./ssr/README.md)\n  - [`cargo-leptos`](./ssr/21_cargo_leptos.md)\n  - [Жизненный цикл загрузки страницы](./ssr/22_life_cycle.md)\n  - [Асинхронный рендеринг и режимы SSR](./ssr/23_ssr_modes.md)\n  - [Баги возникающие при гидратации](./ssr/24_hydration_bugs.md)\n- [Работа с сервером](./server/README.md)\n  - [Серверные функции](./server/25_server_functions.md)\n  - [Экстракторы](./server/26_extractors.md)\n  - [Ответы и перенаправления](./server/27_response.md)\n- [Постепенное улучшение и Изящная деградация](./progressive_enhancement/README.md)\n  - [`<ActionForm/>`](./progressive_enhancement/action_form.md)\n- [Развёртывание](./deployment/README.md)\n  - [Оптимизация размера бинарника WASM](./deployment/binary_size.md)\n- [Руководство: Острова](./islands.md)\n\n- [Приложение: Как работает реактивная система?](./appendix_reactive_graph.md)\n"
  },
  {
    "path": "docs/book_ru/src/appendix_reactive_graph.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/appendix_reactive_graph.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/appendix_reactive_graph.html\">\n"
  },
  {
    "path": "docs/book_ru/src/async/10_resources.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/async/10_resources.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/async/10_resources.html\">\n"
  },
  {
    "path": "docs/book_ru/src/async/11_suspense.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/async/11_suspense.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/async/11_suspense.html\">\n"
  },
  {
    "path": "docs/book_ru/src/async/12_transition.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/async/12_transition.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/async/12_transition.html\">\n"
  },
  {
    "path": "docs/book_ru/src/async/13_actions.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/async/13_action.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/async/13_action.html\">\n"
  },
  {
    "path": "docs/book_ru/src/async/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/async/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/async/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/csr_wrapping_up.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/csr_wrapping_up.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/csr_wrapping_up.html\">\n"
  },
  {
    "path": "docs/book_ru/src/deployment/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/deployment/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/deployment/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/deployment/binary_size.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/deployment/binary_size.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/deployment/binary_size.html\">\n"
  },
  {
    "path": "docs/book_ru/src/getting_started/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/getting_started/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/getting_started/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/getting_started/community_crates.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/getting_started/community_crates.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/getting_started/community_crates.html\">\n"
  },
  {
    "path": "docs/book_ru/src/getting_started/leptos_dx.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/getting_started/leptos_dx.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/getting_started/leptos_dx.html\">\n"
  },
  {
    "path": "docs/book_ru/src/interlude_projecting_children.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/interlude_projecting_children.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/interlude_projecting_children.html\">\n"
  },
  {
    "path": "docs/book_ru/src/interlude_styling.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/interlude_styling.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/interlude_styling.html\">\n"
  },
  {
    "path": "docs/book_ru/src/islands.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/islands.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/islands.html\">\n"
  },
  {
    "path": "docs/book_ru/src/metadata.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/metadata.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/metadata.html\">\n"
  },
  {
    "path": "docs/book_ru/src/progressive_enhancement/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/progressive_enhancement/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/progressive_enhancement/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/progressive_enhancement/action_form.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/progressive_enhancement/action_form.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/progressive_enhancement/action_form.html\">\n"
  },
  {
    "path": "docs/book_ru/src/reactivity/14_create_effect.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/reactivity/14_create_effect.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/reactivity/14_create_effect.html\">\n"
  },
  {
    "path": "docs/book_ru/src/reactivity/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/reactivity/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/reactivity/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/reactivity/interlude_functions.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/reactivity/interlude_functions.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/reactivity/interlude_functions.html\">\n"
  },
  {
    "path": "docs/book_ru/src/reactivity/working_with_signals.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/reactivity/working_with_signals.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/reactivity/working_with_signals.html\">\n"
  },
  {
    "path": "docs/book_ru/src/router/16_routes.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/router/16_routes.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/router/16_routes.html\">\n"
  },
  {
    "path": "docs/book_ru/src/router/17_nested_routing.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/router/17_nested_routing.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/router/17_nested_routing.html\">\n"
  },
  {
    "path": "docs/book_ru/src/router/18_params_and_queries.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/router/18_params_and_queries.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/router/18_params_and_queries.html\">\n"
  },
  {
    "path": "docs/book_ru/src/router/19_a.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/router/19_a.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/router/19_a.html\">\n"
  },
  {
    "path": "docs/book_ru/src/router/20_form.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/router/20_form.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/router/20_form.html\">\n"
  },
  {
    "path": "docs/book_ru/src/router/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/router/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/router/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/server/25_server_functions.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/server/25_server_functions.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/server/25_server_functions.html\">\n"
  },
  {
    "path": "docs/book_ru/src/server/26_extractors.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/server/26_extractors.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/server/26_extractors.html\">\n"
  },
  {
    "path": "docs/book_ru/src/server/27_response.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/server/27_response.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/server/27_response.html\">\n"
  },
  {
    "path": "docs/book_ru/src/server/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/server/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/server/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/ssr/21_cargo_leptos.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/ssr/21_cargo_leptos.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/ssr/21_cargo_leptos.html\">\n"
  },
  {
    "path": "docs/book_ru/src/ssr/22_life_cycle.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/ssr/22_life_cycle.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/ssr/22_life_cycle.html\">\n"
  },
  {
    "path": "docs/book_ru/src/ssr/23_ssr_modes.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/ssr/23_ssr_modes.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/ssr/23_ssr_modes.html\">\n"
  },
  {
    "path": "docs/book_ru/src/ssr/24_hydration_bugs.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/ssr/24_hydration_bugs.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/ssr/24_hydration_bugs.html\">\n"
  },
  {
    "path": "docs/book_ru/src/ssr/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/ssr/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/ssr/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/testing.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/testing.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/testing.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/01_basic_component.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/01_basic_component.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/01_basic_component.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/02_dynamic_attributes.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/02_dynamic_attributes.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/02_dynamic_attributes.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/03_components.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/03_components.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/03_components.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/04_iteration.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/04_iteration.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/04_iteration.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/04b_iteration.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/04b_iteration.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/04b_iteration.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/05_forms.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/05_forms.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/05_forms.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/06_control_flow.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/06_control_flow.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/06_control_flow.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/07_errors.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/07_errors.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/07_errors.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/08_parent_child.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/08_parent_child.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/08_parent_child.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/09_component_children.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/09_component_children.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/09_component_children.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/README.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/index.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/index.html\">\n"
  },
  {
    "path": "docs/book_ru/src/view/builder.md",
    "content": "<meta http-equiv=\"refresh\" content=\"0; URL=https://book.leptos.dev/view/builder.html\">\n<link rel=\"canonical\" href=\"https://book.leptos.dev/view/builder.html\">\n"
  },
  {
    "path": "docs/logos/.gitignore",
    "content": "logo-working.svg"
  },
  {
    "path": "either_of/Cargo.toml",
    "content": "[package]\nname = \"either_of\"\nversion = \"0.1.8\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Utilities for working with enumerated types that contain one of 2..n other types.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\npin-project-lite = { workspace = true, default-features = true }\npaste = { workspace = true, default-features = true }\n\n[features]\ndefault = [\"no_std\"]\nno_std = []\n"
  },
  {
    "path": "either_of/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "either_of/README.md",
    "content": "Utilities for working with enumerated types that contain one of `2..n` other types.\n"
  },
  {
    "path": "either_of/src/lib.rs",
    "content": "#![no_std]\n#![forbid(unsafe_code)]\n\n//! Utilities for working with enumerated types that contain one of `2..n` other types.\n\nuse core::{\n    cmp::Ordering,\n    error::Error,\n    fmt::Display,\n    future::Future,\n    iter::{Product, Sum},\n    pin::Pin,\n    task::{Context, Poll},\n};\nuse paste::paste;\nuse pin_project_lite::pin_project;\n\nmacro_rules! tuples {\n    ($name:ident + $fut_name:ident + $fut_proj:ident {\n        $($ty:ident => ($($rest_variant:ident),*) + <$($mapped_ty:ident),+>),+$(,)?\n    }) => {\n        tuples!($name + $fut_name + $fut_proj {\n            $($ty($ty) => ($($rest_variant),*) + <$($mapped_ty),+>),+\n        });\n    };\n    ($name:ident + $fut_name:ident + $fut_proj:ident {\n        $($variant:ident($ty:ident) => ($($rest_variant:ident),*) + <$($mapped_ty:ident),+>),+$(,)?\n    }) => {\n        #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]\n        pub enum $name<$($ty),+> {\n            $($variant ($ty),)+\n        }\n\n        impl<$($ty),+> $name<$($ty),+> {\n            paste! {\n                #[allow(clippy::too_many_arguments)]\n                pub fn map<$([<F $ty>]),+, $([<$ty 1>]),+>(self, $([<$variant:lower>]: [<F $ty>]),+) -> $name<$([<$ty 1>]),+>\n                where\n                    $([<F $ty>]: FnOnce($ty) -> [<$ty 1>],)+\n                {\n                    match self {\n                        $($name::$variant(inner) => $name::$variant([<$variant:lower>](inner)),)+\n                    }\n                }\n\n                $(\n                    pub fn [<map_ $variant:lower>]<Fun, [<$ty 1>]>(self, f: Fun) -> $name<$($mapped_ty),+>\n                    where\n                        Fun: FnOnce($ty) -> [<$ty 1>],\n                    {\n                        match self {\n                            $name::$variant(inner) => $name::$variant(f(inner)),\n                            $($name::$rest_variant(inner) => $name::$rest_variant(inner),)*\n                        }\n                    }\n\n                    pub fn [<inspect_ $variant:lower>]<Fun, [<$ty 1>]>(self, f: Fun) -> Self\n                    where\n                        Fun: FnOnce(&$ty),\n                    {\n                        if let $name::$variant(inner) = &self {\n                            f(inner);\n                        }\n                        self\n                    }\n\n                    pub fn [<is_ $variant:lower>](&self) -> bool {\n                        matches!(self, $name::$variant(_))\n                    }\n\n                    pub fn [<as_ $variant:lower>](&self) -> Option<&$ty> {\n                        match self {\n                            $name::$variant(inner) => Some(inner),\n                            _ => None,\n                        }\n                    }\n\n                    pub fn [<as_ $variant:lower _mut>](&mut self) -> Option<&mut $ty> {\n                        match self {\n                            $name::$variant(inner) => Some(inner),\n                            _ => None,\n                        }\n                    }\n\n                    pub fn [<unwrap_ $variant:lower>](self) -> $ty {\n                        match self {\n                            $name::$variant(inner) => inner,\n                            _ => panic!(concat!(\n                                \"called `unwrap_\", stringify!([<$variant:lower>]), \"()` on a non-`\", stringify!($variant), \"` variant of `\", stringify!($name), \"`\"\n                            )),\n                        }\n                    }\n\n                    pub fn [<into_ $variant:lower>](self) -> Result<$ty, Self> {\n                        match self {\n                            $name::$variant(inner) => Ok(inner),\n                            _ => Err(self),\n                        }\n                    }\n                )+\n            }\n        }\n\n        impl<$($ty),+> Display for $name<$($ty),+>\n        where\n            $($ty: Display,)+\n        {\n            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n                match self {\n                    $($name::$variant(this) => this.fmt(f),)+\n                }\n            }\n        }\n\n        impl<$($ty),+> Error for $name<$($ty),+>\n        where\n            $($ty: Error,)+\n        {\n            fn source(&self) -> Option<&(dyn Error + 'static)> {\n                match self {\n                    $($name::$variant(this) => this.source(),)+\n                }\n            }\n        }\n\n        impl<Item, $($ty),+> Iterator for $name<$($ty),+>\n        where\n            $($ty: Iterator<Item = Item>,)+\n        {\n            type Item = Item;\n\n            fn next(&mut self) -> Option<Self::Item> {\n                match self {\n                    $($name::$variant(i) => i.next(),)+\n                }\n            }\n\n            fn size_hint(&self) -> (usize, Option<usize>) {\n                match self {\n                    $($name::$variant(i) => i.size_hint(),)+\n                }\n            }\n\n            fn count(self) -> usize\n            where\n                Self: Sized,\n            {\n                match self {\n                    $($name::$variant(i) => i.count(),)+\n                }\n            }\n\n            fn last(self) -> Option<Self::Item>\n            where\n                Self: Sized,\n            {\n                match self {\n                    $($name::$variant(i) => i.last(),)+\n                }\n            }\n\n            fn nth(&mut self, n: usize) -> Option<Self::Item> {\n                match self {\n                    $($name::$variant(i) => i.nth(n),)+\n                }\n            }\n\n            fn for_each<Fun>(self, f: Fun)\n            where\n                Self: Sized,\n                Fun: FnMut(Self::Item),\n            {\n                match self {\n                    $($name::$variant(i) => i.for_each(f),)+\n                }\n            }\n\n            fn collect<Col: FromIterator<Self::Item>>(self) -> Col\n            where\n                Self: Sized,\n            {\n                match self {\n                    $($name::$variant(i) => i.collect(),)+\n                }\n            }\n\n            fn partition<Col, Fun>(self, f: Fun) -> (Col, Col)\n            where\n                Self: Sized,\n                Col: Default + Extend<Self::Item>,\n                Fun: FnMut(&Self::Item) -> bool,\n            {\n                match self {\n                    $($name::$variant(i) => i.partition(f),)+\n                }\n            }\n\n            fn fold<Acc, Fun>(self, init: Acc, f: Fun) -> Acc\n            where\n                Self: Sized,\n                Fun: FnMut(Acc, Self::Item) -> Acc,\n            {\n                match self {\n                    $($name::$variant(i) => i.fold(init, f),)+\n                }\n            }\n\n            fn reduce<Fun>(self, f: Fun) -> Option<Self::Item>\n            where\n                Self: Sized,\n                Fun: FnMut(Self::Item, Self::Item) -> Self::Item,\n            {\n                match self {\n                    $($name::$variant(i) => i.reduce(f),)+\n                }\n            }\n\n            fn all<Fun>(&mut self, f: Fun) -> bool\n            where\n                Self: Sized,\n                Fun: FnMut(Self::Item) -> bool,\n            {\n                match self {\n                    $($name::$variant(i) => i.all(f),)+\n                }\n            }\n\n            fn any<Fun>(&mut self, f: Fun) -> bool\n            where\n                Self: Sized,\n                Fun: FnMut(Self::Item) -> bool,\n            {\n                match self {\n                    $($name::$variant(i) => i.any(f),)+\n                }\n            }\n\n            fn find<Pre>(&mut self, predicate: Pre) -> Option<Self::Item>\n            where\n                Self: Sized,\n                Pre: FnMut(&Self::Item) -> bool,\n            {\n                match self {\n                    $($name::$variant(i) => i.find(predicate),)+\n                }\n            }\n\n            fn find_map<Out, Fun>(&mut self, f: Fun) -> Option<Out>\n            where\n                Self: Sized,\n                Fun: FnMut(Self::Item) -> Option<Out>,\n            {\n                match self {\n                    $($name::$variant(i) => i.find_map(f),)+\n                }\n            }\n\n            fn position<Pre>(&mut self, predicate: Pre) -> Option<usize>\n            where\n                Self: Sized,\n                Pre: FnMut(Self::Item) -> bool,\n            {\n                match self {\n                    $($name::$variant(i) => i.position(predicate),)+\n                }\n            }\n\n            fn max(self) -> Option<Self::Item>\n            where\n                Self: Sized,\n                Self::Item: Ord,\n            {\n                match self {\n                    $($name::$variant(i) => i.max(),)+\n                }\n            }\n\n            fn min(self) -> Option<Self::Item>\n            where\n                Self: Sized,\n                Self::Item: Ord,\n            {\n                match self {\n                    $($name::$variant(i) => i.min(),)+\n                }\n            }\n\n            fn max_by_key<Key: Ord, Fun>(self, f: Fun) -> Option<Self::Item>\n            where\n                Self: Sized,\n                Fun: FnMut(&Self::Item) -> Key,\n            {\n                match self {\n                    $($name::$variant(i) => i.max_by_key(f),)+\n                }\n            }\n\n            fn max_by<Cmp>(self, compare: Cmp) -> Option<Self::Item>\n            where\n                Self: Sized,\n                Cmp: FnMut(&Self::Item, &Self::Item) -> Ordering,\n            {\n                match self {\n                    $($name::$variant(i) => i.max_by(compare),)+\n                }\n            }\n\n            fn min_by_key<Key: Ord, Fun>(self, f: Fun) -> Option<Self::Item>\n            where\n                Self: Sized,\n                Fun: FnMut(&Self::Item) -> Key,\n            {\n                match self {\n                    $($name::$variant(i) => i.min_by_key(f),)+\n                }\n            }\n\n            fn min_by<Cmp>(self, compare: Cmp) -> Option<Self::Item>\n            where\n                Self: Sized,\n                Cmp: FnMut(&Self::Item, &Self::Item) -> Ordering,\n            {\n                match self {\n                    $($name::$variant(i) => i.min_by(compare),)+\n                }\n            }\n\n            fn sum<Out>(self) -> Out\n            where\n                Self: Sized,\n                Out: Sum<Self::Item>,\n            {\n                match self {\n                    $($name::$variant(i) => i.sum(),)+\n                }\n            }\n\n            fn product<Out>(self) -> Out\n            where\n                Self: Sized,\n                Out: Product<Self::Item>,\n            {\n                match self {\n                    $($name::$variant(i) => i.product(),)+\n                }\n            }\n\n            fn cmp<Other>(self, other: Other) -> Ordering\n            where\n                Other: IntoIterator<Item = Self::Item>,\n                Self::Item: Ord,\n                Self: Sized,\n            {\n                match self {\n                    $($name::$variant(i) => i.cmp(other),)+\n                }\n            }\n\n            fn partial_cmp<Other>(self, other: Other) -> Option<Ordering>\n            where\n                Other: IntoIterator,\n                Self::Item: PartialOrd<Other::Item>,\n                Self: Sized,\n            {\n                match self {\n                    $($name::$variant(i) => i.partial_cmp(other),)+\n                }\n            }\n\n            // TODO: uncomment once MSRV is >= 1.82.0\n            // fn is_sorted(self) -> bool\n            // where\n            //     Self: Sized,\n            //     Self::Item: PartialOrd,\n            // {\n            //     match self {\n            //         $($name::$variant(i) => i.is_sorted(),)+\n            //     }\n            // }\n            //\n            // fn is_sorted_by<Cmp>(self, compare: Cmp) -> bool\n            // where\n            //     Self: Sized,\n            //     Cmp: FnMut(&Self::Item, &Self::Item) -> bool,\n            // {\n            //     match self {\n            //         $($name::$variant(i) => i.is_sorted_by(compare),)+\n            //     }\n            // }\n            //\n            // fn is_sorted_by_key<Fun, Key>(self, f: Fun) -> bool\n            // where\n            //     Self: Sized,\n            //     Fun: FnMut(Self::Item) -> Key,\n            //     Key: PartialOrd,\n            // {\n            //     match self {\n            //         $($name::$variant(i) => i.is_sorted_by_key(f),)+\n            //     }\n            // }\n        }\n\n        impl<Item, $($ty),+> ExactSizeIterator for $name<$($ty),+>\n        where\n            $($ty: ExactSizeIterator<Item = Item>,)+\n        {\n            fn len(&self) -> usize {\n                match self {\n                    $($name::$variant(i) => i.len(),)+\n                }\n            }\n        }\n\n        impl<Item, $($ty),+> DoubleEndedIterator for $name<$($ty),+>\n        where\n            $($ty: DoubleEndedIterator<Item = Item>,)+\n        {\n            fn next_back(&mut self) -> Option<Self::Item> {\n                match self {\n                    $($name::$variant(i) => i.next_back(),)+\n                }\n            }\n\n            fn nth_back(&mut self, n: usize) -> Option<Self::Item> {\n                match self {\n                    $($name::$variant(i) => i.nth_back(n),)+\n                }\n            }\n\n            fn rfind<Pre>(&mut self, predicate: Pre) -> Option<Self::Item>\n            where\n                Pre: FnMut(&Self::Item) -> bool,\n            {\n                match self {\n                    $($name::$variant(i) => i.rfind(predicate),)+\n                }\n            }\n        }\n\n        pin_project! {\n            #[project = $fut_proj]\n            pub enum $fut_name<$($ty),+> {\n                $($variant { #[pin] inner: $ty },)+\n            }\n        }\n\n        impl<$($ty),+> Future for $fut_name<$($ty),+>\n        where\n            $($ty: Future,)+\n        {\n            type Output = $name<$($ty::Output),+>;\n\n            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n                let this = self.project();\n                match this {\n                    $($fut_proj::$variant { inner } => match inner.poll(cx) {\n                        Poll::Pending => Poll::Pending,\n                        Poll::Ready(inner) => Poll::Ready($name::$variant(inner)),\n                    },)+\n                }\n            }\n        }\n    }\n}\n\ntuples!(Either + EitherFuture + EitherFutureProj {\n    Left(A) => (Right) + <A1, B>,\n    Right(B) => (Left) + <A, B1>,\n});\n\nimpl<A, B> Either<A, B> {\n    pub fn swap(self) -> Either<B, A> {\n        match self {\n            Either::Left(a) => Either::Right(a),\n            Either::Right(b) => Either::Left(b),\n        }\n    }\n}\n\nimpl<A, B> From<Result<A, B>> for Either<A, B> {\n    fn from(value: Result<A, B>) -> Self {\n        match value {\n            Ok(left) => Either::Left(left),\n            Err(right) => Either::Right(right),\n        }\n    }\n}\n\npub trait EitherOr {\n    type Left;\n    type Right;\n    fn either_or<FA, A, FB, B>(self, a: FA, b: FB) -> Either<A, B>\n    where\n        FA: FnOnce(Self::Left) -> A,\n        FB: FnOnce(Self::Right) -> B;\n}\n\nimpl EitherOr for bool {\n    type Left = ();\n    type Right = ();\n\n    fn either_or<FA, A, FB, B>(self, a: FA, b: FB) -> Either<A, B>\n    where\n        FA: FnOnce(Self::Left) -> A,\n        FB: FnOnce(Self::Right) -> B,\n    {\n        if self {\n            Either::Left(a(()))\n        } else {\n            Either::Right(b(()))\n        }\n    }\n}\n\nimpl<T> EitherOr for Option<T> {\n    type Left = T;\n    type Right = ();\n\n    fn either_or<FA, A, FB, B>(self, a: FA, b: FB) -> Either<A, B>\n    where\n        FA: FnOnce(Self::Left) -> A,\n        FB: FnOnce(Self::Right) -> B,\n    {\n        match self {\n            Some(t) => Either::Left(a(t)),\n            None => Either::Right(b(())),\n        }\n    }\n}\n\nimpl<T, E> EitherOr for Result<T, E> {\n    type Left = T;\n    type Right = E;\n\n    fn either_or<FA, A, FB, B>(self, a: FA, b: FB) -> Either<A, B>\n    where\n        FA: FnOnce(Self::Left) -> A,\n        FB: FnOnce(Self::Right) -> B,\n    {\n        match self {\n            Ok(t) => Either::Left(a(t)),\n            Err(err) => Either::Right(b(err)),\n        }\n    }\n}\n\nimpl<A, B> EitherOr for Either<A, B> {\n    type Left = A;\n    type Right = B;\n\n    #[inline]\n    fn either_or<FA, A1, FB, B1>(self, a: FA, b: FB) -> Either<A1, B1>\n    where\n        FA: FnOnce(<Self as EitherOr>::Left) -> A1,\n        FB: FnOnce(<Self as EitherOr>::Right) -> B1,\n    {\n        self.map(a, b)\n    }\n}\n\n#[test]\nfn test_either_or() {\n    let right = false.either_or(|_| 'a', |_| 12);\n    assert!(matches!(right, Either::Right(12)));\n\n    let left = true.either_or(|_| 'a', |_| 12);\n    assert!(matches!(left, Either::Left('a')));\n\n    let left = Some(12).either_or(|a| a, |_| 'a');\n    assert!(matches!(left, Either::Left(12)));\n    let right = None.either_or(|a: i32| a, |_| 'a');\n    assert!(matches!(right, Either::Right('a')));\n\n    let result: Result<_, ()> = Ok(1.2f32);\n    let left = result.either_or(|a| a * 2f32, |b| b);\n    assert!(matches!(left, Either::Left(2.4f32)));\n\n    let result: Result<i32, _> = Err(\"12\");\n    let right = result.either_or(|a| a, |b| b.chars().next());\n    assert!(matches!(right, Either::Right(Some('1'))));\n\n    let either = Either::<i32, char>::Left(12);\n    let left = either.either_or(|a| a, |b| b);\n    assert!(matches!(left, Either::Left(12)));\n\n    let either = Either::<i32, char>::Right('a');\n    let right = either.either_or(|a| a, |b| b);\n    assert!(matches!(right, Either::Right('a')));\n}\n\ntuples!(EitherOf3 + EitherOf3Future + EitherOf3FutureProj {\n    A => (B, C) + <A1, B, C>,\n    B => (A, C) + <A, B1, C>,\n    C => (A, B) + <A, B, C1>,\n});\ntuples!(EitherOf4 + EitherOf4Future + EitherOf4FutureProj {\n    A => (B, C, D) + <A1, B, C, D>,\n    B => (A, C, D) + <A, B1, C, D>,\n    C => (A, B, D) + <A, B, C1, D>,\n    D => (A, B, C) + <A, B, C, D1>,\n});\ntuples!(EitherOf5 + EitherOf5Future + EitherOf5FutureProj {\n    A => (B, C, D, E) + <A1, B, C, D, E>,\n    B => (A, C, D, E) + <A, B1, C, D, E>,\n    C => (A, B, D, E) + <A, B, C1, D, E>,\n    D => (A, B, C, E) + <A, B, C, D1, E>,\n    E => (A, B, C, D) + <A, B, C, D, E1>,\n});\ntuples!(EitherOf6 + EitherOf6Future + EitherOf6FutureProj {\n    A => (B, C, D, E, F) + <A1, B, C, D, E, F>,\n    B => (A, C, D, E, F) + <A, B1, C, D, E, F>,\n    C => (A, B, D, E, F) + <A, B, C1, D, E, F>,\n    D => (A, B, C, E, F) + <A, B, C, D1, E, F>,\n    E => (A, B, C, D, F) + <A, B, C, D, E1, F>,\n    F => (A, B, C, D, E) + <A, B, C, D, E, F1>,\n});\ntuples!(EitherOf7 + EitherOf7Future + EitherOf7FutureProj {\n    A => (B, C, D, E, F, G) + <A1, B, C, D, E, F, G>,\n    B => (A, C, D, E, F, G) + <A, B1, C, D, E, F, G>,\n    C => (A, B, D, E, F, G) + <A, B, C1, D, E, F, G>,\n    D => (A, B, C, E, F, G) + <A, B, C, D1, E, F, G>,\n    E => (A, B, C, D, F, G) + <A, B, C, D, E1, F, G>,\n    F => (A, B, C, D, E, G) + <A, B, C, D, E, F1, G>,\n    G => (A, B, C, D, E, F) + <A, B, C, D, E, F, G1>,\n});\ntuples!(EitherOf8 + EitherOf8Future + EitherOf8FutureProj {\n    A => (B, C, D, E, F, G, H) + <A1, B, C, D, E, F, G, H>,\n    B => (A, C, D, E, F, G, H) + <A, B1, C, D, E, F, G, H>,\n    C => (A, B, D, E, F, G, H) + <A, B, C1, D, E, F, G, H>,\n    D => (A, B, C, E, F, G, H) + <A, B, C, D1, E, F, G, H>,\n    E => (A, B, C, D, F, G, H) + <A, B, C, D, E1, F, G, H>,\n    F => (A, B, C, D, E, G, H) + <A, B, C, D, E, F1, G, H>,\n    G => (A, B, C, D, E, F, H) + <A, B, C, D, E, F, G1, H>,\n    H => (A, B, C, D, E, F, G) + <A, B, C, D, E, F, G, H1>,\n});\ntuples!(EitherOf9 + EitherOf9Future + EitherOf9FutureProj {\n    A => (B, C, D, E, F, G, H, I) + <A1, B, C, D, E, F, G, H, I>,\n    B => (A, C, D, E, F, G, H, I) + <A, B1, C, D, E, F, G, H, I>,\n    C => (A, B, D, E, F, G, H, I) + <A, B, C1, D, E, F, G, H, I>,\n    D => (A, B, C, E, F, G, H, I) + <A, B, C, D1, E, F, G, H, I>,\n    E => (A, B, C, D, F, G, H, I) + <A, B, C, D, E1, F, G, H, I>,\n    F => (A, B, C, D, E, G, H, I) + <A, B, C, D, E, F1, G, H, I>,\n    G => (A, B, C, D, E, F, H, I) + <A, B, C, D, E, F, G1, H, I>,\n    H => (A, B, C, D, E, F, G, I) + <A, B, C, D, E, F, G, H1, I>,\n    I => (A, B, C, D, E, F, G, H) + <A, B, C, D, E, F, G, H, I1>,\n});\ntuples!(EitherOf10 + EitherOf10Future + EitherOf10FutureProj {\n    A => (B, C, D, E, F, G, H, I, J) + <A1, B, C, D, E, F, G, H, I, J>,\n    B => (A, C, D, E, F, G, H, I, J) + <A, B1, C, D, E, F, G, H, I, J>,\n    C => (A, B, D, E, F, G, H, I, J) + <A, B, C1, D, E, F, G, H, I, J>,\n    D => (A, B, C, E, F, G, H, I, J) + <A, B, C, D1, E, F, G, H, I, J>,\n    E => (A, B, C, D, F, G, H, I, J) + <A, B, C, D, E1, F, G, H, I, J>,\n    F => (A, B, C, D, E, G, H, I, J) + <A, B, C, D, E, F1, G, H, I, J>,\n    G => (A, B, C, D, E, F, H, I, J) + <A, B, C, D, E, F, G1, H, I, J>,\n    H => (A, B, C, D, E, F, G, I, J) + <A, B, C, D, E, F, G, H1, I, J>,\n    I => (A, B, C, D, E, F, G, H, J) + <A, B, C, D, E, F, G, H, I1, J>,\n    J => (A, B, C, D, E, F, G, H, I) + <A, B, C, D, E, F, G, H, I, J1>,\n});\ntuples!(EitherOf11 + EitherOf11Future + EitherOf11FutureProj {\n    A => (B, C, D, E, F, G, H, I, J, K) + <A1, B, C, D, E, F, G, H, I, J, K>,\n    B => (A, C, D, E, F, G, H, I, J, K) + <A, B1, C, D, E, F, G, H, I, J, K>,\n    C => (A, B, D, E, F, G, H, I, J, K) + <A, B, C1, D, E, F, G, H, I, J, K>,\n    D => (A, B, C, E, F, G, H, I, J, K) + <A, B, C, D1, E, F, G, H, I, J, K>,\n    E => (A, B, C, D, F, G, H, I, J, K) + <A, B, C, D, E1, F, G, H, I, J, K>,\n    F => (A, B, C, D, E, G, H, I, J, K) + <A, B, C, D, E, F1, G, H, I, J, K>,\n    G => (A, B, C, D, E, F, H, I, J, K) + <A, B, C, D, E, F, G1, H, I, J, K>,\n    H => (A, B, C, D, E, F, G, I, J, K) + <A, B, C, D, E, F, G, H1, I, J, K>,\n    I => (A, B, C, D, E, F, G, H, J, K) + <A, B, C, D, E, F, G, H, I1, J, K>,\n    J => (A, B, C, D, E, F, G, H, I, K) + <A, B, C, D, E, F, G, H, I, J1, K>,\n    K => (A, B, C, D, E, F, G, H, I, J) + <A, B, C, D, E, F, G, H, I, J, K1>,\n});\ntuples!(EitherOf12 + EitherOf12Future + EitherOf12FutureProj {\n    A => (B, C, D, E, F, G, H, I, J, K, L) + <A1, B, C, D, E, F, G, H, I, J, K, L>,\n    B => (A, C, D, E, F, G, H, I, J, K, L) + <A, B1, C, D, E, F, G, H, I, J, K, L>,\n    C => (A, B, D, E, F, G, H, I, J, K, L) + <A, B, C1, D, E, F, G, H, I, J, K, L>,\n    D => (A, B, C, E, F, G, H, I, J, K, L) + <A, B, C, D1, E, F, G, H, I, J, K, L>,\n    E => (A, B, C, D, F, G, H, I, J, K, L) + <A, B, C, D, E1, F, G, H, I, J, K, L>,\n    F => (A, B, C, D, E, G, H, I, J, K, L) + <A, B, C, D, E, F1, G, H, I, J, K, L>,\n    G => (A, B, C, D, E, F, H, I, J, K, L) + <A, B, C, D, E, F, G1, H, I, J, K, L>,\n    H => (A, B, C, D, E, F, G, I, J, K, L) + <A, B, C, D, E, F, G, H1, I, J, K, L>,\n    I => (A, B, C, D, E, F, G, H, J, K, L) + <A, B, C, D, E, F, G, H, I1, J, K, L>,\n    J => (A, B, C, D, E, F, G, H, I, K, L) + <A, B, C, D, E, F, G, H, I, J1, K, L>,\n    K => (A, B, C, D, E, F, G, H, I, J, L) + <A, B, C, D, E, F, G, H, I, J, K1, L>,\n    L => (A, B, C, D, E, F, G, H, I, J, K) + <A, B, C, D, E, F, G, H, I, J, K, L1>,\n});\ntuples!(EitherOf13 + EitherOf13Future + EitherOf13FutureProj {\n    A => (B, C, D, E, F, G, H, I, J, K, L, M) + <A1, B, C, D, E, F, G, H, I, J, K, L, M>,\n    B => (A, C, D, E, F, G, H, I, J, K, L, M) + <A, B1, C, D, E, F, G, H, I, J, K, L, M>,\n    C => (A, B, D, E, F, G, H, I, J, K, L, M) + <A, B, C1, D, E, F, G, H, I, J, K, L, M>,\n    D => (A, B, C, E, F, G, H, I, J, K, L, M) + <A, B, C, D1, E, F, G, H, I, J, K, L, M>,\n    E => (A, B, C, D, F, G, H, I, J, K, L, M) + <A, B, C, D, E1, F, G, H, I, J, K, L, M>,\n    F => (A, B, C, D, E, G, H, I, J, K, L, M) + <A, B, C, D, E, F1, G, H, I, J, K, L, M>,\n    G => (A, B, C, D, E, F, H, I, J, K, L, M) + <A, B, C, D, E, F, G1, H, I, J, K, L, M>,\n    H => (A, B, C, D, E, F, G, I, J, K, L, M) + <A, B, C, D, E, F, G, H1, I, J, K, L, M>,\n    I => (A, B, C, D, E, F, G, H, J, K, L, M) + <A, B, C, D, E, F, G, H, I1, J, K, L, M>,\n    J => (A, B, C, D, E, F, G, H, I, K, L, M) + <A, B, C, D, E, F, G, H, I, J1, K, L, M>,\n    K => (A, B, C, D, E, F, G, H, I, J, L, M) + <A, B, C, D, E, F, G, H, I, J, K1, L, M>,\n    L => (A, B, C, D, E, F, G, H, I, J, K, M) + <A, B, C, D, E, F, G, H, I, J, K, L1, M>,\n    M => (A, B, C, D, E, F, G, H, I, J, K, L) + <A, B, C, D, E, F, G, H, I, J, K, L, M1>,\n});\ntuples!(EitherOf14 + EitherOf14Future + EitherOf14FutureProj {\n    A => (B, C, D, E, F, G, H, I, J, K, L, M, N) + <A1, B, C, D, E, F, G, H, I, J, K, L, M, N>,\n    B => (A, C, D, E, F, G, H, I, J, K, L, M, N) + <A, B1, C, D, E, F, G, H, I, J, K, L, M, N>,\n    C => (A, B, D, E, F, G, H, I, J, K, L, M, N) + <A, B, C1, D, E, F, G, H, I, J, K, L, M, N>,\n    D => (A, B, C, E, F, G, H, I, J, K, L, M, N) + <A, B, C, D1, E, F, G, H, I, J, K, L, M, N>,\n    E => (A, B, C, D, F, G, H, I, J, K, L, M, N) + <A, B, C, D, E1, F, G, H, I, J, K, L, M, N>,\n    F => (A, B, C, D, E, G, H, I, J, K, L, M, N) + <A, B, C, D, E, F1, G, H, I, J, K, L, M, N>,\n    G => (A, B, C, D, E, F, H, I, J, K, L, M, N) + <A, B, C, D, E, F, G1, H, I, J, K, L, M, N>,\n    H => (A, B, C, D, E, F, G, I, J, K, L, M, N) + <A, B, C, D, E, F, G, H1, I, J, K, L, M, N>,\n    I => (A, B, C, D, E, F, G, H, J, K, L, M, N) + <A, B, C, D, E, F, G, H, I1, J, K, L, M, N>,\n    J => (A, B, C, D, E, F, G, H, I, K, L, M, N) + <A, B, C, D, E, F, G, H, I, J1, K, L, M, N>,\n    K => (A, B, C, D, E, F, G, H, I, J, L, M, N) + <A, B, C, D, E, F, G, H, I, J, K1, L, M, N>,\n    L => (A, B, C, D, E, F, G, H, I, J, K, M, N) + <A, B, C, D, E, F, G, H, I, J, K, L1, M, N>,\n    M => (A, B, C, D, E, F, G, H, I, J, K, L, N) + <A, B, C, D, E, F, G, H, I, J, K, L, M1, N>,\n    N => (A, B, C, D, E, F, G, H, I, J, K, L, M) + <A, B, C, D, E, F, G, H, I, J, K, L, M, N1>,\n});\ntuples!(EitherOf15 + EitherOf15Future + EitherOf15FutureProj {\n    A => (B, C, D, E, F, G, H, I, J, K, L, M, N, O) + <A1, B, C, D, E, F, G, H, I, J, K, L, M, N, O>,\n    B => (A, C, D, E, F, G, H, I, J, K, L, M, N, O) + <A, B1, C, D, E, F, G, H, I, J, K, L, M, N, O>,\n    C => (A, B, D, E, F, G, H, I, J, K, L, M, N, O) + <A, B, C1, D, E, F, G, H, I, J, K, L, M, N, O>,\n    D => (A, B, C, E, F, G, H, I, J, K, L, M, N, O) + <A, B, C, D1, E, F, G, H, I, J, K, L, M, N, O>,\n    E => (A, B, C, D, F, G, H, I, J, K, L, M, N, O) + <A, B, C, D, E1, F, G, H, I, J, K, L, M, N, O>,\n    F => (A, B, C, D, E, G, H, I, J, K, L, M, N, O) + <A, B, C, D, E, F1, G, H, I, J, K, L, M, N, O>,\n    G => (A, B, C, D, E, F, H, I, J, K, L, M, N, O) + <A, B, C, D, E, F, G1, H, I, J, K, L, M, N, O>,\n    H => (A, B, C, D, E, F, G, I, J, K, L, M, N, O) + <A, B, C, D, E, F, G, H1, I, J, K, L, M, N, O>,\n    I => (A, B, C, D, E, F, G, H, J, K, L, M, N, O) + <A, B, C, D, E, F, G, H, I1, J, K, L, M, N, O>,\n    J => (A, B, C, D, E, F, G, H, I, K, L, M, N, O) + <A, B, C, D, E, F, G, H, I, J1, K, L, M, N, O>,\n    K => (A, B, C, D, E, F, G, H, I, J, L, M, N, O) + <A, B, C, D, E, F, G, H, I, J, K1, L, M, N, O>,\n    L => (A, B, C, D, E, F, G, H, I, J, K, M, N, O) + <A, B, C, D, E, F, G, H, I, J, K, L1, M, N, O>,\n    M => (A, B, C, D, E, F, G, H, I, J, K, L, N, O) + <A, B, C, D, E, F, G, H, I, J, K, L, M1, N, O>,\n    N => (A, B, C, D, E, F, G, H, I, J, K, L, M, O) + <A, B, C, D, E, F, G, H, I, J, K, L, M, N1, O>,\n    O => (A, B, C, D, E, F, G, H, I, J, K, L, M, N) + <A, B, C, D, E, F, G, H, I, J, K, L, M, N, O1>,\n});\ntuples!(EitherOf16 + EitherOf16Future + EitherOf16FutureProj {\n    A => (B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) + <A1, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P>,\n    B => (A, C, D, E, F, G, H, I, J, K, L, M, N, O, P) + <A, B1, C, D, E, F, G, H, I, J, K, L, M, N, O, P>,\n    C => (A, B, D, E, F, G, H, I, J, K, L, M, N, O, P) + <A, B, C1, D, E, F, G, H, I, J, K, L, M, N, O, P>,\n    D => (A, B, C, E, F, G, H, I, J, K, L, M, N, O, P) + <A, B, C, D1, E, F, G, H, I, J, K, L, M, N, O, P>,\n    E => (A, B, C, D, F, G, H, I, J, K, L, M, N, O, P) + <A, B, C, D, E1, F, G, H, I, J, K, L, M, N, O, P>,\n    F => (A, B, C, D, E, G, H, I, J, K, L, M, N, O, P) + <A, B, C, D, E, F1, G, H, I, J, K, L, M, N, O, P>,\n    G => (A, B, C, D, E, F, H, I, J, K, L, M, N, O, P) + <A, B, C, D, E, F, G1, H, I, J, K, L, M, N, O, P>,\n    H => (A, B, C, D, E, F, G, I, J, K, L, M, N, O, P) + <A, B, C, D, E, F, G, H1, I, J, K, L, M, N, O, P>,\n    I => (A, B, C, D, E, F, G, H, J, K, L, M, N, O, P) + <A, B, C, D, E, F, G, H, I1, J, K, L, M, N, O, P>,\n    J => (A, B, C, D, E, F, G, H, I, K, L, M, N, O, P) + <A, B, C, D, E, F, G, H, I, J1, K, L, M, N, O, P>,\n    K => (A, B, C, D, E, F, G, H, I, J, L, M, N, O, P) + <A, B, C, D, E, F, G, H, I, J, K1, L, M, N, O, P>,\n    L => (A, B, C, D, E, F, G, H, I, J, K, M, N, O, P) + <A, B, C, D, E, F, G, H, I, J, K, L1, M, N, O, P>,\n    M => (A, B, C, D, E, F, G, H, I, J, K, L, N, O, P) + <A, B, C, D, E, F, G, H, I, J, K, L, M1, N, O, P>,\n    N => (A, B, C, D, E, F, G, H, I, J, K, L, M, O, P) + <A, B, C, D, E, F, G, H, I, J, K, L, M, N1, O, P>,\n    O => (A, B, C, D, E, F, G, H, I, J, K, L, M, N, P) + <A, B, C, D, E, F, G, H, I, J, K, L, M, N, O1, P>,\n    P => (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) + <A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P1>,\n});\n// if you need more eithers feel free to open a PR ;-)\n\n/// Matches over the first expression and returns an either ([`Either`], [`EitherOf3`], ... [`EitherOf8`])\n/// composed of the values returned by the match arms.\n///\n/// The pattern syntax is exactly the same as found in a match arm.\n///\n/// # Examples\n///\n/// ```\n/// # use either_of::*;\n/// let either2 = either!(Some(\"hello\"),\n///     Some(s) => s.len(),\n///     None => 0.0,\n/// );\n/// assert!(matches!(either2, Either::<usize, f64>::Left(5)));\n///\n/// let either3 = either!(Some(\"admin\"),\n///     Some(\"admin\") => \"hello admin\",\n///     Some(_) => 'x',\n///     _ => 0,\n/// );\n/// assert!(matches!(either3, EitherOf3::<&str, char, i32>::A(\"hello admin\")));\n/// ```\n#[macro_export]\nmacro_rules! either {\n    ($match:expr, $left_pattern:pat => $left_expression:expr, $right_pattern:pat => $right_expression:expr$(,)?) => {\n        match $match {\n            $left_pattern => $crate::Either::Left($left_expression),\n            $right_pattern => $crate::Either::Right($right_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf3::A($a_expression),\n            $b_pattern => $crate::EitherOf3::B($b_expression),\n            $c_pattern => $crate::EitherOf3::C($c_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf4::A($a_expression),\n            $b_pattern => $crate::EitherOf4::B($b_expression),\n            $c_pattern => $crate::EitherOf4::C($c_expression),\n            $d_pattern => $crate::EitherOf4::D($d_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf5::A($a_expression),\n            $b_pattern => $crate::EitherOf5::B($b_expression),\n            $c_pattern => $crate::EitherOf5::C($c_expression),\n            $d_pattern => $crate::EitherOf5::D($d_expression),\n            $e_pattern => $crate::EitherOf5::E($e_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf6::A($a_expression),\n            $b_pattern => $crate::EitherOf6::B($b_expression),\n            $c_pattern => $crate::EitherOf6::C($c_expression),\n            $d_pattern => $crate::EitherOf6::D($d_expression),\n            $e_pattern => $crate::EitherOf6::E($e_expression),\n            $f_pattern => $crate::EitherOf6::F($f_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf7::A($a_expression),\n            $b_pattern => $crate::EitherOf7::B($b_expression),\n            $c_pattern => $crate::EitherOf7::C($c_expression),\n            $d_pattern => $crate::EitherOf7::D($d_expression),\n            $e_pattern => $crate::EitherOf7::E($e_expression),\n            $f_pattern => $crate::EitherOf7::F($f_expression),\n            $g_pattern => $crate::EitherOf7::G($g_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf8::A($a_expression),\n            $b_pattern => $crate::EitherOf8::B($b_expression),\n            $c_pattern => $crate::EitherOf8::C($c_expression),\n            $d_pattern => $crate::EitherOf8::D($d_expression),\n            $e_pattern => $crate::EitherOf8::E($e_expression),\n            $f_pattern => $crate::EitherOf8::F($f_expression),\n            $g_pattern => $crate::EitherOf8::G($g_expression),\n            $h_pattern => $crate::EitherOf8::H($h_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr, $i_pattern:pat => $i_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf9::A($a_expression),\n            $b_pattern => $crate::EitherOf9::B($b_expression),\n            $c_pattern => $crate::EitherOf9::C($c_expression),\n            $d_pattern => $crate::EitherOf9::D($d_expression),\n            $e_pattern => $crate::EitherOf9::E($e_expression),\n            $f_pattern => $crate::EitherOf9::F($f_expression),\n            $g_pattern => $crate::EitherOf9::G($g_expression),\n            $h_pattern => $crate::EitherOf9::H($h_expression),\n            $i_pattern => $crate::EitherOf9::I($i_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr, $i_pattern:pat => $i_expression:expr, $j_pattern:pat => $j_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf10::A($a_expression),\n            $b_pattern => $crate::EitherOf10::B($b_expression),\n            $c_pattern => $crate::EitherOf10::C($c_expression),\n            $d_pattern => $crate::EitherOf10::D($d_expression),\n            $e_pattern => $crate::EitherOf10::E($e_expression),\n            $f_pattern => $crate::EitherOf10::F($f_expression),\n            $g_pattern => $crate::EitherOf10::G($g_expression),\n            $h_pattern => $crate::EitherOf10::H($h_expression),\n            $i_pattern => $crate::EitherOf10::I($i_expression),\n            $j_pattern => $crate::EitherOf10::J($j_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr, $i_pattern:pat => $i_expression:expr, $j_pattern:pat => $j_expression:expr, $k_pattern:pat => $k_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf11::A($a_expression),\n            $b_pattern => $crate::EitherOf11::B($b_expression),\n            $c_pattern => $crate::EitherOf11::C($c_expression),\n            $d_pattern => $crate::EitherOf11::D($d_expression),\n            $e_pattern => $crate::EitherOf11::E($e_expression),\n            $f_pattern => $crate::EitherOf11::F($f_expression),\n            $g_pattern => $crate::EitherOf11::G($g_expression),\n            $h_pattern => $crate::EitherOf11::H($h_expression),\n            $i_pattern => $crate::EitherOf11::I($i_expression),\n            $j_pattern => $crate::EitherOf11::J($j_expression),\n            $k_pattern => $crate::EitherOf11::K($k_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr, $i_pattern:pat => $i_expression:expr, $j_pattern:pat => $j_expression:expr, $k_pattern:pat => $k_expression:expr, $l_pattern:pat => $l_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf12::A($a_expression),\n            $b_pattern => $crate::EitherOf12::B($b_expression),\n            $c_pattern => $crate::EitherOf12::C($c_expression),\n            $d_pattern => $crate::EitherOf12::D($d_expression),\n            $e_pattern => $crate::EitherOf12::E($e_expression),\n            $f_pattern => $crate::EitherOf12::F($f_expression),\n            $g_pattern => $crate::EitherOf12::G($g_expression),\n            $h_pattern => $crate::EitherOf12::H($h_expression),\n            $i_pattern => $crate::EitherOf12::I($i_expression),\n            $j_pattern => $crate::EitherOf12::J($j_expression),\n            $k_pattern => $crate::EitherOf12::K($k_expression),\n            $l_pattern => $crate::EitherOf12::L($l_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr, $i_pattern:pat => $i_expression:expr, $j_pattern:pat => $j_expression:expr, $k_pattern:pat => $k_expression:expr, $l_pattern:pat => $l_expression:expr, $m_pattern:pat => $m_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf13::A($a_expression),\n            $b_pattern => $crate::EitherOf13::B($b_expression),\n            $c_pattern => $crate::EitherOf13::C($c_expression),\n            $d_pattern => $crate::EitherOf13::D($d_expression),\n            $e_pattern => $crate::EitherOf13::E($e_expression),\n            $f_pattern => $crate::EitherOf13::F($f_expression),\n            $g_pattern => $crate::EitherOf13::G($g_expression),\n            $h_pattern => $crate::EitherOf13::H($h_expression),\n            $i_pattern => $crate::EitherOf13::I($i_expression),\n            $j_pattern => $crate::EitherOf13::J($j_expression),\n            $k_pattern => $crate::EitherOf13::K($k_expression),\n            $l_pattern => $crate::EitherOf13::L($l_expression),\n            $m_pattern => $crate::EitherOf13::M($m_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr, $i_pattern:pat => $i_expression:expr, $j_pattern:pat => $j_expression:expr, $k_pattern:pat => $k_expression:expr, $l_pattern:pat => $l_expression:expr, $m_pattern:pat => $m_expression:expr, $n_pattern:pat => $n_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf14::A($a_expression),\n            $b_pattern => $crate::EitherOf14::B($b_expression),\n            $c_pattern => $crate::EitherOf14::C($c_expression),\n            $d_pattern => $crate::EitherOf14::D($d_expression),\n            $e_pattern => $crate::EitherOf14::E($e_expression),\n            $f_pattern => $crate::EitherOf14::F($f_expression),\n            $g_pattern => $crate::EitherOf14::G($g_expression),\n            $h_pattern => $crate::EitherOf14::H($h_expression),\n            $i_pattern => $crate::EitherOf14::I($i_expression),\n            $j_pattern => $crate::EitherOf14::J($j_expression),\n            $k_pattern => $crate::EitherOf14::K($k_expression),\n            $l_pattern => $crate::EitherOf14::L($l_expression),\n            $m_pattern => $crate::EitherOf14::M($m_expression),\n            $n_pattern => $crate::EitherOf14::N($n_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr, $i_pattern:pat => $i_expression:expr, $j_pattern:pat => $j_expression:expr, $k_pattern:pat => $k_expression:expr, $l_pattern:pat => $l_expression:expr, $m_pattern:pat => $m_expression:expr, $n_pattern:pat => $n_expression:expr, $o_pattern:pat => $o_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf15::A($a_expression),\n            $b_pattern => $crate::EitherOf15::B($b_expression),\n            $c_pattern => $crate::EitherOf15::C($c_expression),\n            $d_pattern => $crate::EitherOf15::D($d_expression),\n            $e_pattern => $crate::EitherOf15::E($e_expression),\n            $f_pattern => $crate::EitherOf15::F($f_expression),\n            $g_pattern => $crate::EitherOf15::G($g_expression),\n            $h_pattern => $crate::EitherOf15::H($h_expression),\n            $i_pattern => $crate::EitherOf15::I($i_expression),\n            $j_pattern => $crate::EitherOf15::J($j_expression),\n            $k_pattern => $crate::EitherOf15::K($k_expression),\n            $l_pattern => $crate::EitherOf15::L($l_expression),\n            $m_pattern => $crate::EitherOf15::M($m_expression),\n            $n_pattern => $crate::EitherOf15::N($n_expression),\n            $o_pattern => $crate::EitherOf15::O($o_expression),\n        }\n    };\n    ($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr, $i_pattern:pat => $i_expression:expr, $j_pattern:pat => $j_expression:expr, $k_pattern:pat => $k_expression:expr, $l_pattern:pat => $l_expression:expr, $m_pattern:pat => $m_expression:expr, $n_pattern:pat => $n_expression:expr, $o_pattern:pat => $o_expression:expr, $p_pattern:pat => $p_expression:expr$(,)?) => {\n        match $match {\n            $a_pattern => $crate::EitherOf16::A($a_expression),\n            $b_pattern => $crate::EitherOf16::B($b_expression),\n            $c_pattern => $crate::EitherOf16::C($c_expression),\n            $d_pattern => $crate::EitherOf16::D($d_expression),\n            $e_pattern => $crate::EitherOf16::E($e_expression),\n            $f_pattern => $crate::EitherOf16::F($f_expression),\n            $g_pattern => $crate::EitherOf16::G($g_expression),\n            $h_pattern => $crate::EitherOf16::H($h_expression),\n            $i_pattern => $crate::EitherOf16::I($i_expression),\n            $j_pattern => $crate::EitherOf16::J($j_expression),\n            $k_pattern => $crate::EitherOf16::K($k_expression),\n            $l_pattern => $crate::EitherOf16::L($l_expression),\n            $m_pattern => $crate::EitherOf16::M($m_expression),\n            $n_pattern => $crate::EitherOf16::N($n_expression),\n            $o_pattern => $crate::EitherOf16::O($o_expression),\n            $p_pattern => $crate::EitherOf16::P($p_expression),\n        }\n    };\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    // compile time test\n    #[test]\n    fn either_macro() {\n        let _: Either<&str, f64> = either!(12,\n            12 => \"12\",\n            _ => 0.0f64,\n        );\n        let _: EitherOf3<&str, f64, i32> = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            _ => 12i32,\n        );\n        let _: EitherOf4<&str, f64, char, i32> = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            _ => 12i32,\n        );\n        let _: EitherOf5<&str, f64, char, f32, i32> = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            _ => 12i32,\n        );\n        let _: EitherOf6<&str, f64, char, f32, u8, i32> = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            _ => 12i32,\n        );\n        let _: EitherOf7<&str, f64, char, f32, u8, i8, i32> = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            _ => 12i32,\n        );\n        let _: EitherOf8<&str, f64, char, f32, u8, i8, u16, i32> = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            _ => 12i32,\n        );\n        let _: EitherOf9<&str, f64, char, f32, u8, i8, u16, i16, i32> = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            19 => 64i16,\n            _ => 12i32,\n        );\n        let _: EitherOf10<&str, f64, char, f32, u8, i8, u16, i16, u32, i32> = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            19 => 64i16,\n            20 => 84u32,\n            _ => 12i32,\n        );\n        let _: EitherOf11<\n            &str,\n            f64,\n            char,\n            f32,\n            u8,\n            i8,\n            u16,\n            i16,\n            u32,\n            i32,\n            u64,\n        > = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            19 => 64i16,\n            20 => 84u32,\n            21 => 12i32,\n            _ => 13u64,\n        );\n        let _: EitherOf12<\n            &str,\n            f64,\n            char,\n            f32,\n            u8,\n            i8,\n            u16,\n            i16,\n            u32,\n            i32,\n            u64,\n            i64,\n        > = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            19 => 64i16,\n            20 => 84u32,\n            21 => 12i32,\n            22 => 13u64,\n            _ => 14i64,\n        );\n        let _: EitherOf13<\n            &str,\n            f64,\n            char,\n            f32,\n            u8,\n            i8,\n            u16,\n            i16,\n            u32,\n            i32,\n            u64,\n            i64,\n            u128,\n        > = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            19 => 64i16,\n            20 => 84u32,\n            21 => 12i32,\n            22 => 13u64,\n            23 => 14i64,\n            _ => 15u128,\n        );\n        let _: EitherOf14<\n            &str,\n            f64,\n            char,\n            f32,\n            u8,\n            i8,\n            u16,\n            i16,\n            u32,\n            i32,\n            u64,\n            i64,\n            u128,\n            i128,\n        > = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            19 => 64i16,\n            20 => 84u32,\n            21 => 12i32,\n            22 => 13u64,\n            23 => 14i64,\n            24 => 15u128,\n            _ => 16i128,\n        );\n        let _: EitherOf15<\n            &str,\n            f64,\n            char,\n            f32,\n            u8,\n            i8,\n            u16,\n            i16,\n            u32,\n            i32,\n            u64,\n            i64,\n            u128,\n            i128,\n            [u8; 1],\n        > = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            19 => 64i16,\n            20 => 84u32,\n            21 => 12i32,\n            22 => 13u64,\n            23 => 14i64,\n            24 => 15u128,\n            25 => 16i128,\n            _ => [1u8],\n        );\n        let _: EitherOf16<\n            &str,\n            f64,\n            char,\n            f32,\n            u8,\n            i8,\n            u16,\n            i16,\n            u32,\n            i32,\n            u64,\n            i64,\n            u128,\n            i128,\n            [u8; 1],\n            [i8; 1],\n        > = either!(12,\n            12 => \"12\",\n            13 => 0.0f64,\n            14 => ' ',\n            15 => 0.0f32,\n            16 => 24u8,\n            17 => 2i8,\n            18 => 42u16,\n            19 => 64i16,\n            20 => 84u32,\n            21 => 12i32,\n            22 => 13u64,\n            23 => 14i64,\n            24 => 15u128,\n            25 => 16i128,\n            26 => [1u8],\n            _ => [1i8],\n        );\n    }\n\n    #[test]\n    #[should_panic]\n    fn unwrap_wrong_either() {\n        Either::<i32, &str>::Left(0).unwrap_right();\n    }\n}\n"
  },
  {
    "path": "examples/Makefile.toml",
    "content": "extend = [{ path = \"./cargo-make/main.toml\" }]\n\n[env]\nCARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true\nCARGO_MAKE_CARGO_BUILD_TEST_FLAGS = \"\"\nCARGO_MAKE_WORKSPACE_EMULATION = true\nCARGO_MAKE_CRATE_WORKSPACE_MEMBERS = [\n  \"action-form-error-handling\",\n  \"animated_show\",\n  \"counter\",\n  \"counter_isomorphic\",\n  \"counters\",\n  \"counter_url_query\",\n  \"counter_without_macros\",\n  \"directives\",\n  \"error_boundary\",\n  \"errors_axum\",\n  \"fetch\",\n  \"hackernews\",\n  \"hackernews_axum\",\n  \"hackernews_islands_axum\",\n  \"hackernews_js_fetch\",\n  \"js-framework-benchmark\",\n  \"login_with_token_csr_only\",\n  \"parent_child\",\n  \"portal\",\n  \"router\",\n  \"server_fns_axum\",\n  \"session_auth_axum\",\n  \"slots\",\n  \"spread\",\n  \"sso_auth_axum\",\n  \"ssr_modes\",\n  \"ssr_modes_axum\",\n  \"suspense_tests\",\n  \"tailwind_actix\",\n  \"tailwind_axum\",\n  \"tailwind_csr\",\n  \"timer\",\n  \"todo_app_sqlite\",\n  \"todo_app_sqlite_axum\",\n  \"todo_app_sqlite_csr\",\n  \"todomvc\",\n]\n\n[tasks.gen-members]\nworkspace = false\ndescription = \"Generate the list of workspace members\"\nscript = '''\nexamples=$(ls |\ngrep -v .md |\ngrep -v Makefile.toml |\ngrep -v cargo-make |\ngrep -v gtk |\njq -R -s -c 'split(\"\\n\")[:-1]')\necho \"CARGO_MAKE_CRATE_WORKSPACE_MEMBERS = $examples\"\n'''\n\n[tasks.test-report]\nworkspace = false\ndescription = \"show the cargo-make configuration for web examples [web|all|help]\"\nscript = { file = \"./cargo-make/scripts/web-report.sh\" }\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples README\n\n## Main Branch\n\nThe examples in this directory are all built and tested against the current `main` branch.\n\nTo the extent that new features have been released or breaking changes have been made since the previous release, the examples are compatible with the `main` branch and not the current release.\n\n## Getting Started\n\nThe simplest way to get started with any example is to use the “quick start” command found in the README for each example. Most of the examples use either [`trunk`](https://trunkrs.dev/) (a simple build system and dev server for client-side-rendered apps) or [`cargo-leptos`](https://github.com/leptos-rs/cargo-leptos) (a build system for server-rendered and client-hydrated apps).\n\n## Using Cargo Make\n\nYou can also run any of the examples using [`cargo-make`](https://github.com/sagiegurari/cargo-make). Note that this is completely optional. We use it for CI, and it can be convenient for running the examples, but is not required.\n\nFollow these steps to get any example up and running.\n\n1. `cd` to the example you want to run\n2. Make sure `cargo-make` is installed (for example by running `cargo install cargo-make`)\n3. Make sure `rustup target add wasm32-unknown-unknown` was executed for the currently selected toolchain.\n4. Run `cargo make ci` to setup and test the example\n5. Run `cargo make start` to run the example\n6. Open the client URL in the console output (<http://127.0.0.1:8080> or <http://127.0.0.1:3000> by default)\n7. Run `cargo make stop` to end any processes started by `cargo make start`.\n\nHere are a few additional notes:\n\n- Extendable custom task files are located in the [cargo-make](./cargo-make/) directory\n- Running a task will automatically install `cargo` dependencies\n- Each `Makefile.toml` file must extend the [cargo-make/main.toml](./cargo-make/main.toml) file\n- [cargo-make](./cargo-make/) files that end in `*-test.toml` configure web testing strategies\n- Run `cargo make test-report` to learn which examples have web tests\n\n## Prerequisites\n\nExample projects depend on the following tools. Please install them as needed.\n\n- [Rust](https://www.rust-lang.org/)\n- Nightly Rust\n  - Run `rustup toolchain install nightly`\n  - Run `rustup target add wasm32-unknown-unknown`\n- [Cargo Make](https://sagiegurari.github.io/cargo-make/)\n  - Run `cargo install --force cargo-make`\n  - Setup a command alias like `alias cm='cargo make'` to reduce typing (**_Optional_**)\n- [Trunk](https://github.com/thedodd/trunk)\n  - Run `cargo install trunk`\n- [Node Version Manager](https://github.com/nvm-sh/nvm/) (**_Optional_**)\n- [Node.js](https://nodejs.org/)\n- [pnpm](https://pnpm.io/) (**_Optional_**)\n"
  },
  {
    "path": "examples/SSR_NOTES.md",
    "content": "# Server Side Rendering\n\n## Cargo Leptos\n\ncargo-leptos is now the easiest and most featureful way to build server side rendered apps with hydration. It provides automatic recompilation of client and server code, wasm optimisation, CSS minification, and more! Check out more about it [here](https://github.com/akesson/cargo-leptos)\n\n1. Install cargo-leptos\n\n```bash\ncargo install --locked cargo-leptos\n```\n\n2. Build the site in watch mode, recompiling on file changes\n\n```bash\ncargo leptos watch\n```\n\nOpen browser on [http://localhost:3000/](http://localhost:3000/)\n\n3. When ready to deploy, run\n\n```bash\ncargo leptos build --release\n```\n\n## WASM Pack\n\nTo run it as a server side app with hydration, you'll need to have wasm-pack installed.\n\n0. Edit the `[package.metadata.leptos]` section and set `site-root` to `\".\"`. For examples with CSS you also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.\n\n1. Install wasm-pack\n\n```bash\ncargo install wasm-pack\n```\n\n2. Build the Webassembly used to hydrate the HTML from the server\n\n```bash\nwasm-pack build --target=web --debug --no-default-features --features=hydrate\n```\n\n3. Run the server to serve the Webassembly, JS, and HTML\n\n```bash\ncargo run --no-default-features --features=ssr\n```\n\n### Server Side Rendering With Hydration\n\nTo run it as a server side app with hydration, first you should run\n\n```bash\nwasm-pack build --target=web --debug --no-default-features --features=hydrate\n```\n\nto generate the WebAssembly to hydrate the HTML delivered from the server.\n\nThen run the server with `cargo run` to serve the server side rendered HTML and the WASM bundle for hydration.\n\n```bash\ncargo run --no-default-features --features=ssr\n```\n\n> Note that if your hydration code changes, you will have to rerun the wasm-pack command above before running\n> `cargo run`"
  },
  {
    "path": "examples/action-form-error-handling/Cargo.toml",
    "content": "[package]\nname = \"action-form-error-handling\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nactix-files = { version = \"0.6.6\", optional = true }\nactix-web = { version = \"4.8\", optional = true, features = [\"macros\"] }\nconsole_error_panic_hook = \"0.1.7\"\ncfg-if = \"1.0\"\nleptos = { path = \"../../leptos\" }\nleptos_meta = { path = \"../../meta\" }\nleptos_actix = { path = \"../../integrations/actix\", optional = true }\nleptos_router = { path = \"../../router\" }\nwasm-bindgen = \"0.2.93\"\nserde = { version = \"1.0\", features = [\"derive\"] }\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:actix-files\",\n  \"dep:actix-web\",\n  \"dep:leptos_actix\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n]\n\n# Defines a size-optimized profile for the WASM bundle in release mode\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name   \noutput-name = \"leptos_start\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\t\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\n# The profile to use for the lib target when compiling for release\n#\n# Optional. Defaults to \"release\".\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "examples/action-form-error-handling/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"action_form_error_handling\"\n"
  },
  {
    "path": "examples/action-form-error-handling/README.md",
    "content": "# Action Form Error Handling Example\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nExecute `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/action-form-error-handling/src/app.rs",
    "content": "use leptos::{logging, prelude::*};\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router},\n    StaticSegment,\n};\n\n#[component]\npub fn App() -> impl IntoView {\n    view! {\n        // content for this welcome page\n        <Router>\n            <main id=\"app\">\n                <FlatRoutes fallback=NotFound>\n                    <Route path=StaticSegment(\"\") view=HomePage/>\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[server]\nasync fn do_something(\n    should_error: Option<String>,\n) -> Result<String, ServerFnError> {\n    if should_error.is_none() {\n        Ok(String::from(\"Successful submit\"))\n    } else {\n        Err(ServerFnError::ServerError(String::from(\n            \"You got an error!\",\n        )))\n    }\n}\n\n/// Renders the home page of your application.\n#[component]\nfn HomePage() -> impl IntoView {\n    let do_something_action = ServerAction::<DoSomething>::new();\n    let value = Signal::derive(move || {\n        do_something_action\n            .value()\n            .get()\n            .unwrap_or_else(|| Ok(String::new()))\n    });\n\n    Effect::new_isomorphic(move |_| {\n        logging::log!(\"Got value = {:?}\", value.get());\n    });\n\n    view! {\n        <h1>\"Test the action form!\"</h1>\n        <ErrorBoundary fallback=move |error| {\n            move || format!(\"{:#?}\", error.get())\n        }>\n            <pre>{value}</pre>\n            <ActionForm action=do_something_action attr:class=\"form\">\n                <label>\"Should error: \"<input type=\"checkbox\" name=\"should_error\"/></label>\n                <button type=\"submit\">Submit</button>\n            </ActionForm>\n        </ErrorBoundary>\n    }\n}\n\n/// 404 - Not Found\n#[component]\nfn NotFound() -> impl IntoView {\n    // set an HTTP status code 404\n    // this is feature gated because it can only be done during\n    // initial server-side rendering\n    // if you navigate to the 404 page subsequently, the status\n    // code will not be set because there is not a new HTTP request\n    // to the server\n    #[cfg(feature = \"ssr\")]\n    {\n        // this can be done inline because it's synchronous\n        // if it were async, we'd use a server function\n        let resp = expect_context::<leptos_actix::ResponseOptions>();\n        resp.set_status(actix_web::http::StatusCode::NOT_FOUND);\n    }\n\n    view! { <h1>\"Not Found\"</h1> }\n}\n"
  },
  {
    "path": "examples/action-form-error-handling/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n\n    console_error_panic_hook::set_once();\n\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/action-form-error-handling/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    use action_form_error_handling::app::*;\n    use actix_files::Files;\n    use actix_web::*;\n    use leptos::prelude::*;\n    use leptos_actix::{generate_route_list, LeptosRoutes};\n    use leptos_meta::MetaTags;\n\n    // Generate the list of routes in your Leptos App\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    println!(\"listening on http://{}\", &addr);\n\n    HttpServer::new(move || {\n        // Generate the list of routes in your Leptos App\n        let routes = generate_route_list(App);\n        let leptos_options = &conf.leptos_options;\n        let site_root = &leptos_options.site_root;\n\n        App::new()\n            .service(Files::new(\"/pkg\", format!(\"{site_root}/pkg\")))\n            .leptos_routes(routes, {\n                let leptos_options = leptos_options.clone();\n                move || {\n                    use leptos::prelude::*;\n\n                    view! {\n                        <!DOCTYPE html>\n                        <html lang=\"en\">\n                            <head>\n                                <meta charset=\"utf-8\"/>\n                                <meta\n                                    name=\"viewport\"\n                                    content=\"width=device-width, initial-scale=1\"\n                                />\n                                <AutoReload options=leptos_options.clone()/>\n                                <HydrationScripts options=leptos_options.clone()/>\n                                <MetaTags/>\n                            </head>\n                            <body>\n                                <App/>\n                            </body>\n                        </html>\n                    }\n            }})\n        //.wrap(middleware::Compress::default())\n    })\n    .bind(&addr)?\n    .run()\n    .await\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n    // see optional feature `csr` instead\n}\n"
  },
  {
    "path": "examples/action-form-error-handling/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n\ttext-align: center;\n}\n\n#app {\n\ttext-align: center;\n}\n\n.form {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 0.5rem;\n}\n"
  },
  {
    "path": "examples/axum_js_ssr/Cargo.toml",
    "content": "[package]\nname = \"axum_js_ssr\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.8.1\", optional = true }\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\ngloo-utils = \"0.2.0\"\nhtml-escape = \"0.2.13\"\nhttp-body-util = { version = \"0.1.0\", optional = true }\njs-sys = { version = \"0.3.69\", optional = true }\nleptos = { path = \"../../leptos\", features = [\"tracing\"] }\nleptos_meta = { path = \"../../meta\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../router\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\nthiserror = \"2.0.12\"\ntokio = { version = \"1.39\", features = [\n  \"rt-multi-thread\",\n  \"macros\",\n  \"time\",\n], optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\nwasm-bindgen = \"0.2.105\"\nweb-sys = { version = \"0.3.69\", features = [\n  \"AddEventListenerOptions\",\n  \"Document\",\n  \"Element\",\n  \"Event\",\n  \"EventListener\",\n  \"EventTarget\",\n  \"Performance\",\n  \"Window\",\n], optional = true }\n\n[features]\nhydrate = [\"leptos/hydrate\", \"dep:js-sys\", \"dep:web-sys\"]\nssr = [\n  \"dep:axum\",\n  \"dep:http-body-util\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"dep:leptos_axum\",\n  \"leptos_router/ssr\",\n]\n\n[profile.release]\npanic = \"abort\"\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"axum_js_ssr\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"assets\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "examples/axum_js_ssr/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Tommy Yu\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/axum_js_ssr/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"axum_js_ssr\"\n"
  },
  {
    "path": "examples/axum_js_ssr/README.md",
    "content": "# Leptos Axum JS SSR Example\n\nThis example shows the various ways that JavaScript may be included into\na Leptos application.  The intent is to demonstrate how this may be done\nand how it may cause the application to fail in an unexpected manner if\ndone incorrectly.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/axum_js_ssr/package.json",
    "content": "{\n  \"name\": \"axum_js_ssr\",\n  \"dependencies\": {\n    \"@highlightjs/cdn-assets\": \"^11.10.0\"\n  }\n}\n"
  },
  {
    "path": "examples/axum_js_ssr/src/api.rs",
    "content": "use leptos::{prelude::ServerFnError, server};\n\n#[server]\npub async fn fetch_code() -> Result<String, ServerFnError> {\n    // emulate loading of code from a database/version control/etc\n    tokio::time::sleep(std::time::Duration::from_millis(50)).await;\n    Ok(crate::consts::CH05_02A.to_string())\n}\n"
  },
  {
    "path": "examples/axum_js_ssr/src/app.rs",
    "content": "use crate::{\n    api::fetch_code,\n    consts::{CH03_05A, LEPTOS_HYDRATED},\n};\nuse leptos::prelude::*;\nuse leptos_meta::{MetaTags, *};\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router, A},\n    path, SsrMode,\n};\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n    let fallback = || view! { \"Page not found.\" }.into_view();\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/axum_js_ssr.css\"/>\n        <Title text=\"Leptos JavaScript Integration Demo with SSR in Axum\"/>\n        <Meta name=\"color-scheme\" content=\"dark light\"/>\n        <Router>\n            <nav>\n                <A attr:class=\"section\" href=\"/\">\"Introduction (home)\"</A>\n                <A attr:class=\"example\" href=\"/naive\">\"Naive \"<code>\"<script>\"</code>\n                    <small>\"truly naive to start off\"</small></A>\n                <A attr:class=\"example\" href=\"/naive-alt\">\"Leptos \"<code>\"<Script>\"</code>\n                    <small>\"naively using load event\"</small></A>\n                <A attr:class=\"example\" href=\"/naive-hook\">\"Leptos \"<code>\"<Script>\"</code>\n                    <small>\"... correcting placement\"</small></A>\n                <A attr:class=\"example\" href=\"/naive-fallback\">\"Leptos \"<code>\"<Script>\"</code>\n                    <small>\"... with fallback\"</small></A>\n                <A attr:class=\"example\" href=\"/signal-effect-script\">\"Leptos Signal + Effect\"\n                    <small>\"an idiomatic Leptos solution\"</small></A>\n                <A attr:class=\"subexample section\" href=\"/custom-event\">\"Hydrated Event\"\n                    <small>\"using \"<code>\"js_sys\"</code>\"/\"<code>\"web_sys\"</code></small></A>\n                <A attr:class=\"example\" href=\"/wasm-bindgen-naive\">\"Using \"<code>\"wasm-bindgen\"</code>\n                    <small>\"naively to start with\"</small></A>\n                <A attr:class=\"example\" href=\"/wasm-bindgen-event\">\"Using \"<code>\"wasm-bindgen\"</code>\n                    <small>\"overcomplication with events\"</small></A>\n                <A attr:class=\"example\" href=\"/wasm-bindgen-effect\">\"Using \"<code>\"wasm-bindgen\"</code>\n                    <small>\"lazily delay DOM manipulation\"</small></A>\n                <A attr:class=\"example\" href=\"/wasm-bindgen-direct\">\"Using \"<code>\"wasm-bindgen\"</code>\n                    <small>\"without DOM manipulation\"</small></A>\n                <A attr:class=\"example section\" href=\"/wasm-bindgen-direct-fixed\">\n                    \"Using \"<code>\"wasm-bindgen\"</code>\n                    <small>\"corrected with signal + effect\"</small>\n                </A>\n                <a id=\"reset\" href=\"/\" target=\"_self\">\"Restart/Rehydrate\"\n                    <small>\"to make things work again\"</small></a>\n            </nav>\n            <main>\n                <div id=\"notice\">\n                    \"The WASM application has panicked during hydration. \"\n                    <a href=\"/\" target=\"_self\">\n                        \"Restart the application by going home\"\n                    </a>\".\"\n                </div>\n                <article>\n                    <h1>\"Leptos JavaScript Integration Demo with SSR in Axum\"</h1>\n                    <FlatRoutes fallback>\n                        <Route path=path!(\"\") view=HomePage/>\n                        <Route path=path!(\"naive\") view=Naive ssr=SsrMode::Async/>\n                        <Route path=path!(\"naive-alt\") view=|| view! { <NaiveEvent/> } ssr=SsrMode::Async/>\n                        <Route path=path!(\"naive-hook\") view=|| view! { <NaiveEvent hook=true/> } ssr=SsrMode::Async/>\n                        <Route path=path!(\"naive-fallback\") view=|| view! {\n                            <NaiveEvent hook=true fallback=true/>\n                        } ssr=SsrMode::Async/>\n                        <Route path=path!(\"signal-effect-script\") view=CodeDemoSignalEffect ssr=SsrMode::Async/>\n                        <Route path=path!(\"custom-event\") view=CustomEvent ssr=SsrMode::Async/>\n                        <Route path=path!(\"wasm-bindgen-naive\") view=WasmBindgenNaive ssr=SsrMode::Async/>\n                        <Route path=path!(\"wasm-bindgen-event\") view=WasmBindgenJSHookReadyEvent ssr=SsrMode::Async/>\n                        <Route path=path!(\"wasm-bindgen-effect\") view=WasmBindgenEffect ssr=SsrMode::Async/>\n                        <Route path=path!(\"wasm-bindgen-direct\") view=WasmBindgenDirect ssr=SsrMode::Async/>\n                        <Route path=path!(\"wasm-bindgen-direct-fixed\") view=WasmBindgenDirectFixed ssr=SsrMode::Async/>\n                    </FlatRoutes>\n                </article>\n            </main>\n        </Router>\n    }\n}\n\n#[component]\nfn HomePage() -> impl IntoView {\n    view! {\n        <p>\"\n            This example application demonstrates a number of ways that JavaScript may be included and used\n            with Leptos naively, describing and showing the shortcomings and failures associated with each of\n            them for both SSR (Server-Side Rendering) and CSR (Client-Side Rendering) with hydration, before\n            leading up to the idiomatic solutions where they work as expected.\n        \"</p>\n        <p>\"\n            For the demonstrations, \"<a href=\"https://github.com/highlightjs/highlight.js\"><code>\n            \"highlight.js\"</code></a>\" will be invoked from within this Leptos application by the examples\n            linked on the side bar.  Since the library to be integrated is a JavaScript library, it must be\n            enabled to fully appreciate this demo, and having the browser's developer tools/console opened is\n            recommended as the logs will indicate the effects and issues as they happen.\n        \"</p>\n        <p>\"\n            Examples 1 to 5 are primarily JavaScript based, where the integration code is included as \"<code>\n            \"<script>\"</code>\" tags, with example 5 (final example of the group) being the idiomatic solution\n            that runs without errors or panic during hydration, plus an additional example 5.1 showing how to\n            get hydration to dispatch an event for JavaScript libraries should that be required.  Examples 6\n            to 10 uses \"<code>\"wasm-bindgen\"</code>\" to call out to the JavaScript library from Rust, starting\n            off with naive examples that mimics JavaScript conventions, again with the final example of the\n            group (example 10) being the fully working version that embraces the use of Rust.\n        \"</p>\n    }\n}\n\n#[derive(Clone, Debug)]\nstruct CodeDemoHook {\n    js_hook: String,\n}\n\n#[component]\nfn CodeDemo() -> impl IntoView {\n    let code = Resource::new(|| (), |_| fetch_code());\n    let code_view = move || {\n        Suspend::new(async move {\n            let hook = use_context::<CodeDemoHook>().map(|h| {\n                leptos::logging::log!(\"use context suspend JS\");\n                view! {\n                    <Script>{h.js_hook}</Script>\n                }\n            });\n            view! {\n                <pre><code class=\"language-rust\">{code.await}</code></pre>\n                {hook}\n            }\n        })\n    };\n    view! {\n        <p>\"Explanation on what is being demonstrated follows after the following code example table.\"</p>\n        <div id=\"code-demo\">\n            <table>\n                <thead>\n                    <tr>\n                        <th>\"Inline code block (part of this component)\"</th>\n                        <th>\"Dynamic code block (loaded via server fn)\"</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td><pre><code class=\"language-rust\">{CH03_05A}</code></pre></td>\n                        <td>\n                            <Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>\n                                {code_view}\n                            </Suspense>\n                        </td>\n                    </tr>\n                </tbody>\n            </table>\n        </div>\n    }\n}\n\n#[component]\nfn Naive() -> impl IntoView {\n    let loader = r#\"<script src=\"/highlight.min.js\"></script>\n<script>hljs.highlightAll();</script>\"#;\n    view! {\n        <h2>\"Showing what happens when script inclusion is done naively\"</h2>\n        <CodeDemo/>\n        <p>\"\n            This page demonstrates what happens (or doesn't happen) when it is assumed that the \"<code>\n            \"highlight.js\"</code>\" library can just be included from some CDN (well, hosted locally for this\n            example) as per their instructions for basic usage in the browser, specifically:\n        \"</p>\n        <div><pre><code class=\"language-html\">{loader}</code></pre></div>\n        <p>\"\n            The following actions should be taken in order to fully experience the things that do not work as\n            expected:\n        \"</p>\n        <ol>\n            <li>\"\n                You may find that during the initial load of this page when first navigating to here from\n                \\\"Introduction\\\" (do navigate there, reload to reinitiate this application to properly\n                replicate the behavior, or simply use the Restart link at the bottom), none of the code\n                examples below are highlighted.\n            \"</li>\n            <li>\"\n                Go back and then forward again using the browser's navigation system the inline code block\n                will become highlighted.  The cause is due to \"<code>\"highlight.js\"</code>\" being loaded in a\n                standard \"<code>\"<script>\"</code>\" tag that is part of this component and initially it wasn't\n                loaded before the call to \"<code>\"hljs.highlightAll();\"</code>\" was made. Later, when the\n                component gets re-rendered the second time, the code is finally available to ensure one of\n                them works (while also reloading the script, which probably isn't desirable for this use\n                case).\n            \"</li>\n            <li>\"\n                If you have the browser reload this page, you will find that \"<strong>\"both\"</strong>\" code\n                examples now appear to highlight correctly, yay! However you will also find that the browser's\n                back button appears to do nothing at all (even though the address bar may have changed), and\n                that most of the links on the side-bar are non-functional.  A message should have popped up at\n                the top indicating that the application has panicked.\n                \"<details>\"\n                    \"<summary>\"Details about the cause of the crash:\"</summary>\n                    <p>\"\n                        The cause here is because the hydration system found a node where text was expected, a\n                        simple violation of the application's invariant.  Specifically, the code block\n                        originally contained plain text, but with highlighting that got changed to some HTML\n                        markup \"<em>\"before\"</em>\" hydration happened, completely ouside of expectations.\n                        Generally speaking, a panic is the worst kind of error, as it is a hard crash which\n                        stops the application from working, and in this case the reactive system is in a\n                        completely non-functional state.\n                    \"</p>\n                    <p>\"\n                        Fortunately for this application, some internal links within this application have\n                        been specifically excluded from the reactive system (specifically the restart links,\n                        so they remain usable as they are just standard links which include the bottommost one\n                        of the side bar and the one that should become visible as a notification as the panic\n                        happened at the top - both may be used to navigate non-reactively back to the\n                        homepage.\n                    \"</p>\n                    <p>\"\n                        Navigating back after using the non-reactive links will also restart the application,\n                        so using that immediately after to return to this page will once again trigger the\n                        same condition that will result the hydration to panic.  If you wish to maintain the\n                        push state within the history, simply use the browser navigation to navigate through\n                        those pushed addresses and find one that may be reloaded without causing the crash,\n                        and then go the opposite direction the same number of steps to get back to here.\n                    \"</p>\"\n                \"</details>\"\n            \"</li>\n            <li>\"\n                In the working CSR state, if you continue to use the browser's navigation system to go back to\n                home and forward back to this page, you will find that the the browser's console log is\n                spammed with the different delays added to the loading of the standard highlight.js file.  The\n                cause is because the script is unloaded/reloaded every time its \"<code>\"<script>\"</code>\" tag\n                is re-created by this component.  This may or may not be a desirable behavior, so where\n                exactly these tags are situated will matter - if the goal is to load the script once, the tag\n                should be provided above the Router.\n            \"</li>\n            <li>\"\n                Simply use the restart links to get back home and move onto the next example - or come back\n                here, if you wish - while all the examples can be used out of order, the intended broken\n                behaviors being demonstrated are best experienced by going home using the reactive link at the\n                top, and go back to the target example.  Going between different examples demonstrating the\n                subtly broken behavior(s) in arbitrary order can and will amplify into further unexpected and\n                potentially hard to reproduce behaviors.  What they are and why they happen are left as\n                exercise for the users and readers of this demo application.\n            \"</li>\n        </ol>\n        <script src=\"/highlight.min.js\"></script>\n        <script>\"hljs.highlightAll();\"</script>\n    }\n}\n\n#[component]\nfn NaiveEvent(\n    #[prop(optional)] hook: bool,\n    #[prop(optional)] fallback: bool,\n) -> impl IntoView {\n    let render_hook = \"\\\ndocument.querySelector('#hljs-src')\n    .addEventListener('load', (e) => { hljs.highlightAll() }, false);\";\n    let render_call = \"\\\nif (window.hljs) {\n    hljs.highlightAll();\n} else {\n    document.querySelector('#hljs-src')\n        .addEventListener('load', (e) => { hljs.highlightAll() }, false);\n}\";\n    let js_hook = if fallback { render_call } else { render_hook };\n    let explanation = if hook {\n        provide_context(CodeDemoHook {\n            js_hook: js_hook.to_string(),\n        });\n        if fallback {\n            view! {\n                <ol>\n                    <li>\"\n                        In this iteration, the following load hook is set in a \"<code>\"<Script>\"</code>\"\n                        component after the dynamically loaded code example.\"\n                        <pre><code class=\"language-javascript\">{js_hook}</code></pre>\n                    </li>\n                    <li><strong>CSR</strong>\"\n                        This works much better now under CSR due to the fallback that checks whether the\n                        library is already loaded or not.  Using the library directly if it's already loaded\n                        and only register the event otherwise solves the rendering issue under CSR.\n                    \"</li>\n                    <li><strong>SSR</strong>\"\n                        Much like the second example, hydration will still panic some of the time as per the\n                        race condition that was described.\n                    \"</li>\n                </ol>\n                <p>\"\n                    All that being said, all these naive examples still result in hydration being\n                    non-functional in varying degrees of (non-)reproducibility due to race conditions.  Is\n                    there any way to fix this?  Is \"<code>\"wasm-bindgen\"</code>\" the only answer?  What if the\n                    goal is to incorporate external scripts that change often and thus can't easily have\n                    bindings built?  Follow onto the next examples to solve some of this, at the very least\n                    prevent the panic during hydration.\n                \"</p>\n\n            }.into_any()\n        } else {\n            view! {\n                <ol>\n                    <li>\"\n                        In this iteration, the following load hook is set in a \"<code>\"<Script>\"</code>\"\n                        component after the dynamically loaded code example.\"\n                        <pre><code class=\"language-javascript\">{js_hook}</code></pre>\n                    </li>\n                    <li><strong>CSR</strong>\"\n                        Unfortunately, this still doesn't work reliably to highlight both code examples, in\n                        fact, none of the code examples may highlight at all!  Placing the JavaScript loader\n                        hook inside a \"<code>Suspend</code>\" will significantly increase the likelihood that\n                        the event will be fired long before the loader adds the event hook.  As a matter of\n                        fact, the highlighting is likely to only work with the largest latencies added for\n                        the loading of \"<code>\"highlight.js\"</code>\", but at least both code examples will\n                        highlight when working.\n                    \"</li>\n                    <li><strong>SSR</strong>\"\n                        Much like the second example, hydration will still panic some of the time as per the\n                        race condition that was described - basically if the timing results in CSR not showing\n                        highlight code, the code will highlight here in SSR but will panic during hydration.\n                    \"</li>\n                </ol>\n            }.into_any()\n        }\n    } else {\n        view! {\n            <ol>\n                <li>\"\n                    In this iteration, the following hook is set in a \"<code>\"<Script>\"</code>\" component\n                    immediately following the one that loaded \"<code>\"highlight.js\"</code>\".\n                    \"<pre><code class=\"language-javascript\">{js_hook}</code></pre>\n                </li>\n                <li><strong>CSR</strong>\"\n                    Unfortunately, the hook is being set directly on this component, rather than inside the\n                    view for the dynamic block.  Given the nature of asynchronous loading which results in the\n                    uncertainty of the order of events, it may or may not result in the dynamic code block (or\n                    any) being highlighted under CSR (as there may or may not be a fully formed code block for\n                    highlighting to happen).  This is affected by latency, so the loader here emulates a small\n                    number of latency values (they repeat in a cycle).  The latency value is logged into the\n                    console and it may be referred to witness its effects on what it does under CSR - look for\n                    the line that might say \\\"loaded standard highlight.js with a minimum latency of 40 ms\\\".\n                    Test this by going from home to here and then navigating between them using the browser's\n                    back and forward feature for convenience - do ensure the \"<code>\"highlight.js\" </code>\"\n                    isn't being cached by the browser.\n                \"</li>\n                <li><strong>SSR</strong>\"\n                    Moreover, hydration will panic if the highlight script is loaded before hydration is\n                    completed (from the resulting DOM mismatch after code highlighting).  Refreshing here\n                    repeatedly may trigger the panic only some of the time when the \"<code>\"highlight.js\"\n                    </code>\" script is loaded under the lowest amounts of artificial delay, as even under no\n                    latency the hydration can still succeed due to the non-deterministic nature of this race\n                    condition.\n                \"</li>\n            </ol>\n        }.into_any()\n    };\n    // FIXME Seems like <Script> require a text node, otherwise hydration error from marker mismatch\n    view! {\n        <h2>\"Using the Leptos \"<code>\"<Script>\"</code>\" component asynchronously instead\"</h2>\n        <CodeDemo/>\n        <Script id=\"hljs-src\" async_=\"true\" src=\"/highlight.min.js\">\"\"</Script>\n        // Example 2's <Script> invocation; Example 3 and 4 will be provided via a context to allow the\n        // inclusion of the `highlightAll()` call in the Suspend\n        {(!hook).then(|| view! { <Script>{render_hook}</Script>})}\n        <p>\"\n            What the \"<code>\"<Script>\"</code>\" component does is to ensure the \"<code>\"<script>\"</code>\" tag\n            is placed in the document head in the order it is defined in a given component, rather than at\n            where it was placed into the DOM.  Note that it is also a reactive component, much like the first\n            example, it gets unloaded under CSR when the component is no longer active, In this improved\n            version, \"<code>\"highlight.js\"</code>\" is also loaded asynchronously (using the \"<code>\"async\"\n            </code>\" attribute), to allow an event listener that can delay highlighting to after the library\n            is loaded.  This should all work out fine, right?\n        \"</p>\n        {explanation}\n    }\n}\n\n#[component]\nfn CustomEvent() -> impl IntoView {\n    let js_hook = format!(\n        \"\\\nvar events = [];\nif (!window.hljs) {{\n    console.log('pushing listener for hljs load');\n    events.push(new Promise((r) =>\n        document.querySelector('#hljs-src').addEventListener('load', r, \\\n         false)));\n}}\nif (!window.{LEPTOS_HYDRATED}) {{\n    console.log('pushing listener for leptos hydration');\n    events.push(new Promise((r) => \\\n         document.addEventListener('{LEPTOS_HYDRATED}', r, false)));\n}}\nPromise.all(events).then(() => {{\n    console.log(`${{events.length}} events have been dispatched; now calling \\\n         highlightAll()`);\n    hljs.highlightAll();\n}});\n\"\n    );\n    provide_context(CodeDemoHook {\n        js_hook: js_hook.clone(),\n    });\n    // FIXME Seems like <Script> require a text node, otherwise hydration error from marker mismatch\n    view! {\n        <h2>\"Have Leptos dispatch an event when body is hydrated\"</h2>\n        <CodeDemo/>\n        <Script id=\"hljs-src\" async_=\"true\" src=\"/highlight.min.js\">\"\"</Script>\n        <p>\"\n            So if using events fixes problems with timing issues, couldn't Leptos provide an event to signal\n            that the body is hydrated?  Well, this problem is typically solved by having a signal in the\n            component, and then inside the \"<code>\"Suspend\"</code>\" provide an \"<code>\"Effect\"</code>\" that\n            would set the signal to \"<code>\"Some\"</code>\" string that will then mount the \"<code>\"<Script>\"\n            </code>\" onto the body.  However, if a hydrated event is desired from within JavaScript (e.g.\n            where some existing JavaScript library/framework is managing event listeners for some particular\n            reason), given that typical Leptos applications provide the \"<code>\"fn hydate()\"</code>\" (usually\n            in \"<code>\" lib.rs\"</code>\"), that can be achieved by providing the following after \"<code>\n            \"leptos::mount::hydrate_body(App);\"</code>\".\n        \"</p>\n        <div><pre><code class=\"language-rust\">{format!(r#\"#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {{\n    use app::App;\n    // ... other calls omitted, as this example is only a rough\n    // reproduction of what is actually executed.\n    leptos::mount::hydrate_body(App);\n\n    // Now hydrate_body is done, provide ways to inform that\n    let window = leptos::prelude::window();\n    // first set a flag to signal that hydration has happened and other\n    // JavaScript code may just run without waiting for the event that\n    // is just about to be dispatched, as the event is only a one-time\n    // deal but this lives on as a variable that can be checked.\n    js_sys::Reflect::set(\n        &window,\n        &wasm_bindgen::JsValue::from_str({LEPTOS_HYDRATED:?}),\n        &wasm_bindgen::JsValue::TRUE,\n    ).expect(\"error setting hydrated status\");\n    // Then dispatch the event for all the listeners that were added.\n    let event = web_sys::Event::new({LEPTOS_HYDRATED:?})\n        .expect(\"error creating hydrated event\");\n    let document = leptos::prelude::document();\n    document.dispatch_event(&event)\n        .expect(\"error dispatching hydrated event\");\n}}\"#\n        )}</code></pre></div>\n        <p>\"\n            With the notification that hydration is completed, the following JavaScript code may be called\n            inside \"<code>\"Suspense\"</code>\" block (in this live example, it's triggered by providing the\n            following JavaScript code via a \"<code>\"provide_context\"</code>\" which the code rendering\n            component will then use within a \"<code>\"Suspend\"</code>\"):\n        \"</p>\n        <div><pre><code class=\"language-javascript\">{js_hook}</code></pre></div>\n        <p>\"\n            For this simple example with a single \"<code>\"Suspense\"</code>\", no matter what latency there is,\n            in whichever order the API calls are completed, the setup ensures that \"<code>\"highlightAll()\"\n            </code>\" is called only after hydration is done and also after the delayed content is properly\n            rendered onto the DOM.  Specifically, only use the event to wait for the required resource if it\n            is not set to a ready state, and wait for all the events to become ready before actually calling\n            the function.\n        \"</p>\n        <p>\"\n            If there are multiple \"<code>\"Suspense\"</code>\", it will be a matter of adding all the event\n            listeners that will respond to the completion of all the \"<code>\"Suspend\"</code>\"ed futures, which\n            will then invoke the code highlighting function.\n        \"</p>\n        // Leaving this last bit as a bonus page? As an exercise for the readers?\n    }\n}\n\n#[component]\nfn CodeDemoSignalEffect() -> impl IntoView {\n    // Full JS without the use of hydration event\n    // this version will unset hljs if hljs was available to throw a wrench into\n    // the works, but it should still just work.\n    let render_call = r#\"\nif (window.hljs) {\n    hljs.highlightAll();\n    console.log('unloading hljs to try to force the need for addEventListener for next time');\n    window['hljs'] = undefined;\n} else {\n    document.querySelector('#hljs-src')\n        .addEventListener('load', (e) => {\n            hljs.highlightAll();\n            console.log('using hljs inside addEventListener; leaving hljs loaded');\n        }, false);\n};\"#;\n    let code = Resource::new(|| (), |_| fetch_code());\n    let (script, set_script) = signal(None::<String>);\n    let code_view = move || {\n        Suspend::new(async move {\n            Effect::new(move |_| {\n                set_script.set(Some(render_call.to_string()));\n            });\n            view! {\n                <pre><code class=\"language-rust\">{code.await}</code></pre>\n                <ShowLet some=script let:script>\n                    <Script>{script}</Script>\n                </ShowLet>\n            }\n        })\n    };\n    view! {\n        <Script id=\"hljs-src\" async_=\"true\" src=\"/highlight.min.js\">\"\"</Script>\n        <h2>\"Using signal + effect to dynamically set \"<code>\"<Script>\"</code>\" tag as view is mounted\"</h2>\n        <p>\"Explanation on what is being demonstrated follows after the following code example table.\"</p>\n        <div id=\"code-demo\">\n            <table>\n                <thead>\n                    <tr>\n                        <th>\"Inline code block (part of this component)\"</th>\n                        <th>\"Dynamic code block (loaded via server fn)\"</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td><pre><code class=\"language-rust\">{CH03_05A}</code></pre></td>\n                        <td>\n                            <Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>\n                                {code_view}\n                            </Suspense>\n                        </td>\n                    </tr>\n                </tbody>\n            </table>\n        </div>\n        <p>\"\n            To properly ensure the \"<code>\"<Script>\"</code>\" tag containing the initialization code for the\n            target JavaScript usage is executed after the \"<code>\"Suspend\"</code>\"ed view is fully rendered\n            and mounted onto the DOM, with the use of an effect that sets a signal to trigger the rendering\n            inside the suspend will achieve exactly that.  That was a mouthful, so let's look at the code\n            for that then:\n        \"</p>\n        <div><pre><code class=\"language-rust\">r##\"#[component]\nfn CodeDemoSignalEffect() -> impl IntoView {\n    let render_call = r#\"\nif (window.hljs) {\n    hljs.highlightAll();\n} else {\n    document.querySelector('#hljs-src')\n        .addEventListener('load', (e) => { hljs.highlightAll() }, false);\n};\"#;\n    let code = Resource::new(|| (), |_| fetch_code());\n    let (script, set_script) = signal(None::<String>);\n    let code_view = move || {\n        Suspend::new(async move {\n            Effect::new(move |_| {\n                set_script.set(Some(render_call.to_string()));\n            });\n            view! {\n                <pre><code class=\"language-rust\">{code.await}</code></pre>\n                <ShowLet some=script let:script>\n                    <Script>{script}</Script>\n                </ShowLet>\n            }\n        })\n    };\n    view! {\n        <Script id=\"hljs-src\" async_=\"true\" src=\"/highlight.min.js\">\"\"</Script>\n        <Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>\n            {code_view}\n        </Suspense>\n    }\n}\"##</code></pre></div>\n        <p>\"\n            The \"<code>\"Suspend\"</code>\" ensures the asynchronous \"<code>\"Resource\"</code>\" will be completed\n            before the view is returned, which will be mounted onto the DOM, but the initial value of the\n            signal \"<code>\"script\"</code>\" will be \"<code>\"None\"</code>\", so no \"<code>\"<Script>\"</code>\" tag\n            will be rendered at that stage.  Only after the suspended view is mounted onto the DOM the \"<code>\n            \"Effect\"</code>\" will run, which will call \"<code>\"set_script\"</code>\" with \"<code>\"Some\"</code>\"\n            value which will finally populate the \"<code>\"<Script>\"</code>\" tag with the desired JavaScript to\n            be executed, in this case invoke the code highlighting feature if available otherwise wait for it.\n        \"</p>\n        <p>\"\n            If there are multiple \"<code>\"Suspense\"</code>\", it will be a matter of adding the event to be\n            dispatched to \"<code>\"set_script.set\"</code>\" so that it gets dispatched for the component, and\n            then elsewhere above all those components a JavaScript list will tracking all the events will be\n            waited on by \"<code>\"Promise.all\"</code>\", where its completion will finally invoke the desired\n            JavaScript function.\n        \"</p>\n    }\n}\n\nenum WasmDemo {\n    Naive,\n    ReadyEvent,\n    RequestAnimationFrame,\n}\n\n#[component]\nfn CodeDemoWasm(mode: WasmDemo) -> impl IntoView {\n    let code = Resource::new(|| (), |_| fetch_code());\n    let suspense_choice = match mode {\n        WasmDemo::Naive => view! {\n            <Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>{\n                move || Suspend::new(async move {\n                    view! {\n                        <pre><code class=\"language-rust\">{code.await}</code></pre>\n                        {\n                            #[cfg(not(feature = \"ssr\"))]\n                            {\n                                use crate::hljs::highlight_all;\n                                leptos::logging::log!(\"calling highlight_all\");\n                                highlight_all();\n                            }\n                        }\n                    }\n                })\n            }</Suspense>\n        }.into_any(),\n        WasmDemo::ReadyEvent => view! {\n            <Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>{\n                move || Suspend::new(async move {\n                    view! {\n                        <pre><code class=\"language-rust\">{code.await}</code></pre>\n                        {\n                            #[cfg(not(feature = \"ssr\"))]\n                            {\n                                use crate::hljs;\n                                use wasm_bindgen::{closure::Closure, JsCast};\n\n                                let document = document();\n                                // Rules relating to hydration still applies when loading via SSR!  Changing\n                                // the dom before hydration is done is still problematic, as the same issues\n                                // such as the panic as demonstrated in the relevant JavaScript demo.\n                                let hydrate_listener = Closure::<dyn Fn(_)>::new(move |_: web_sys::Event| {\n                                    leptos::logging::log!(\"wasm hydration_listener highlighting\");\n                                    hljs::highlight_all();\n                                }).into_js_value();\n                                document.add_event_listener_with_callback(\n                                    LEPTOS_HYDRATED,\n                                    hydrate_listener.as_ref().unchecked_ref(),\n                                ).expect(\"failed to add event listener to document\");\n\n                                // For CSR rendering, wait for the hljs_hook which will be fired when this\n                                // suspended bit is fully mounted onto the DOM, and this is done using a\n                                // JavaScript shim described below.\n                                let csr_listener = Closure::<dyn FnMut(_)>::new(move |_: web_sys::Event| {\n                                    leptos::logging::log!(\"wasm csr_listener highlighting\");\n                                    hljs::highlight_all();\n                                }).into_js_value();\n                                let options = web_sys::AddEventListenerOptions::new();\n                                options.set_once(true);\n                                // FIXME this actually is not added as a unique function so after a quick re-\n                                // render will re-add this as a new listener, which causes a double call\n                                // to highlightAll.  To fix this there needs to be a way to put the listener\n                                // and keep it unique, but this looks to be rather annoying to do from within\n                                // this example...\n                                document.add_event_listener_with_callback_and_add_event_listener_options(\n                                    \"hljs_hook\",\n                                    csr_listener.as_ref().unchecked_ref(),\n                                    &options,\n                                ).expect(\"failed to add event listener to document\");\n                                leptos::logging::log!(\"wasm csr_listener listener added\");\n\n                                // Dispatch the event when this view is finally mounted onto the DOM.\n                                request_animation_frame(move || {\n                                    let event = web_sys::Event::new(\"hljs_hook\")\n                                        .expect(\"error creating hljs_hook event\");\n                                    document.dispatch_event(&event)\n                                        .expect(\"error dispatching hydrated event\");\n                                });\n                                // Alternative, use a script tag, but at that point, you might as well write\n                                // all of the above in JavaScript because in this simple example none of the\n                                // above is native to Rust or Leptos.\n                            }\n                        }\n                    }\n                })\n            }</Suspense>\n        }.into_any(),\n        WasmDemo::RequestAnimationFrame => view! {\n            <Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>{\n                move || Suspend::new(async move {\n                    Effect::new(move |_| {\n                        request_animation_frame(move || {\n                            leptos::logging::log!(\"request_animation_frame invoking hljs::highlight_all\");\n                            // under SSR this is an noop, but it wouldn't be called under there anyway because\n                            // it isn't the isomorphic version, i.e. Effect::new_isomorphic(...).\n                            crate::hljs::highlight_all();\n                        });\n                    });\n                    view! {\n                        <pre><code class=\"language-rust\">{code.await}</code></pre>\n                    }\n                })\n            }</Suspense>\n        }.into_any(),\n    };\n    view! {\n        <p>\"\n            The syntax highlighting shown in the table below is done by invoking \"<code>\"hljs.highlightAll()\"\n            </code>\" via the binding generated using \"<code>\"wasm-bindgen\"</code>\" - thus the ES version of \"\n            <code>\"highlight.js\"</code>\" is loaded by the output bundle generated by Leptos under this set of\n            demonstrations. However, things may still not work as expected, with the explanation on what is\n            being demonstrated follows after the following code example table.\n        \"</p>\n        <div id=\"code-demo\">\n            <table>\n                <thead>\n                    <tr>\n                        <th>\"Inline code block (part of this component)\"</th>\n                        <th>\"Dynamic code block (loaded via server fn)\"</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td><pre><code class=\"language-rust\">{CH03_05A}</code></pre></td>\n                        <td>{suspense_choice}</td>\n                    </tr>\n                </tbody>\n            </table>\n        </div>\n    }\n}\n\n#[component]\nfn WasmBindgenNaive() -> impl IntoView {\n    let example = r#\"<Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>{\n    move || Suspend::new(async move {\n        view! {\n            <pre><code>{code.await}</code></pre>\n            {\n                #[cfg(not(feature = \"ssr\"))]\n                {\n                    use crate::hljs::highlight_all;\n                    leptos::logging::log!(\"calling highlight_all\");\n                    highlight_all();\n                }\n            }\n        }\n    })\n}</Suspense>\"#;\n    view! {\n        <h2>\"Will \"<code>\"wasm-bindgen\"</code>\" magically avoid all the problems?\"</h2>\n        <CodeDemoWasm mode=WasmDemo::Naive/>\n        <p>\"\n            Well, the naively done example clearly does not work, as the behavior of this demo is almost\n            exactly like the very first naive JavaScript example (after the script loaded), where only the\n            inline code block will highlight under CSR and hydration is broken when trying to load this under\n            SSR.  This is the consequence of porting the logic naively.  In this example, the calling of\n            \"<code>\"hljs::highlight_all()\"</code>\" is located inside a \"<code>\"Suspend\"</code>\" immediately\n            after the code block, but it doesn't mean the execution will apply to that because it hasn't been\n            mounted onto the DOM itself for \"<code>\"highlight.js\"</code>\" to process.\n        \"</p>\n        <p>\"\n            Similarly, SSR may also error under a similar mechanism, which again breaks hydration because the\n            code is run on the dehydrated nodes before hydration has happened.  Using event listeners via\n            \"<code>\"web_sys\"</code>\" in a similar manner like the JavaScript based solutions shown previously\n            can fix this, but there are other approaches also.\n        \"</p>\n        <p>\"\n            For a quick reference, the following is the \"<code>\"Suspense\"</code>\" that would ultimately render\n            the dynamic code block:\n        \"</p>\n        <div><pre><code class=\"language-rust\">{example}</code></pre></div>\n    }\n}\n\n#[component]\nfn WasmBindgenJSHookReadyEvent() -> impl IntoView {\n    let example = r#\"#[cfg(not(feature = \"ssr\"))]\n{\n    use crate::hljs;\n    use wasm_bindgen::{closure::Closure, JsCast};\n\n    let document = document();\n    // Rules relating to hydration still applies when loading via SSR!  Changing\n    // the dom before hydration is done is still problematic, as the same issues\n    // such as the panic as demonstrated in the relevant JavaScript demo.\n    let hydrate_listener = Closure::<dyn Fn(_)>::new(move |_: web_sys::Event| {\n        leptos::logging::log!(\"wasm hydration_listener highlighting\");\n        hljs::highlight_all();\n    }).into_js_value();\n    document.add_event_listener_with_callback(\n        LEPTOS_HYDRATED,\n        hydrate_listener.as_ref().unchecked_ref(),\n    ).expect(\"failed to add event listener to document\");\n\n    // For CSR rendering, wait for the hljs_hook which will be fired when this\n    // suspended bit is fully mounted onto the DOM, and this is done using a\n    // JavaScript shim described below.\n    let csr_listener = Closure::<dyn FnMut(_)>::new(move |_: web_sys::Event| {\n        leptos::logging::log!(\"wasm csr_listener highlighting\");\n        hljs::highlight_all();\n    }).into_js_value();\n    let options = web_sys::AddEventListenerOptions::new();\n    options.set_once(true);\n    // FIXME this actually is not added as a unique function so after a quick re-\n    // render will re-add this as a new listener, which causes a double call\n    // to highlightAll.  To fix this there needs to be a way to put the listener\n    // and keep it unique, but this looks to be rather annoying to do from within\n    // this example...\n    document.add_event_listener_with_callback_and_add_event_listener_options(\n        \"hljs_hook\",\n        csr_listener.as_ref().unchecked_ref(),\n        &options,\n    ).expect(\"failed to add event listener to document\");\n    leptos::logging::log!(\"wasm csr_listener listener added\");\n\n    // Dispatch the event when this view is finally mounted onto the DOM.\n    request_animation_frame(move || {\n        let event = web_sys::Event::new(\"hljs_hook\")\n            .expect(\"error creating hljs_hook event\");\n        document.dispatch_event(&event)\n            .expect(\"error dispatching hydrated event\");\n    });\n    // Alternative, use a script tag, but at that point, you might as well write\n    // all of the above in JavaScript because in this simple example none of the\n    // above is native to Rust or Leptos.\n}\"#;\n\n    view! {\n        <h2>\"Using \"<code>\"wasm-bindgen\"</code>\" with proper consideration\"</h2>\n        <CodeDemoWasm mode=WasmDemo::ReadyEvent/>\n        <p>\"\n            Well, this works a lot better, under SSR the code is highlighted only after hydration to avoid the\n            panic, and under CSR a new event is created for listening and responding to for the rendering to\n            happen only after the suspended node is populated onto the DOM.  There is a bit of a kink with the\n            way this is implemented, but it largely works.\n        \"</p>\n        <p>\"\n            The code that drives this is needlessly overcomplicated, to say the least.  This is what got added\n            to the \"<code>\"view! {...}\"</code>\" from the last example:\n        \"</p>\n        <details>\n            <summary>\"Expand for the rather verbose code example\"</summary>\n            <div><pre><code class=\"language-rust\">{example}</code></pre></div>\n        </details>\n        <p>\"\n            Given that multiple frameworks that will manipulate the DOM in their own and assume they are the\n            only source of truth is the problem - being demonstrated by Leptos in previous examples assuming\n            that nothing else would change the DOM for hydration.  So if it is possible to use the JavaScript\n            library in a way that wouldn't cause unexpected DOM changes, then that can be a way to avoid\n            needing all these additional event listeners for working around the panics.\n        \"</p>\n        <p>\"\n            One thing to note is that this is a very simple example with a single Suspense (or Transition), so\n            if there are more than one of them and they have significantly different resolution timings,\n            calling that potentially indiscriminate JavaScript DOM manipulation function may require\n            additional care (e.g. needing to wait for all the events in a future before making the final call\n            to do make the invasive DOM manipulation).  Let's look at one more similar example that use a\n            cheap workaround that may work for cases like integrating the simple JavaScript library here.\n        \"</p>\n    }\n}\n\n#[component]\nfn WasmBindgenEffect() -> impl IntoView {\n    let example = r#\"<Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>{\n    move || Suspend::new(async move {\n        Effect::new(move |_| {\n            request_animation_frame(move || {\n                leptos::logging::log!(\"request_animation_frame invoking hljs::highlight_all\");\n                // under SSR this is an noop.\n                crate::hljs::highlight_all();\n            });\n        });\n        view! {\n            <pre><code>{code.await}</code></pre>\n        }\n    })\n}</Suspense>\"#;\n\n    view! {\n        <h2>\"Using \"<code>\"wasm-bindgen\"</code>\" with proper consideration, part 2\"</h2>\n        <CodeDemoWasm mode=WasmDemo::RequestAnimationFrame/>\n        <p>\"\n            This example simply uses \"<code>\"window.requestAnimationFrame()\"</code>\" (via the binding\n            available as \"<code>\"leptos::prelude::request_animation_frame\"</code>\") to delay the running of\n            the highlighting by a tick so that both the hydration would complete for SSR, and that it would\n            also delay highlighting call to after the suspend results are loaded onto the DOM.  The Suspend\n            for the dynamic code block is simply reduced to the following:\n        \"</p>\n        <div><pre><code class=\"language-rust\">{example}</code></pre></div>\n        <p>\"\n            However, this method does have a drawback, which is that the inline code blocks will be processed\n            multiple times by this indiscriminate method (which \"<code>\"highlight.js\"</code>\" thankfully has a\n            failsafe detection which avoids issues, but definitely don't count on this being the norm with\n            JavaScript libraries).  We could go back to the previous example where we use events to trigger\n            for when the Suspend is resolved, but this will mean there needs to be some way to co-ordinate and\n            wait for all of them to ensure the JavaScript library is only invoked once on the hydrated output.\n        \"</p>\n        <p>\"\n            If the JavaScript library provides an alternative API that does not involve this wrestling of the\n            DOM but does achieve the intended objectives is in fact available, it would definitely be the\n            better choice.  Even better, make them available in Rust through \"<code>\"wasm-bindgen\"</code>\" so\n            that the relevant Leptos component may use them directly.  In the next couple examples we will see\n            how this idea may be put into practice.\n        \"</p>\n    }\n}\n\n#[derive(Clone)]\nstruct InnerEffect;\n\n#[component]\nfn CodeInner(code: String, lang: String) -> impl IntoView {\n    // lang is currently unused for SSR, so just drop it now to use it to avoid warning.\n    #[cfg(feature = \"ssr\")]\n    drop(lang);\n    if use_context::<InnerEffect>().is_none() {\n        #[cfg(feature = \"ssr\")]\n        let inner = Some(html_escape::encode_text(&code).into_owned());\n        #[cfg(not(feature = \"ssr\"))]\n        let inner = {\n            let inner = crate::hljs::highlight(code, lang);\n            leptos::logging::log!(\n                \"about to populate inner_html with: {inner:?}\"\n            );\n            inner\n        };\n        view! {\n            <pre><code inner_html=inner></code></pre>\n        }\n        .into_any()\n    } else {\n        let (inner, set_inner) = signal(String::new());\n        #[cfg(feature = \"ssr\")]\n        {\n            set_inner.set(html_escape::encode_text(&code).into_owned());\n        };\n        #[cfg(not(feature = \"ssr\"))]\n        {\n            leptos::logging::log!(\"calling out to hljs::highlight\");\n            let result = crate::hljs::highlight(code, lang);\n            Effect::new(move |_| {\n                leptos::logging::log!(\n                    \"setting the result of hljs::highlight inside an effect\"\n                );\n                if let Some(r) = result.clone() {\n                    set_inner.set(r)\n                }\n            });\n        };\n        view! {\n            <pre><code inner_html=inner></code></pre>\n        }\n        .into_any()\n    }\n}\n\n#[component]\nfn CodeDemoWasmInner() -> impl IntoView {\n    let code = Resource::new(|| (), |_| fetch_code());\n    let code_view = move || {\n        Suspend::new(async move {\n            code.await.map(|code| {\n                view! {\n                    <CodeInner code=code lang=\"rust\".to_string()/>\n                }\n            })\n        })\n    };\n    view! {\n        <p>\"\n            The following code examples are assigned via \"<code>\"inner_html\"</code>\" after processing through\n            the relevant/available API call depending on SSR/CSR, without using any \"<code>\"web_sys\"</code>\"\n            events or DOM manipulation outside of Leptos.\n        \"</p>\n        <div id=\"code-demo\">\n            <table>\n                <thead>\n                    <tr>\n                        <th>\"Inline code block (part of this component)\"</th>\n                        <th>\"Dynamic code block (loaded via server fn)\"</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td><CodeInner code=CH03_05A.to_string() lang=\"rust\".to_string()/></td>\n                        <td>\n                            <Suspense fallback=move || view! { <p>\"Loading code example...\"</p> }>\n                                {code_view}\n                            </Suspense>\n                        </td>\n                    </tr>\n                </tbody>\n            </table>\n        </div>\n    }\n}\n\n#[component]\nfn WasmBindgenDirect() -> impl IntoView {\n    let code = r#\"#[component]\nfn CodeInner(code: String, lang: String) -> impl IntoView {\n    #[cfg(feature = \"ssr\")]\n    let inner = Some(html_escape::encode_text(&code).into_owned());\n    #[cfg(not(feature = \"ssr\"))]\n    let inner = crate::hljs::highlight(code, lang);\n    view! {\n        <pre><code inner_html=inner></code></pre>\n    }\n}\n\n// Simply use the above component in a view like so:\n//\n// view! { <CodeInner code lang/> }\"#\n        .to_string();\n    let lang = \"rust\".to_string();\n\n    view! {\n        <h2>\"If possible, avoid DOM manipulation outside of Leptos\"</h2>\n        <CodeDemoWasmInner/>\n        <p>\"\n            Whenever possible, look for a way to use the target JavaScript library to produce the desired\n            markup without going through a global DOM manipulation can end up being much more straight-forward\n            to write when working in pure Rust code.  More so if there is a server side counterpart, which\n            means the use of the module don't need the disambiguation within the component itself.  A\n            simplified version of a component that will render a code block that gets highlighted under CSR\n            (and plain text under SSR) may look something like this:\n        \"</p>\n        <CodeInner code lang/>\n        <p>\"\n            In the above example, no additional \"<code>\"<script>\"</code>\" tags, post-hydration processing,\n            event listeners nor other DOM manipuation are needed, as the JavaScript function that converts a\n            string to highlighted markup can be made from Rust through bindings generated with the use of\n            \"<code>\"wasm-bindgen\"</code>\" under CSR.  As the highlight functionality isn't available under\n            SSR, the incoming code is simply processed using \"<code>\"html_escape::encode_text\"</code>\".\n        \"</p>\n        <p>\"\n            ... Well, if only it actually works, as there is a bit of an unexpected surprise during hydration.\n            During the hydration of the above code rendering component, the CSR specific pipeline kicks in and\n            calls \"<code>\"hljs::highlight\"</code>\", producing a different output that was assumed to trigger\n            a re-rendering.  As hydration assumes the HTML rendered under SSR is isomorphic with CSR, a\n            violation of this expectation (i.e. CSR rendering something entierly different) is not something\n            it anticipates; the lack of re-rendering is in fact an optimization for performance reasons as it\n            avoids unnecessary work.  However in this instance, that isn't the desired behavior as the the\n            syntax highlighting will not be shown as expected, and thankfully in this instance it does not\n            result in a crash.\n        \"</p>\n        <p>\"\n            All that being said, the code is not doing what is desired, is there any way to go about this?\n            Fortunately, this is where effects comes in as it provides the intent to do something on the\n            client side, being able to function as an opt-in for CSR content to \\\"overwrite\\\" SSR content.\n            The next and final example will show how this should be done.\n        \"</p>\n    }\n}\n\n#[component]\nfn WasmBindgenDirectFixed() -> impl IntoView {\n    let code = r#\"#[component]\nfn CodeInner(code: String, lang: String) -> impl IntoView {\n    let (inner, set_inner) = signal(String::new());\n    #[cfg(feature = \"ssr\")]\n    {\n        set_inner.set(html_escape::encode_text(&code).into_owned());\n    }\n    #[cfg(not(feature = \"ssr\"))]\n    {\n        let result = crate::hljs::highlight(code, lang);\n        Effect::new(move |_| {\n            if let Some(r) = result.clone() { set_inner.set(r) }\n        });\n    }\n    view! {\n        <pre><code inner_html=inner></code></pre>\n    }\n}\"#\n    .to_string();\n    let lang = \"rust\".to_string();\n    provide_context(InnerEffect);\n\n    view! {\n        <h2>\"Corrected example using signal + effect (again).\"</h2>\n        <CodeDemoWasmInner/>\n        <p>\"\n            Since the previous example didn't quite get everything working due to the component here providing\n            different content between SSR and CSR, using client side signal and effect can opt-in the\n            difference to overwrite the SSR rendering when hydration is complete.  This is pretty much the\n            identical approach as example 5 as it is the idiomatic solution.  The improved version of the code\n            rendering component from the previous example may look something like the following:\n        \"</p>\n        <CodeInner code lang/>\n        <p>\"\n            With the use of effects, the expected final rendering after hydration and under CSR will be the\n            highlighted version as expected.  As part of trial and error, the author previously tried to\n            workaround this issue by using events via \"<code>\"web_sys\"</code>\" hack around signal, but again,\n            using effects like so is a lot better for this particular library.\n        \"</p>\n        <p>\"\n            Given the difference between CSR and SSR, the two different renderings are disambiguated via the\n            use of \"<code>\"[cfg(feature = ...)]\"</code>\" for the available behavior.  If there is a\n            corresponding API to provided highlighting markup under SSR, this feature gating would be managed\n            at the library level and the component would simply call the \"<code>\"highlight\"</code>\" function\n            directly, resulting in both SSR/CSR rendering being fully isomorphic even with JavaScript disabled\n            on the client.\n        \"</p>\n        <p>\"\n            To include the output of JavaScript code for SSR may be achieved in any of the following ways:\n        \"</p>\n        <ul>\n            <li>\"\n                Run a JavaScript code in some JavaScript runtime such as Node.js, SpiderMonkey or Deno with\n                the input, and return the collected output.\n            \"</li>\n            <li>\"\n                Use a JavaScript engine as above but more directly through some kind of Rust bindings through\n                packages such as \"<code>\"rusty_v8\"</code>\" or \"<code>\"mozjs\"</code>\".\n            \"</li>\n            <li>\"\n                Or go the full WASM route - compile the required JavaScript into WASM and use that through\n                Wasmtime on the server.\n            \"</li>\n        </ul>\n        <p>\"\n            All of the above are very much outside the scope of this demo which is already showing the too\n            many ways to include JavaScript into a Leptos project.\n        \"</p>\n    }\n}\n"
  },
  {
    "path": "examples/axum_js_ssr/src/consts.rs",
    "content": "// Example programs from the Rust Programming Language Book\n\npub const CH03_05A: &str = r#\"fn main() {\n    let number = 3;\n\n    if number < 5 {\n        println!(\"condition was true\");\n    } else {\n        println!(\"condition was false\");\n    }\n}\n\n\n\n\n\"#;\n\n// For some reason, swapping the code examples \"fixes\" example 6.  It\n// might have something to do with the lower complexity of highlighting\n// a shorter example.  Anyway, including extra newlines for the shorter\n// example to match with the longer in order to avoid reflowing the\n// table during the async resource loading for CSR.\n\npub const CH05_02A: &str = r#\"fn main() {\n    let width1 = 30;\n    let height1 = 50;\n\n    println!(\n        \"The area of the rectangle is {} square pixels.\",\n        area(width1, height1)\n    );\n}\n\nfn area(width: u32, height: u32) -> u32 {\n    width * height\n}\n\"#;\n\npub const LEPTOS_HYDRATED: &str = \"_leptos_hydrated\";\n"
  },
  {
    "path": "examples/axum_js_ssr/src/hljs.rs",
    "content": "#[cfg(not(feature = \"ssr\"))]\nmod csr {\n    use gloo_utils::format::JsValueSerdeExt;\n    use js_sys::{\n        Object,\n        Reflect::{get, set},\n    };\n    use wasm_bindgen::{prelude::wasm_bindgen, JsValue};\n\n    #[wasm_bindgen(\n        module = \"/node_modules/@highlightjs/cdn-assets/es/highlight.min.js\"\n    )]\n    extern \"C\" {\n        type HighlightOptions;\n\n        #[wasm_bindgen(catch, js_namespace = defaultMod, js_name = highlight)]\n        fn highlight_lang(\n            code: String,\n            options: Object,\n        ) -> Result<Object, JsValue>;\n\n        #[wasm_bindgen(js_namespace = defaultMod, js_name = highlightAll)]\n        pub fn highlight_all();\n    }\n\n    // Keeping the `ignoreIllegals` argument out of the default case, and since there is no optional arguments\n    // in Rust, this will have to be provided in a separate function (e.g. `highlight_ignore_illegals`), much\n    // like how `web_sys` does it for the browser APIs.  For simplicity, only the highlighted HTML code is\n    // returned on success, and None on error.\n    pub fn highlight(code: String, lang: String) -> Option<String> {\n        let options = js_sys::Object::new();\n        set(&options, &\"language\".into(), &lang.into())\n            .expect(\"failed to assign lang to options\");\n        highlight_lang(code, options)\n            .map(|result| {\n                let value = get(&result, &\"value\".into())\n                    .expect(\"HighlightResult failed to contain the value key\");\n                value.into_serde().expect(\"Value should have been a string\")\n            })\n            .ok()\n    }\n}\n\n#[cfg(feature = \"ssr\")]\nmod ssr {\n    // noop under ssr\n    pub fn highlight_all() {}\n\n    // TODO see if there is a Rust-based solution that will enable isomorphic rendering for this feature.\n    // the current (disabled) implementation simply calls html_escape.\n    // pub fn highlight(code: String, _lang: String) -> Option<String> {\n    //     Some(html_escape::encode_text(&code).into_owned())\n    // }\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub use csr::*;\n#[cfg(feature = \"ssr\")]\npub use ssr::*;\n"
  },
  {
    "path": "examples/axum_js_ssr/src/lib.rs",
    "content": "pub mod api;\npub mod app;\npub mod consts;\npub mod hljs;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n    use consts::LEPTOS_HYDRATED;\n    use std::panic;\n    panic::set_hook(Box::new(|info| {\n        // this custom hook will call out to show the usual error log at\n        // the console while also attempt to update the UI to indicate\n        // a restart of the application is required to continue.\n        console_error_panic_hook::hook(info);\n        let window = leptos::prelude::window();\n        if !matches!(\n            js_sys::Reflect::get(&window, &wasm_bindgen::JsValue::from_str(LEPTOS_HYDRATED)),\n            Ok(t) if t == true\n        ) {\n            let document = leptos::prelude::document();\n            let _ = document.query_selector(\"#reset\").map(|el| {\n                el.map(|el| {\n                    el.set_class_name(\"panicked\");\n                })\n            });\n            let _ = document.query_selector(\"#notice\").map(|el| {\n                el.map(|el| {\n                    el.set_class_name(\"panicked\");\n                })\n            });\n        }\n    }));\n    leptos::mount::hydrate_body(App);\n\n    let window = leptos::prelude::window();\n    js_sys::Reflect::set(\n        &window,\n        &wasm_bindgen::JsValue::from_str(LEPTOS_HYDRATED),\n        &wasm_bindgen::JsValue::TRUE,\n    )\n    .expect(\"error setting hydrated status\");\n    let event = web_sys::Event::new(LEPTOS_HYDRATED)\n        .expect(\"error creating hydrated event\");\n    let document = leptos::prelude::document();\n    document\n        .dispatch_event(&event)\n        .expect(\"error dispatching hydrated event\");\n    leptos::logging::log!(\"dispatched hydrated event\");\n}\n"
  },
  {
    "path": "examples/axum_js_ssr/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\nmod latency {\n    use std::sync::{Mutex, OnceLock};\n    pub static LATENCY: OnceLock<\n        Mutex<std::iter::Cycle<std::slice::Iter<'_, u64>>>,\n    > = OnceLock::new();\n    pub static ES_LATENCY: OnceLock<\n        Mutex<std::iter::Cycle<std::slice::Iter<'_, u64>>>,\n    > = OnceLock::new();\n}\n\n#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::{\n        body::Body,\n        extract::Request,\n        http::{\n            header::{self, HeaderValue},\n            StatusCode,\n        },\n        middleware::{self, Next},\n        response::{IntoResponse, Response},\n        routing::get,\n        Router,\n    };\n    use axum_js_ssr::app::*;\n    use http_body_util::BodyExt;\n    use leptos::{logging::log, prelude::*};\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n\n    latency::LATENCY.get_or_init(|| [0, 4, 40, 400].iter().cycle().into());\n    latency::ES_LATENCY.get_or_init(|| [0].iter().cycle().into());\n    // Having the ES_LATENCY (a cycle of latency for the loading of the es\n    // module) in an identical cycle as LATENCY (for the standard version)\n    // adversely influences the intended demo, as this ultimately delays\n    // hydration when set too high which can cause panic under every case.\n    // If you want to test the effects of the delay just modify the list of\n    // values for the desired cycle of delays.\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let leptos_options = conf.leptos_options;\n    // Generate the list of routes in your Leptos App\n    let routes = generate_route_list(App);\n\n    async fn highlight_js() -> impl IntoResponse {\n        (\n            [(header::CONTENT_TYPE, \"text/javascript\")],\n            include_str!(\n                \"../node_modules/@highlightjs/cdn-assets/highlight.min.js\"\n            ),\n        )\n    }\n\n    async fn latency_for_highlight_js(\n        req: Request,\n        next: Next,\n    ) -> Result<impl IntoResponse, (StatusCode, String)> {\n        let uri_parts = &mut req.uri().path().rsplit('/');\n\n        let is_highlightjs = uri_parts.next() == Some(\"highlight.min.js\");\n        let es = uri_parts.next() == Some(\"es\");\n        let module_type = if es { \"es module \" } else { \"standard \" };\n        let res = next.run(req).await;\n        if is_highlightjs {\n            // additional processing if the filename is the test subject\n            let (mut parts, body) = res.into_parts();\n            let bytes = body\n                .collect()\n                .await\n                .map_err(|err| {\n                    (\n                        StatusCode::BAD_REQUEST,\n                        format!(\"error reading body: {err}\"),\n                    )\n                })?\n                .to_bytes();\n            let latency = if es {\n                &latency::ES_LATENCY\n            } else {\n                &latency::LATENCY\n            };\n\n            let delay = match latency\n                .get()\n                .expect(\"latency cycle wasn't set up\")\n                .try_lock()\n            {\n                Ok(ref mut mutex) => {\n                    *mutex.next().expect(\"cycle always has next\")\n                }\n                Err(_) => 0,\n            };\n\n            // inject the logging of the delay used into the target script\n            log!(\n                \"loading {module_type}highlight.min.js with latency of \\\n                 {delay} ms\"\n            );\n            let js_log = format!(\n                \"\\nconsole.log('loaded {module_type}highlight.js with a \\\n                 minimum latency of {delay} ms');\"\n            );\n            tokio::time::sleep(std::time::Duration::from_millis(delay)).await;\n\n            let bytes = [bytes, js_log.into()].concat();\n            let length = bytes.len();\n            let body = Body::from(bytes);\n\n            // Provide the bare minimum set of headers to avoid browser cache.\n            parts.headers = header::HeaderMap::from_iter(\n                [\n                    (\n                        header::CONTENT_TYPE,\n                        HeaderValue::from_static(\"text/javascript\"),\n                    ),\n                    (header::CONTENT_LENGTH, HeaderValue::from(length)),\n                ]\n                .into_iter(),\n            );\n            Ok(Response::from_parts(parts, body))\n        } else {\n            Ok(res)\n        }\n    }\n\n    let app = Router::new()\n        .route(\"/highlight.min.js\", get(highlight_js))\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .layer(middleware::from_fn(latency_for_highlight_js))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    log!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "examples/axum_js_ssr/style/main.scss",
    "content": "html, body {\n    margin: 0;\n    padding: 0;\n    font-family: sans-serif;\n    height: 100vh;\n    overflow: hidden;\n}\n\nbody {\n    display: flex;\n    flex-flow: row nowrap;\n}\n\nnav {\n    min-width: 17em;\n    height: 100vh;\n    counter-reset: example-counter 0;\n    list-style-type: none;\n    list-style-position: outside;\n    overflow: auto;\n}\n\nnav a {\n    display: block;\n    padding: 0.5em 2em;\n    text-decoration: none;\n}\n\nnav a small {\n    display: block;\n}\n\nnav a.example::before {\n    counter-reset: subexample-counter 0;\n    counter-increment: example-counter 1;\n    content: counter(example-counter) \". \";\n}\n\nnav a.subexample::before {\n    counter-increment: subexample-counter 1;\n    content: counter(example-counter) \".\" counter(subexample-counter) \" \";\n}\n\ndiv#notice {\n    display: none;\n}\n\nmain div#notice.panicked {\n    position: sticky;\n    top: 0;\n    padding: 0.5em 2em;\n    display: block;\n}\n\nmain {\n    width: 100%;\n    overflow: auto;\n}\n\nmain article {\n    max-width: 60em;\n    margin: 0 1em;\n    padding: 0 1em;\n}\n\nmain p, main li {\n    line-height: 1.3em;\n}\n\nmain li pre code, main div pre code {\n    display: block;\n    line-height: normal;\n}\n\nmain ol, main ul {\n    padding-left: 2em;\n}\n\nh2>code, p>code, li>code {\n    border-radius: 3px;\n    padding: 2px;\n}\n\nli pre code, div pre code {\n    margin: 0 !important;\n    padding: 0 !important;\n}\n\n#code-demo {\n    overflow-x: auto;\n}\n\n#code-demo table {\n    width: 50em;\n    margin: auto;\n}\n\n#code-demo table td {\n    vertical-align: top;\n}\n\n#code-demo table code {\n    display: block;\n    padding: 1em;\n}\n\n@media (prefers-color-scheme: light) {\n    nav {\n        background: #f7f7f7;\n    }\n\n    nav a {\n        color: #000;\n    }\n\n    nav a[aria-current=\"page\"] {\n        background-color: #e0e0e0;\n    }\n\n    nav a:hover, h2>code, p>code, li>code  {\n        background-color: #e7e7e7;\n    }\n\n    nav a.panicked, main div#notice.panicked {\n        background: #fdd;\n    }\n\n    main div#notice.panicked a {\n        color: #000;\n    }\n\n    nav a.section {\n        border-bottom: 1px solid #777;\n    }\n}\n\n@media (prefers-color-scheme: dark) {\n    nav {\n        background: #080808;\n    }\n\n    nav a {\n        color: #fff;\n    }\n\n    nav a[aria-current=\"page\"] {\n        background-color: #3f3f3f;\n    }\n\n    nav a:hover, h2>code, p>code, li>code  {\n        background-color: #383838;\n    }\n\n    nav a.panicked, main div#notice.panicked {\n        background: #733;\n    }\n\n    main div#notice.panicked a {\n        color: #fff;\n    }\n\n    nav a.section {\n        border-bottom: 1px solid #888;\n    }\n}\n\n// Just include the raw style as-is because I can't find a quick and easy way to import them just for the\n// appropriate media type...\npre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}\n@media (prefers-color-scheme: light){.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}}\n@media (prefers-color-scheme: dark){.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}}\n"
  },
  {
    "path": "examples/cargo-make/cargo-leptos-compress.toml",
    "content": "[tasks.make-target-site-dir]\ncommand = \"mkdir\"\nargs = [\"-p\", \"target/site\"]\n\n[tasks.install-cargo-leptos]\ninstall_crate = { crate_name = \"cargo-leptos\", binary = \"cargo-leptos\", test_arg = \"--help\" }\n\n[tasks.cargo-leptos-e2e]\ncommand = \"cargo\"\nargs = [\"leptos\", \"end-to-end\"]\n\n[tasks.build]\nclear = true\ncommand = \"cargo\"\ndependencies = [\"make-target-site-dir\"]\nargs = [\"leptos\", \"build\", \"--release\", \"-P\"]\n\n[tasks.check]\nclear = true\ndependencies = [\"check-debug\", \"check-release\"]\n\n[tasks.check-debug]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"clippy\"]\n\n[tasks.check-release]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"clippy\", \"--release\"]\n\n[tasks.start-client]\ndependencies = [\"install-cargo-leptos\"]\ncommand = \"cargo\"\nargs = [\"leptos\", \"watch\", \"--release\", \"-P\"]\n"
  },
  {
    "path": "examples/cargo-make/cargo-leptos-split-webdriver-test.toml",
    "content": "extend = [\n    { path = \"./cargo-leptos.toml\" },\n    { path = \"../cargo-make/webdriver.toml\" },\n]\n\n[tasks.integration-test]\ndependencies = [\n    \"install-cargo-leptos\",\n    \"start-webdriver\",\n    \"cargo-leptos-e2e-split\",\n]\n"
  },
  {
    "path": "examples/cargo-make/cargo-leptos-test.toml",
    "content": "extend = { path = \"./cargo-leptos.toml\" }\n\n[tasks.integration-test]\ndependencies = [\"install-cargo-leptos\", \"cargo-leptos-e2e\"]\n"
  },
  {
    "path": "examples/cargo-make/cargo-leptos-webdriver-test.toml",
    "content": "extend = [\n    { path = \"./cargo-leptos.toml\" },\n    { path = \"../cargo-make/webdriver.toml\" },\n]\n\n[tasks.integration-test]\ndependencies = [\"install-cargo-leptos\", \"start-webdriver\", \"cargo-leptos-e2e\"]\n"
  },
  {
    "path": "examples/cargo-make/cargo-leptos.toml",
    "content": "[tasks.cargo-all-features]\ninstall_script = '''\ncargo install cargo-all-features\n'''\n\n[tasks.install-cargo-leptos]\ninstall_crate = { crate_name = \"cargo-leptos\", binary = \"cargo-leptos\", test_arg = \"--help\" }\nargs = [\"--locked\"]\n\n[tasks.cargo-leptos-e2e]\ncommand = \"cargo\"\nargs = [\"leptos\", \"end-to-end\"]\n\n[tasks.cargo-leptos-e2e-split]\ncommand = \"cargo\"\nargs = [\"leptos\", \"end-to-end\", \"--split\"]\n\n[tasks.build]\nclear = true\ncommand = \"cargo\"\nargs = [\"leptos\", \"build\"]\n\n[tasks.check]\nclear = true\ndependencies = [\"check-debug\", \"check-release\"]\n\n[tasks.check-debug]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"clippy\"]\n\n[tasks.check-release]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"clippy\", \"--release\"]\n\n[tasks.start-client]\ndependencies = [\"install-cargo-leptos\"]\ncommand = \"cargo\"\nargs = [\"leptos\", \"watch\"]\n"
  },
  {
    "path": "examples/cargo-make/clean.toml",
    "content": "[tasks.clean]\ndependencies = [\n  \"clean-cargo\",\n  \"clean-trunk\",\n  \"clean-node_modules\",\n  \"clean-playwright\",\n  \"clean-pkg\",\n]\n\n[tasks.clean-cargo]\nscript = '''\nfind . -type d -name target | xargs rm -rf\n'''\n\n[tasks.clean-trunk]\nscript = '''\nfind . -type d -name dist | xargs rm -rf\n'''\n\n[tasks.clean-node_modules]\nscript = '''\nproject_dir=${PWD##*/}\nif [ \"$project_dir\" != \"todomvc\" ]; then\n  find . -type d -name node_modules | xargs rm -rf\nfi\n'''\n\n[tasks.clean-playwright]\nscript = '''\nfind . -name playwright-report -name playwright -name test-results | xargs rm -rf\n'''\n\n[tasks.clean-pkg]\nscript = '''\nfind . -type d -name pkg | xargs rm -rf\n'''\n"
  },
  {
    "path": "examples/cargo-make/client-process.toml",
    "content": "[tasks.start-client]\n\n[tasks.stop-client]\ncondition = { env_set = [\"CLIENT_PROCESS_NAME\"] }\nscript = '''\n  if pidof -q ${CLIENT_PROCESS_NAME}; then\n    echo \"  Stopping ${CLIENT_PROCESS_NAME}\"\n    pkill -ef ${CLIENT_PROCESS_NAME}\n  else\n    echo \"  ${CLIENT_PROCESS_NAME} is already stopped\"\n  fi\n'''\n\n[tasks.client-status]\ncondition = { env_set = [\"CLIENT_PROCESS_NAME\"] }\nscript = '''\n  if pidof -q ${CLIENT_PROCESS_NAME}; then\n    echo \"  ${CLIENT_PROCESS_NAME} is up\"\n  else\n    echo \"  ${CLIENT_PROCESS_NAME} is not running\"\n  fi\n'''\n\n[tasks.maybe-start-client]\ncondition = { env_set = [\"CLIENT_PROCESS_NAME\"] }\nscript = '''\n  if pidof -q ${CLIENT_PROCESS_NAME}; then\n    echo \"  ${CLIENT_PROCESS_NAME} is already started\"\n  else\n    echo \"  Starting ${CLIENT_PROCESS_NAME}\"\n    if [ -n \"${SPAWN_CLIENT_PROCESS}\" ];then\n      echo \"Spawning process...\"\n      cargo make start-client ${@} &\n    else\n      cargo make start-client ${@}\n    fi\n  fi\n'''\n"
  },
  {
    "path": "examples/cargo-make/compile.toml",
    "content": "[tasks.cargo-all-features]\ninstall_script = '''\ncargo install cargo-all-features\n'''\n\n[tasks.build]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"build\"]\n"
  },
  {
    "path": "examples/cargo-make/deno-build.toml",
    "content": "[tasks.cargo-all-features]\ninstall_script = '''\ncargo install cargo-all-features\n'''\n\n[tasks.build]\ninstall_crate = { crate_name = \"wasm-pack\", binary = \"wasm-pack\", test_arg = \"--help\" }\nclear = true\ncommand = \"deno\"\nargs = [\"task\", \"build\"]\n\n[tasks.start-client]\ninstall_crate = { crate_name = \"wasm-pack\", binary = \"wasm-pack\", test_arg = \"--help\" }\ncommand = \"deno\"\nargs = [\"task\", \"start\"]\n\n[tasks.check]\nclear = true\ndependencies = [\"check-debug\", \"check-release\"]\n\n[tasks.check-debug]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"clippy\"]\n\n[tasks.check-release]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"clippy\", \"--release\"]\n"
  },
  {
    "path": "examples/cargo-make/lint.toml",
    "content": "[tasks.cargo-all-features]\ninstall_script = '''\ncargo install cargo-all-features\n'''\n\n[tasks.lint]\ndependencies = [\"check-format-flow\", \"clippy-each-feature\"]\n\n[tasks.clippy-each-feature]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"clippy\", \"--no-deps\", \"--\", \"-D\", \"warnings\"]\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../../\" }\nargs = [\"fmt\", \"--\", \"--check\", \"--config-path\", \"${LEPTOS_PROJECT_DIRECTORY}\"]\n"
  },
  {
    "path": "examples/cargo-make/main.toml",
    "content": "extend = [\n  { path = \"./compile.toml\" },\n  { path = \"./clean.toml\" },\n  { path = \"./lint.toml\" },\n  { path = \"./node.toml\" },\n  { path = \"./process.toml\" },\n]\n\n# CI Stages\n\n[tasks.ci]\ndependencies = [\"prepare\", \"lint\", \"test-flow\", \"integration-test\"]\n\n[tasks.prepare]\ndependencies = [\"setup-node\"]\n\n[tasks.integration-test]\n\n# Support Local Runs\n\n[tasks.ci-clean]\ndependencies = [\"ci\", \"clean\"]\n\n[tasks.check-clean]\ndependencies = [\"check\", \"clean\"]\n\n[tasks.build-clean]\ndependencies = [\"build\", \"clean\"]\n\n# ALIASES\n\n[tasks.verify-flow]\nalias = \"ci\"\n\n[tasks.t]\ndependencies = [\"test-flow\"]\n\n[tasks.it]\nalias = \"integration-test\"\n"
  },
  {
    "path": "examples/cargo-make/node.toml",
    "content": "[tasks.setup-node]\ndescription = \"Install node dependencies and playwright browsers\"\nenv = { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = \"1\" }\nscript = '''\nBOLD=\"\\e[1m\"\nGREEN=\"\\e[0;32m\"\nRED=\"\\e[0;31m\"\nRESET=\"\\e[0m\"\n\nproject_dir=$CARGO_MAKE_WORKING_DIRECTORY\n\n# Discover commands\nif command -v pnpm; then\n    NODE_CMD=pnpm\n    PLAYWRIGHT_CMD=pnpm\nelif command -v npm; then\n    NODE_CMD=npm\n    PLAYWRIGHT_CMD=npx\nelse\n    echo \"${RED}${BOLD}ERROR${RESET} - pnpm or npm is required by this task\"\n    exit 1\nfi\n\n# Install node dependencies\nfor node_path in $(find . -name package.json -not -path '*/node_modules/*')\ndo\n  node_dir=$(dirname $node_path)\n  echo Install node dependencies for $node_dir\n  cd $node_dir\n  ${NODE_CMD} install\n  cd ${project_dir}\ndone\n\n# Install playwright browsers\nfor pw_path in $(find . -name playwright.config.ts)\ndo\n  pw_dir=$(dirname $pw_path)\n  echo Install playwright browsers for $pw_dir\n  cd $pw_dir\n  ${PLAYWRIGHT_CMD} playwright install\n  cd $project_dir\ndone\n'''\n"
  },
  {
    "path": "examples/cargo-make/playwright-test.toml",
    "content": "extend = [{ path = \"../cargo-make/playwright.toml\" }]\n\n[tasks.integration-test]\ndependencies = [\"test-playwright-autostart\"]\n"
  },
  {
    "path": "examples/cargo-make/playwright-trunk-test.toml",
    "content": "extend = [\n  { path = \"../cargo-make/playwright.toml\" },\n  { path = \"../cargo-make/trunk_server.toml\" },\n]\n\n[tasks.integration-test]\ndependencies = [\"build\", \"start-client\", \"test-playwright\"]\ndescription = \"Run integration test with automated start and stop of processes\"\n"
  },
  {
    "path": "examples/cargo-make/playwright.toml",
    "content": "[tasks.test-playwright-autostart]\ncommand = \"npm\"\nargs = [\"run\", \"e2e:auto-start\"]\n\n[tasks.test-playwright]\nscript = '''\nBOLD=\"\\e[1m\"\nGREEN=\"\\e[0;32m\"\nRED=\"\\e[0;31m\"\nRESET=\"\\e[0m\"\n\nproject_dir=$CARGO_MAKE_WORKING_DIRECTORY\n\n# Discover commands\nif command -v pnpm; then\n    PLAYWRIGHT_CMD=pnpm\nelif command -v npm; then\n    PLAYWRIGHT_CMD=npx\nelse\n    echo \"${RED}${BOLD}ERROR${RESET} - pnpm or npm is required by this task\"\n    exit 1\nfi\n\n# Run playwright command\nfor pw_path in $(find . -name playwright.config.ts)\ndo\n  pw_dir=$(dirname $pw_path)\n  cd $pw_dir\n  ${PLAYWRIGHT_CMD} playwright test\n  cd $project_dir\ndone\n'''\n\n[tasks.test-playwright-ui]\nscript = '''\nBOLD=\"\\e[1m\"\nGREEN=\"\\e[0;32m\"\nRED=\"\\e[0;31m\"\nRESET=\"\\e[0m\"\n\nproject_dir=$CARGO_MAKE_WORKING_DIRECTORY\n\n# Discover commands\nif command -v pnpm; then\n    PLAYWRIGHT_CMD=pnpm\nelif command -v npm; then\n    PLAYWRIGHT_CMD=npx\nelse\n    echo \"${RED}${BOLD}ERROR${RESET} - pnpm or npm is required by this task\"\n    exit 1\nfi\n\n# Run playwright command\nfor pw_path in $(find . -name playwright.config.ts)\ndo\n  pw_dir=$(dirname $pw_path)\n  cd $pw_dir\n  ${PLAYWRIGHT_CMD} playwright test --ui\n  cd $project_dir\ndone\n'''\n\n[tasks.test-playwright-report]\nscript = '''\nBOLD=\"\\e[1m\"\nGREEN=\"\\e[0;32m\"\nRED=\"\\e[0;31m\"\nRESET=\"\\e[0m\"\n\nproject_dir=$CARGO_MAKE_WORKING_DIRECTORY\n\n# Discover commands\nif command -v pnpm; then\n    PLAYWRIGHT_CMD=pnpm\nelif command -v npm; then\n    PLAYWRIGHT_CMD=npx\nelse\n    echo \"${RED}${BOLD}ERROR${RESET} - pnpm or npm is required by this task\"\n    exit 1\nfi\n\n# Run playwright command\nfor pw_path in $(find . -name playwright.config.ts)\ndo\n  pw_dir=$(dirname $pw_path)\n  cd $pw_dir\n  ${PLAYWRIGHT_CMD} playwright show-report\n  cd $project_dir\ndone\n'''\n\n# ALIASES\n\n[tasks.pw]\ndependencies = [\"test-playwright\"]\n\n[tasks.pw-ui]\ndependencies = [\"test-playwright-ui\"]\n\n[tasks.pw-report]\ndependencies = [\"test-playwright-report\"]\n"
  },
  {
    "path": "examples/cargo-make/process.toml",
    "content": "extend = [\n    { path = \"./client-process.toml\" },\n    { path = \"./server-process.toml\" },\n]\n\n[tasks.start]\ndependencies = [\"maybe-start-server\", \"maybe-start-client\"]\n\n[tasks.status]\ndependencies = [\"server-status\", \"client-status\"]\n\n[tasks.stop]\ndependencies = [\"stop-client\", \"stop-server\"]\n"
  },
  {
    "path": "examples/cargo-make/scripts/web-report.sh",
    "content": "#!/bin/bash\n\nset -emu\n\nBOLD=\"\\e[1m\"\nITALIC=\"\\e[3m\"\nYELLOW=\"\\e[1;33m\"\nBLUE=\"\\e[1;36m\"\nRESET=\"\\e[0m\"\n\nfunction web { #task: only include examples with web cargo-make configuration\n    print_header\n    print_crate_tags \"$@\"\n    print_footer\n}\n\nfunction all { #task: includes all examples\n    print_header\n    print_crate_tags \"all\"\n    print_footer\n}\n\nfunction print_header {\n    echo -e \"${YELLOW}Cargo Make Web Report${RESET}\"\n    echo\n    echo -e \"${ITALIC}Show how crates are configured to run and test web examples with cargo-make${RESET}\"\n    echo\n}\n\nfunction print_crate_tags {\n    local makefile_paths\n    makefile_paths=$(find_makefile_lines)\n\n    local start_path\n    start_path=$(pwd)\n\n    for path in $makefile_paths; do\n        cd \"$path\"\n\n        local crate_tags=\n\n        # Add cargo tags\n        while read -r line; do\n            case $line in\n            *\"cucumber\"*)\n                crate_tags=$crate_tags\"C\"\n                ;;\n            *\"fantoccini\"*)\n                crate_tags=$crate_tags\"F\"\n                ;;\n            *\"package.metadata.leptos\"*)\n                crate_tags=$crate_tags\"M\"\n                ;;\n            esac\n        done <\"./Cargo.toml\"\n\n        #Add makefile tags\n\n        local pw_count\n        pw_count=$(find . -name playwright.config.ts | wc -l)\n\n        while read -r line; do\n            case $line in\n            *\"cargo-make/wasm-test.toml\"*)\n                crate_tags=$crate_tags\"W\"\n                ;;\n            *\"cargo-make/playwright-test.toml\"*)\n                crate_tags=$crate_tags\"P\"\n                crate_tags=$crate_tags\"N\"\n                ;;\n            *\"cargo-make/playwright-trunk-test.toml\"*)\n                crate_tags=$crate_tags\"P\"\n                crate_tags=$crate_tags\"T\"\n                ;;\n            *\"cargo-make/trunk_server.toml\"*)\n                crate_tags=$crate_tags\"T\"\n                ;;\n            *\"cargo-make/cargo-leptos-webdriver-test.toml\"*)\n                crate_tags=$crate_tags\"L\"\n                ;;\n            *\"cargo-make/cargo-leptos-test.toml\"*)\n                crate_tags=$crate_tags\"L\"\n                if [ \"$pw_count\" -gt 0 ]; then\n                    crate_tags=$crate_tags\"P\"\n                fi\n                ;;\n            *\"cargo-make/cargo-leptos.toml\"*)\n                crate_tags=$crate_tags\"L\"\n                ;;\n            *\"cargo-make/deno-build.toml\"*)\n                crate_tags=$crate_tags\"D\"\n                ;;\n            esac\n        done <\"./Makefile.toml\"\n\n        # Sort tags\n        local keys\n        keys=$(echo \"$crate_tags\" | grep -o . | sort | tr -d \"\\n\")\n\n        # Find leptos projects that are not configured to build with cargo-leptos\n        keys=${keys//\"LM\"/\"L\"}\n\n        # Find leptos projects that are not configured to build with deno\n        keys=${keys//\"DM\"/\"D\"}\n\n        # Maybe print line\n        local crate_line=$path\n\n        if [ -n \"$crate_tags\" ]; then\n            local color=$YELLOW\n            case $keys in\n            *\"M\"*)\n                color=$BLUE\n                ;;\n            esac\n\n            crate_line=\"$crate_line ➤ ${color}$keys${RESET}\"\n            echo -e \"$crate_line\"\n        elif [ \"$#\" -gt 0 ]; then\n            crate_line=\"${BOLD}$crate_line${RESET}\"\n            echo -e \"$crate_line\"\n        fi\n\n        cd \"$start_path\"\n    done\n}\n\nfunction find_makefile_lines {\n    find . -name Makefile.toml -not -path '*/target/*' -not -path '*/node_modules/*' |\n        sed 's%./%%' |\n        sed 's%/Makefile.toml%%' |\n        grep -v Makefile.toml |\n        sort -u\n}\n\nfunction print_footer {\n    c=\"${BOLD}${YELLOW}C${RESET} = Cucumber Test Runner\"\n    d=\"${BOLD}${YELLOW}D${RESET} = Deno\"\n    f=\"${BOLD}${YELLOW}F${RESET} = Fantoccini WebDriver\"\n    l=\"${BOLD}${YELLOW}L${RESET} = Cargo Leptos\"\n    m=\"${BOLD}${BLUE}M${RESET} = Cargo Leptos Metadata Only (${ITALIC}ci is not configured to build with cargo-leptos or deno${RESET})\"\n    n=\"${BOLD}${YELLOW}N${RESET} = Node\"\n    p=\"${BOLD}${YELLOW}P${RESET} = Playwright Test\"\n    t=\"${BOLD}${YELLOW}T${RESET} = Trunk\"\n    w=\"${BOLD}${YELLOW}W${RESET} = WASM Test\"\n\n    echo\n    echo -e \"${ITALIC}Report Keys:${RESET}\\n $c\\n $d\\n $f\\n $l\\n $m\\n $n\\n $p\\n $t\\n $w\"\n    echo\n}\n\n###################\n# HELP\n###################\n\nfunction list_help_for {\n    local task=$1\n    grep -E \"^function.+ #$task\" \"$0\" |\n        sed 's/function/ /' |\n        sed -e \"s| { #$task: |~|g\" |\n        column -s\"~\" -t |\n        sort\n}\n\nfunction help { #help: show task descriptions\n    echo -e \"${BOLD}Usage:${RESET} ./$(basename \"$0\") <task> [options]\"\n    echo\n    echo \"Show the cargo-make configuration for web examples\"\n    echo\n    echo -e \"${BOLD}Tasks:${RESET}\"\n    list_help_for task\n    echo\n}\n\nTIMEFORMAT=\"./web-report.sh completed in %3lR\"\ntime \"${@:-all}\" # Show the report by default\n"
  },
  {
    "path": "examples/cargo-make/server-process.toml",
    "content": "[tasks.start-server]\n\n[tasks.stop-server]\ncondition = { env_set = [\"SERVER_PROCESS_NAME\"] }\nscript = '''\n  if pidof -q ${SERVER_PROCESS_NAME}; then\n    echo \"  Stopping ${SERVER_PROCESS_NAME}\"\n    pkill -ef ${SERVER_PROCESS_NAME}\n  else\n    echo \"  ${SERVER_PROCESS_NAME} is already stopped\"\n  fi\n'''\n\n[tasks.server-status]\ncondition = { env_set = [\"SERVER_PROCESS_NAME\"] }\nscript = '''\n  if pidof -q ${SERVER_PROCESS_NAME}; then\n    echo \"  ${SERVER_PROCESS_NAME} is up\"\n  else\n    echo \"  ${SERVER_PROCESS_NAME} is not running\"\n  fi\n'''\n\n[tasks.maybe-start-server]\ncondition = { env_set = [\"SERVER_PROCESS_NAME\"] }\nscript = '''\n  YELLOW=\"\\e[0;33m\"\n  RESET=\"\\e[0m\"\n\n  if pidof -q ${SERVER_PROCESS_NAME}; then\n    echo \"  ${SERVER_PROCESS_NAME} is already started\"\n  else\n    echo \"  Starting ${SERVER_PROCESS_NAME}\"\n    echo \"  ${YELLOW}>> Run cargo make stop to end process${RESET}\"\n    cargo make start-server ${@} & \n  fi\n'''\n"
  },
  {
    "path": "examples/cargo-make/trunk_server.toml",
    "content": "[env]\nCLIENT_PROCESS_NAME = \"trunk\"\n\n[tasks.build]\ncommand = \"trunk\"\nargs = [\"build\"]\n\n[tasks.start-client]\nscript = '''\ntrunk serve -q \"${@}\" &\n'''\n"
  },
  {
    "path": "examples/cargo-make/wasm-test.toml",
    "content": "[tasks.post-test]\ndependencies = [\"test-wasm\"]\n\n[tasks.test-wasm]\nenv = { CARGO_MAKE_WASM_TEST_ARGS = \"--headless --chrome\" }\ncommand = \"cargo\"\nargs = [\"make\", \"wasm-pack-test\"]\n"
  },
  {
    "path": "examples/cargo-make/webdriver.toml",
    "content": "[tasks.start-webdriver]\nscript = '''\n  BOLD=\"\\e[1m\"\n  GREEN=\"\\e[0;32m\"\n  RED=\"\\e[0;31m\"\n  RESET=\"\\e[0m\"\n\n  if command -v chromedriver; then\n    if pidof -q chromedriver; then\n      echo \"  chromedriver is already started\"\n    else\n      echo \"Starting chomedriver\"\n      chromedriver --port=4444 &\n    fi\n  else\n    echo \"${RED}${BOLD}ERROR${RESET} - chromedriver not found\"\n    exit 1\n  fi\n'''\n\n[tasks.stop-webdriver]\nscript = '''\n  if pidof -q chromedriver; then\n    echo \"  Stopping chromedriver\"\n    pkill -ef \"chromedriver\"\n  else\n    echo \"  chromedriver is already stopped\"\n  fi\n'''\n\n[tasks.webdriver-status]\nscript = '''\n  if pidof -q chromedriver; then\n    echo chromedriver is up\n  else\n    echo chromedriver is not running\n  fi\n'''\n"
  },
  {
    "path": "examples/counter/Cargo.toml",
    "content": "[package]\nname = \"counter\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\nopt-level = 'z'\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_log = \"1.0\"\nlog = \"0.4.22\"\nconsole_error_panic_hook = \"0.1.7\"\ngloo-timers = { version = \"0.3.0\", features = [\"futures\"] }\n\n[dev-dependencies]\nwasm-bindgen = \"0.2.93\"\nwasm-bindgen-test = \"0.3.42\"\nweb-sys = \"0.3.70\"\n"
  },
  {
    "path": "examples/counter/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/wasm-test.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/counter/README.md",
    "content": "# Leptos Counter Example\n\nThis example creates a simple counter in a client side rendered app with Rust and WASM!\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/counter/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <link data-trunk rel=\"rust\" data-wasm-opt=\"z\" />\n    <link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\" />\n  </head>\n  <body></body>\n</html>\n\n"
  },
  {
    "path": "examples/counter/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/counter/src/lib.rs",
    "content": "use leptos::prelude::*;\n\n/// A simple counter component.\n///\n/// You can use doc comments like this to document your component.\n#[component]\npub fn SimpleCounter(\n    /// The starting value for the counter\n    initial_value: i32,\n    /// The change that should be applied each time the button is clicked.\n    step: i32,\n) -> impl IntoView {\n    let (value, set_value) = signal(initial_value);\n\n    view! {\n        <div>\n            <button on:click=move |_| set_value.set(0)>\"Clear\"</button>\n            <button on:click=move |_| *set_value.write() -= step>\"-1\"</button>\n            <span>\"Value: \" {value} \"!\"</span>\n            <button on:click=move |_| set_value.update(|value| *value += step)>\"+1\"</button>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/counter/src/main.rs",
    "content": "use counter::SimpleCounter;\nuse leptos::prelude::*;\n\npub fn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(|| {\n        view! { <SimpleCounter initial_value=0 step=1/> }\n    })\n}\n"
  },
  {
    "path": "examples/counter/tests/web.rs",
    "content": "#![allow(dead_code)]\n\nuse counter::*;\nuse leptos::{mount::mount_to, prelude::*, task::tick};\nuse wasm_bindgen::JsCast;\nuse wasm_bindgen_test::*;\n\nwasm_bindgen_test_configure!(run_in_browser);\n\n#[wasm_bindgen_test]\nasync fn clear() {\n    let document = document();\n    let test_wrapper = document.create_element(\"section\").unwrap();\n    let _ = document.body().unwrap().append_child(&test_wrapper);\n\n    // start by rendering our counter and mounting it to the DOM\n    // note that we start at the initial value of 10\n    let _dispose = mount_to(\n        test_wrapper.clone().unchecked_into(),\n        || view! { <SimpleCounter initial_value=10 step=1/> },\n    );\n\n    // now we extract the buttons by iterating over the DOM\n    // this would be easier if they had IDs\n    let div = test_wrapper.query_selector(\"div\").unwrap().unwrap();\n    let clear = test_wrapper\n        .query_selector(\"button\")\n        .unwrap()\n        .unwrap()\n        .unchecked_into::<web_sys::HtmlElement>();\n\n    // now let's click the `clear` button\n    clear.click();\n\n    // the reactive system is built on top of the async system, so changes are not reflected\n    // synchronously in the DOM\n    // in order to detect the changes here, we'll just yield for a brief time after each change,\n    // allowing the effects that update the view to run\n    tick().await;\n\n    // now let's test the <div> against the expected value\n    // we can do this by testing its `outerHTML`\n    assert_eq!(div.outer_html(), {\n        // it's as if we're creating it with a value of 0, right?\n        let (value, _set_value) = signal(0);\n\n        // we can remove the event listeners because they're not rendered to HTML\n        view! {\n            <div>\n                <button>\"Clear\"</button>\n                <button>\"-1\"</button>\n                <span>\"Value: \" {value} \"!\"</span>\n                <button>\"+1\"</button>\n            </div>\n        }\n        // Leptos supports multiple backend renderers for HTML elements\n        // .into_view() here is just a convenient way of specifying \"use the regular DOM renderer\"\n        .into_view()\n        // views are lazy -- they describe a DOM tree but don't create it yet\n        // calling .build() will actually build the DOM elements\n        .build()\n        // .build() returned an ElementState, which is a smart pointer for\n        // a DOM element. So we can still just call .outer_html(), which access the outerHTML on\n        // the actual DOM element\n        .outer_html()\n    });\n\n    // There's actually an easier way to do this...\n    // We can just test against a <SimpleCounter/> with the initial value 0\n    assert_eq!(test_wrapper.inner_html(), {\n        let comparison_wrapper = document.create_element(\"section\").unwrap();\n        let _dispose = mount_to(\n            comparison_wrapper.clone().unchecked_into(),\n            || view! { <SimpleCounter initial_value=0 step=1/>},\n        );\n        comparison_wrapper.inner_html()\n    });\n}\n\n#[wasm_bindgen_test]\nasync fn inc() {\n    let document = document();\n    let test_wrapper = document.create_element(\"section\").unwrap();\n    let _ = document.body().unwrap().append_child(&test_wrapper);\n\n    let _dispose = mount_to(\n        test_wrapper.clone().unchecked_into(),\n        || view! { <SimpleCounter initial_value=0 step=1/> },\n    );\n\n    // You can do testing with vanilla DOM operations\n    let div = test_wrapper.query_selector(\"div\").unwrap().unwrap();\n    let clear = div\n        .first_child()\n        .unwrap()\n        .dyn_into::<web_sys::HtmlElement>()\n        .unwrap();\n    let dec = clear\n        .next_sibling()\n        .unwrap()\n        .dyn_into::<web_sys::HtmlElement>()\n        .unwrap();\n    let text = dec\n        .next_sibling()\n        .unwrap()\n        .dyn_into::<web_sys::HtmlElement>()\n        .unwrap();\n    let inc = text\n        .next_sibling()\n        .unwrap()\n        .dyn_into::<web_sys::HtmlElement>()\n        .unwrap();\n\n    inc.click();\n    inc.click();\n\n    tick().await;\n\n    assert_eq!(text.text_content(), Some(\"Value: 2!\".to_string()));\n\n    dec.click();\n    dec.click();\n    dec.click();\n    dec.click();\n\n    tick().await;\n\n    assert_eq!(text.text_content(), Some(\"Value: -2!\".to_string()));\n\n    clear.click();\n\n    tick().await;\n\n    assert_eq!(text.text_content(), Some(\"Value: 0!\".to_string()));\n\n    // Or you can test against a sample view!\n    assert_eq!(\n        div.outer_html(),\n        {\n            let (value, _) = signal(0);\n            view! {\n                <div>\n                    <button>\"Clear\"</button>\n                    <button>\"-1\"</button>\n                    <span>\"Value: \" {value} \"!\"</span>\n                    <button>\"+1\"</button>\n                </div>\n            }\n        }\n        .into_view()\n        .build()\n        .outer_html()\n    );\n\n    inc.click();\n\n    tick().await;\n\n    assert_eq!(\n        div.outer_html(),\n        {\n            // because we've clicked, it's as if the signal is starting at 1\n            let (value, _) = signal(1);\n            view! {\n                <div>\n                    <button>\"Clear\"</button>\n                    <button>\"-1\"</button>\n                    <span>\"Value: \" {value} \"!\"</span>\n                    <button>\"+1\"</button>\n                </div>\n            }\n        }\n        .into_view()\n        .build()\n        .outer_html()\n    );\n}\n"
  },
  {
    "path": "examples/counter_isomorphic/.gitignore",
    "content": ".leptos.kdl"
  },
  {
    "path": "examples/counter_isomorphic/Cargo.toml",
    "content": "[package]\nname = \"counter_isomorphic\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nactix-files = { version = \"0.6.6\", optional = true }\nactix-web = { version = \"4.8\", optional = true, features = [\"macros\"] }\nbroadcaster = \"1.0\"\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.30\"\nleptos = { path = \"../../leptos\" }\nleptos_actix = { path = \"../../integrations/actix\", optional = true }\nleptos_router = { path = \"../../router\" }\nlog = \"0.4.22\"\ngloo-net = { version = \"0.6.0\" }\nwasm-bindgen = \"0.2.93\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nsimple_logger = \"5.0\"\ntracing = { version = \"0.1.40\", optional = true }\nsend_wrapper = \"0.6.0\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:actix-files\",\n  \"dep:actix-web\",\n  \"dep:tracing\",\n  \"leptos/ssr\",\n  \"leptos_actix\",\n  \"leptos_router/ssr\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"actix-files\", \"actix-web\", \"leptos_actix\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"counter_isomorphic\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\n# When NOT using cargo-leptos this must be updated to \".\" or the counters will not work. The above warning still applies if you do switch to cargo-leptos later.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\n# style-file = \"src/styles/tailwind.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/counter_isomorphic/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/counter_isomorphic/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"counter_isomorphic\"\n"
  },
  {
    "path": "examples/counter_isomorphic/README.md",
    "content": "# Leptos Counter Isomorphic Example\n\nThis example demonstrates how to use a function isomorphically, to run a server side function from the browser and receive a result.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/counter_isomorphic/src/counters.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router, A},\n    StaticSegment,\n};\n#[cfg(feature = \"ssr\")]\nuse tracing::instrument;\n\n#[cfg(feature = \"ssr\")]\npub mod ssr_imports {\n    pub use broadcaster::BroadcastChannel;\n    pub use std::sync::atomic::{AtomicI32, Ordering};\n    use std::sync::LazyLock;\n\n    pub static COUNT: AtomicI32 = AtomicI32::new(0);\n\n    pub static COUNT_CHANNEL: LazyLock<BroadcastChannel<i32>> =\n        LazyLock::new(BroadcastChannel::<i32>::new);\n}\n\n#[server]\n#[cfg_attr(feature = \"ssr\", instrument)]\npub async fn get_server_count() -> Result<i32, ServerFnError> {\n    use ssr_imports::*;\n\n    Ok(COUNT.load(Ordering::Relaxed))\n}\n\n#[server]\n#[cfg_attr(feature = \"ssr\", instrument)]\npub async fn adjust_server_count(\n    delta: i32,\n    msg: String,\n) -> Result<i32, ServerFnError> {\n    use ssr_imports::*;\n\n    let new = COUNT.load(Ordering::Relaxed) + delta;\n    COUNT.store(new, Ordering::Relaxed);\n    _ = COUNT_CHANNEL.send(&new).await;\n    println!(\"message = {:?}\", msg);\n    Ok(new)\n}\n\n#[server]\n#[cfg_attr(feature = \"ssr\", instrument)]\npub async fn clear_server_count() -> Result<i32, ServerFnError> {\n    use ssr_imports::*;\n\n    COUNT.store(0, Ordering::Relaxed);\n    _ = COUNT_CHANNEL.send(&0).await;\n    Ok(0)\n}\n#[component]\npub fn Counters() -> impl IntoView {\n    view! {\n        <Router>\n            <header>\n                <h1>\"Server-Side Counters\"</h1>\n                <p>\"Each of these counters stores its data in the same variable on the server.\"</p>\n                <p>\n                    \"The value is shared across connections. Try opening this is another browser tab to see what I mean.\"\n                </p>\n            </header>\n            <nav>\n                <ul>\n                    <li>\n                        <A href=\"\">\"Simple\"</A>\n                    </li>\n                    <li>\n                        <A href=\"form\">\"Form-Based\"</A>\n                    </li>\n                    <li>\n                        <A href=\"multi\">\"Multi-User\"</A>\n                    </li>\n                </ul>\n            </nav>\n            <main>\n                <FlatRoutes fallback=|| \"Not found.\">\n                    <Route path=StaticSegment(\"\") view=Counter/>\n                    <Route path=StaticSegment(\"form\") view=FormCounter/>\n                    <Route path=StaticSegment(\"multi\") view=MultiuserCounter/>\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n// This is an example of \"single-user\" server functions\n// The counter value is loaded from the server, and re-fetches whenever\n// it's invalidated by one of the user's own actions\n// This is the typical pattern for a CRUD app\n#[component]\npub fn Counter() -> impl IntoView {\n    let dec = Action::new(|_: &()| adjust_server_count(-1, \"decing\".into()));\n    let inc = Action::new(|_: &()| adjust_server_count(1, \"incing\".into()));\n    let clear = Action::new(|_: &()| clear_server_count());\n    let counter = Resource::new(\n        move || {\n            (\n                dec.version().get(),\n                inc.version().get(),\n                clear.version().get(),\n            )\n        },\n        |_| get_server_count(),\n    );\n\n    view! {\n        <div>\n            <h2>\"Simple Counter\"</h2>\n            <p>\n                \"This counter sets the value on the server and automatically reloads the new value.\"\n            </p>\n            <ErrorBoundary fallback=|errors| move || format!(\"Error: {:#?}\", errors.get())>\n                <div>\n                    <button on:click=move |_| { clear.dispatch(()); }>\"Clear\"</button>\n                    <button on:click=move |_| { dec.dispatch(()); }>\"-1\"</button>\n                    <span>\"Value: \" <Suspense>{counter} \"!\"</Suspense></span>\n                    <button on:click=move |_| { inc.dispatch(()); }>\"+1\"</button>\n                </div>\n            </ErrorBoundary>\n        </div>\n    }\n}\n\n// This is the <Form/> counter\n// It uses the same invalidation pattern as the plain counter,\n// but uses HTML forms to submit the actions\n#[component]\npub fn FormCounter() -> impl IntoView {\n    // these struct names are auto-generated by #[server]\n    // they are just the PascalCased versions of the function names\n    let adjust = ServerAction::<AdjustServerCount>::new();\n    let clear = ServerAction::<ClearServerCount>::new();\n\n    let counter = Resource::new(\n        move || (adjust.version().get(), clear.version().get()),\n        |_| {\n            log::debug!(\"FormCounter running fetcher\");\n            get_server_count()\n        },\n    );\n    let value = move || {\n        log::debug!(\"FormCounter looking for value\");\n        counter.get().and_then(|n| n.ok()).unwrap_or(0)\n    };\n\n    view! {\n        <div>\n            <h2>\"Form Counter\"</h2>\n            <p>\n                \"This counter uses forms to set the value on the server. When progressively enhanced, it should behave identically to the “Simple Counter.”\"\n            </p>\n            <div>\n                // calling a server function is the same as POSTing to its API URL\n                // so we can just do that with a form and button\n                <ActionForm action=clear>\n                    <input type=\"submit\" value=\"Clear\"/>\n                </ActionForm>\n                // We can submit named arguments to the server functions\n                // by including them as input values with the same name\n                <ActionForm action=adjust>\n                    <input type=\"hidden\" name=\"delta\" value=\"-1\"/>\n                    <input type=\"hidden\" name=\"msg\" value=\"form value down\"/>\n                    <input type=\"submit\" value=\"-1\"/>\n                </ActionForm>\n                <span>\"Value: \" <Suspense>{value} \"!\"</Suspense></span>\n                <ActionForm action=adjust>\n                    <input type=\"hidden\" name=\"delta\" value=\"1\"/>\n                    <input type=\"hidden\" name=\"msg\" value=\"form value up\"/>\n                    <input type=\"submit\" value=\"+1\"/>\n                </ActionForm>\n            </div>\n        </div>\n    }\n}\n\n// This is a kind of \"multi-user\" counter\n// It relies on a stream of server-sent events (SSE) for the counter's value\n// Whenever another user updates the value, it will update here\n// This is the primitive pattern for live chat, collaborative editing, etc.\n#[component]\npub fn MultiuserCounter() -> impl IntoView {\n    let dec =\n        Action::new(|_: &()| adjust_server_count(-1, \"dec dec goose\".into()));\n    let inc =\n        Action::new(|_: &()| adjust_server_count(1, \"inc inc moose\".into()));\n    let clear = Action::new(|_: &()| clear_server_count());\n\n    #[cfg(not(feature = \"ssr\"))]\n    let multiplayer_value = {\n        use futures::StreamExt;\n        use send_wrapper::SendWrapper;\n\n        let mut source = SendWrapper::new(\n            gloo_net::eventsource::futures::EventSource::new(\"/api/events\")\n                .expect(\"couldn't connect to SSE stream\"),\n        );\n        let s = ReadSignal::from_stream_unsync(\n            source\n                .subscribe(\"message\")\n                .unwrap()\n                .map(|value| match value {\n                    Ok(value) => value\n                        .1\n                        .data()\n                        .as_string()\n                        .expect(\"expected string value\"),\n                    Err(_) => \"0\".to_string(),\n                }),\n        );\n\n        on_cleanup(move || source.take().close());\n        s\n    };\n\n    #[cfg(feature = \"ssr\")]\n    let (multiplayer_value, _) = signal(None::<i32>);\n\n    view! {\n        <div>\n            <h2>\"Multi-User Counter\"</h2>\n            <p>\n                \"This one uses server-sent events (SSE) to live-update when other users make changes.\"\n            </p>\n            <div>\n                <button on:click=move |_| { clear.dispatch(()); }>\"Clear\"</button>\n                <button on:click=move |_| { dec.dispatch(()); }>\"-1\"</button>\n                <span>\n                    \"Multiplayer Value: \" {move || multiplayer_value.get().unwrap_or_default()}\n                </span>\n                <button on:click=move |_| { inc.dispatch(()); }>\"+1\"</button>\n            </div>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/counter_isomorphic/src/lib.rs",
    "content": "pub mod counters;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::counters::Counters;\n\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n\n    leptos::mount::hydrate_body(Counters);\n}\n"
  },
  {
    "path": "examples/counter_isomorphic/src/main.rs",
    "content": "mod counters;\n\nuse crate::counters::*;\nuse actix_files::Files;\nuse actix_web::*;\nuse leptos_actix::{generate_route_list, LeptosRoutes};\n\n#[get(\"/api/events\")]\nasync fn counter_events() -> impl Responder {\n    use crate::counters::ssr_imports::*;\n    use futures::StreamExt;\n\n    let stream = futures::stream::once(async {\n        crate::counters::get_server_count().await.unwrap_or(0)\n    })\n    .chain(COUNT_CHANNEL.clone())\n    .map(|value| {\n        Ok(web::Bytes::from(format!(\n            \"event: message\\ndata: {value}\\n\\n\"\n        ))) as Result<web::Bytes>\n    });\n    HttpResponse::Ok()\n        .insert_header((\"Content-Type\", \"text/event-stream\"))\n        .streaming(stream)\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    use leptos::prelude::*;\n\n    // Setting this to None means we'll be using cargo-leptos and its env vars.\n    // when not using cargo-leptos None must be replaced with Some(\"Cargo.toml\")\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    println!(\"listening on http://{}\", &addr);\n\n    HttpServer::new(move || {\n        // Generate the list of routes in your Leptos App\n        let routes = generate_route_list(Counters);\n        let leptos_options = &conf.leptos_options;\n        let site_root = &leptos_options.site_root;\n\n        App::new()\n            .service(counter_events)\n            .leptos_routes(routes, {\n                let leptos_options = leptos_options.clone();\n                move || {\n                    view! {\n                        <!DOCTYPE html>\n                        <html lang=\"en\">\n                            <head>\n                                <meta charset=\"utf-8\"/>\n                                <meta\n                                    name=\"viewport\"\n                                    content=\"width=device-width, initial-scale=1\"\n                                />\n                                <AutoReload options=leptos_options.clone()/>\n                                <HydrationScripts options=leptos_options.clone()/>\n                            </head>\n                            <body>\n                                <Counters/>\n                            </body>\n                        </html>\n                    }\n            }})\n            .service(Files::new(\"/\", site_root.as_ref()))\n    })\n    .bind(&addr)?\n    .run()\n    .await\n}\n"
  },
  {
    "path": "examples/counter_url_query/Cargo.toml",
    "content": "[package]\nname = \"counter_url_query\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nleptos_router = { path = \"../../router\", features = [] }\nconsole_error_panic_hook = \"0.1.7\"\n\n[dev-dependencies]\nwasm-bindgen = \"0.2.93\"\nwasm-bindgen-test = \"0.3.42\"\nweb-sys = \"0.3.70\"\n"
  },
  {
    "path": "examples/counter_url_query/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/counter_url_query/README.md",
    "content": "# Leptos Query Counter Example\n\nThis example creates a simple counter whose state is persisted and synced in the url with query params.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/counter_url_query/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/counter_url_query/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/counter_url_query/src/lib.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::hooks::query_signal;\n\n/// A simple counter component.\n///\n/// You can use doc comments like this to document your component.\n#[component]\npub fn SimpleQueryCounter() -> impl IntoView {\n    let (count, set_count) = query_signal::<i32>(\"count\");\n    let clear = move |_| set_count.set(None);\n    let decrement = move |_| set_count.set(Some(count.get().unwrap_or(0) - 1));\n    let increment = move |_| set_count.set(Some(count.get().unwrap_or(0) + 1));\n\n    let (msg, set_msg) = query_signal::<String>(\"message\");\n    let update_msg = move |ev| {\n        let new_msg = event_target_value(&ev);\n        if new_msg.is_empty() {\n            set_msg.set(None);\n        } else {\n            set_msg.set(Some(new_msg));\n        }\n    };\n\n    view! {\n        <div>\n            <button on:click=clear>\"Clear\"</button>\n            <button on:click=decrement>\"-1\"</button>\n            <span>\"Value: \" {move || count.get().unwrap_or(0)} \"!\"</span>\n            <button on:click=increment>\"+1\"</button>\n\n            <br />\n\n            <input\n                prop:value=move || msg.get().unwrap_or_default()\n                on:input=update_msg\n            />\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/counter_url_query/src/main.rs",
    "content": "use counter_url_query::SimpleQueryCounter;\nuse leptos::prelude::*;\nuse leptos_router::components::Router;\n\npub fn main() {\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(|| {\n        view! {\n            <Router>\n                <SimpleQueryCounter/>\n            </Router>\n        }\n    })\n}\n"
  },
  {
    "path": "examples/counter_without_macros/Cargo.toml",
    "content": "[package]\nname = \"counter_without_macros\"\nversion = \"0.1.0\"\nedition = \"2021\"\nrust-version = \"1.75\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_error_panic_hook = \"0.1.7\"\n\n[dev-dependencies]\nwasm-bindgen = \"0.2\"\nwasm-bindgen-test = \"0.3.42\"\npretty_assertions = \"1.4\"\nrstest = \"0.22.0\"\n\n[dev-dependencies.web-sys]\nfeatures = [\"HtmlElement\", \"XPathResult\"]\nversion = \"0.3.70\"\n"
  },
  {
    "path": "examples/counter_without_macros/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/wasm-test.toml\" },\n  { path = \"../cargo-make/trunk_server.toml\" },\n]\n\n[tasks.build]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"build\"]\n\n[tasks.check]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\"all-features\", \"clippy\"]\n"
  },
  {
    "path": "examples/counter_without_macros/README.md",
    "content": "# Leptos Counter Example\n\nThis example is the same like the `counter` but it's written without using macros and can be build with stable Rust.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/counter_without_macros/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/counter_without_macros/src/lib.rs",
    "content": "use leptos::{\n    ev,\n    html::{button, div, span},\n    prelude::*,\n};\n\n/// A simple counter view.\n// A component is really just a function call: it runs once to create the DOM and reactive system\npub fn counter(initial_value: i32, step: u32) -> impl IntoView {\n    let count = RwSignal::new(Count::new(initial_value, step));\n    Effect::new(move |_| {\n        leptos::logging::log!(\"count = {:?}\", count.get());\n    });\n\n    // the function name is the same as the HTML tag name\n    div()\n        // children can be added with .child()\n        // this takes any type that implements IntoView as its argument\n        // for example, a string or an HtmlElement<_>\n        // it can also take an array of types that impl IntoView\n        // or a tuple of up to 26 objects that impl IntoView\n        .child((\n            button()\n                // typed events found in leptos::ev\n                // 1) prevent typos in event names\n                // 2) allow for correct type inference in callbacks\n                .on(ev::click, move |_| count.update(Count::clear))\n                .child(\"Clear\"),\n            button()\n                .on(ev::click, move |_| count.update(Count::decrease))\n                .child(\"-1\"),\n            span().child((\"Value: \", move || count.get().value(), \"!\")),\n            button()\n                .on(ev::click, move |_| count.update(Count::increase))\n                .child(\"+1\"),\n        ))\n}\n\n#[derive(Debug, Clone)]\npub struct Count {\n    value: i32,\n    step: i32,\n}\n\nimpl Count {\n    pub fn new(value: i32, step: u32) -> Self {\n        Count {\n            value,\n            step: step as i32,\n        }\n    }\n\n    pub fn value(&self) -> i32 {\n        leptos::logging::log!(\"value = {}\", self.value);\n        self.value\n    }\n\n    pub fn increase(&mut self) {\n        self.value += self.step;\n    }\n\n    pub fn decrease(&mut self) {\n        self.value += -self.step;\n    }\n\n    pub fn clear(&mut self) {\n        self.value = 0;\n    }\n}\n"
  },
  {
    "path": "examples/counter_without_macros/src/main.rs",
    "content": "use counter_without_macros::counter;\n\n/// Show the counter\npub fn main() {\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(|| counter(0, 1))\n}\n"
  },
  {
    "path": "examples/counter_without_macros/tests/business.rs",
    "content": "mod count {\n    use counter_without_macros::Count;\n    use pretty_assertions::assert_eq;\n    use rstest::rstest;\n\n    #[rstest]\n    #[case(-2, 1)]\n    #[case(-1, 1)]\n    #[case(0, 1)]\n    #[case(1, 1)]\n    #[case(2, 1)]\n    #[case(3, 2)]\n    #[case(4, 3)]\n    fn should_increase_count(#[case] initial_value: i32, #[case] step: u32) {\n        let mut count = Count::new(initial_value, step);\n        count.increase();\n        assert_eq!(count.value(), initial_value + step as i32);\n    }\n\n    #[rstest]\n    #[case(-2, 1)]\n    #[case(-1, 1)]\n    #[case(0, 1)]\n    #[case(1, 1)]\n    #[case(2, 1)]\n    #[case(3, 2)]\n    #[case(4, 3)]\n    #[trace]\n    fn should_decrease_count(#[case] initial_value: i32, #[case] step: u32) {\n        let mut count = Count::new(initial_value, step);\n        count.decrease();\n        assert_eq!(count.value(), initial_value - step as i32);\n    }\n\n    #[rstest]\n    #[case(-2, 1)]\n    #[case(-1, 1)]\n    #[case(0, 1)]\n    #[case(1, 1)]\n    #[case(2, 1)]\n    #[case(3, 2)]\n    #[case(4, 3)]\n    #[trace]\n    fn should_clear_count(#[case] initial_value: i32, #[case] step: u32) {\n        let mut count = Count::new(initial_value, step);\n        count.clear();\n        assert_eq!(count.value(), 0);\n    }\n}\n"
  },
  {
    "path": "examples/counter_without_macros/tests/web.rs",
    "content": "#![allow(dead_code)]\n\nuse counter_without_macros::counter;\nuse leptos::{prelude::*, task::tick};\nuse pretty_assertions::assert_eq;\nuse wasm_bindgen::JsCast;\nuse wasm_bindgen_test::*;\nuse web_sys::HtmlElement;\n\nwasm_bindgen_test_configure!(run_in_browser);\n\n#[wasm_bindgen_test]\nasync fn should_increment_counter() {\n    open_counter();\n\n    click_increment();\n    click_increment();\n\n    // reactive changes run asynchronously, so yield briefly before observing the DOM\n    tick().await;\n\n    assert_eq!(see_text(), Some(\"Value: 2!\".to_string()));\n}\n\n#[wasm_bindgen_test]\nasync fn should_decrement_counter() {\n    open_counter();\n\n    click_decrement();\n    click_decrement();\n\n    tick().await;\n\n    assert_eq!(see_text(), Some(\"Value: -2!\".to_string()));\n}\n\n#[wasm_bindgen_test]\nasync fn should_clear_counter() {\n    open_counter();\n\n    click_increment();\n    click_increment();\n\n    click_clear();\n\n    tick().await;\n\n    assert_eq!(see_text(), Some(\"Value: 0!\".to_string()));\n}\n\nfn open_counter() {\n    remove_existing_counter();\n    leptos::mount::mount_to_body(move || counter(0, 1));\n}\n\nfn remove_existing_counter() {\n    if let Some(counter) = document().query_selector(\"body div\").unwrap() {\n        counter.remove();\n    }\n}\n\nfn click_clear() {\n    click_text(\"Clear\");\n}\n\nfn click_decrement() {\n    click_text(\"-1\");\n}\n\nfn click_increment() {\n    click_text(\"+1\");\n}\n\nfn click_text(text: &str) {\n    find_by_text(text).click();\n}\n\nfn see_text() -> Option<String> {\n    find_by_text(\"Value: \").text_content()\n}\n\nfn find_by_text(text: &str) -> HtmlElement {\n    let xpath = format!(\"//*[text()='{}']\", text);\n    let document = document();\n    document\n        .evaluate(&xpath, &document)\n        .unwrap()\n        .iterate_next()\n        .unwrap()\n        .unwrap()\n        .dyn_into::<HtmlElement>()\n        .unwrap()\n}\n"
  },
  {
    "path": "examples/counters/.gitignore",
    "content": "# Support playwright testing\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\npnpm-lock.yaml"
  },
  {
    "path": "examples/counters/Cargo.toml",
    "content": "[package]\nname = \"counters\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_error_panic_hook = \"0.1.7\"\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.42\"\nwasm-bindgen = \"0.2.93\"\nweb-sys = \"0.3.70\"\n"
  },
  {
    "path": "examples/counters/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/wasm-test.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n    { path = \"../cargo-make/playwright-trunk-test.toml\" },\n]\n"
  },
  {
    "path": "examples/counters/README.md",
    "content": "# Leptos Counters Example\n\nThis example showcases a basic leptos app with many counters. It is a good example of how to setup a basic reactive app with signals and effects, and how to interact with browser events.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/counters/e2e/.gitignore",
    "content": "node_modules/\n/test-results/\n/playwright-report/\n/playwright/.cache/\n"
  },
  {
    "path": "examples/counters/e2e/package.json",
    "content": "{\n  \"private\": \"true\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.46.1\"\n  },\n  \"dependencies\": {\n    \"pnpm\": \"^10.28.2\"\n  }\n}\n"
  },
  {
    "path": "examples/counters/e2e/playwright.config.ts",
    "content": "import { defineConfig, devices } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://github.com/motdotla/dotenv\n */\n// require('dotenv').config();\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nexport default defineConfig({\n  testDir: \"./tests\",\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !process.env.DEV,\n  /* Retry on CI only */\n  retries: process.env.DEV ? 0 : 10,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.DEV ? 1 : 1,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: [[\"html\", { open: \"never\" }], [\"list\"]],\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    baseURL: \"http://127.0.0.1:8080\",\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: \"on-first-retry\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"chromium\",\n      use: { ...devices[\"Desktop Chrome\"] },\n    },\n\n    // {\n    //   name: \"firefox\",\n    //   use: { ...devices[\"Desktop Firefox\"] },\n    // },\n\n    // {\n    //   name: \"webkit\",\n    //   use: { ...devices[\"Desktop Safari\"] },\n    // },\n\n    /* Test against mobile viewports. */\n    // {\n    //   name: 'Mobile Chrome',\n    //   use: { ...devices['Pixel 5'] },\n    // },\n    // {\n    //   name: 'Mobile Safari',\n    //   use: { ...devices['iPhone 12'] },\n    // },\n\n    /* Test against branded browsers. */\n    // {\n    //   name: 'Microsoft Edge',\n    //   use: { ...devices['Desktop Edge'], channel: 'msedge' },\n    // },\n    // {\n    //   name: 'Google Chrome',\n    //   use: { ..devices['Desktop Chrome'], channel: 'chrome' },\n    // },\n  ],\n\n  /* Run your local dev server before starting the tests */\n  // webServer: {\n  //   command: \"cd ../ && trunk serve\",\n  //   url: \"http://127.0.0.1:8080\",\n  //   reuseExistingServer: false, //!process.env.CI,\n  // },\n});\n"
  },
  {
    "path": "examples/counters/e2e/tests/add_1k_counters.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { CountersPage } from \"./fixtures/counters_page\";\n\ntest.describe(\"Add 1000 Counters\", () => {\n  test(\"should increase the number of counters\", async ({ page }) => {\n    const ui = new CountersPage(page);\n\n    await Promise.all([\n      await ui.goto(),\n      await ui.addOneThousandCountersButton.waitFor(),\n    ]);\n\n    await ui.addOneThousandCounters();\n    await ui.addOneThousandCounters();\n    await ui.addOneThousandCounters();\n\n    await expect(ui.counters).toHaveText(\"3000\");\n  });\n});\n"
  },
  {
    "path": "examples/counters/e2e/tests/add_counter.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { CountersPage } from \"./fixtures/counters_page\";\n\ntest.describe(\"Add Counter\", () => {\n  test(\"should increase the number of counters\", async ({ page }) => {\n    const ui = new CountersPage(page);\n    await ui.goto();\n\n    await ui.addCounter();\n    await ui.addCounter();\n    await ui.addCounter();\n\n    await expect(ui.counters).toHaveText(\"3\");\n  });\n});\n"
  },
  {
    "path": "examples/counters/e2e/tests/clear_counters.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { CountersPage } from \"./fixtures/counters_page\";\n\ntest.describe(\"Clear Counters\", () => {\n  test(\"should reset the counts\", async ({ page }) => {\n    const ui = new CountersPage(page);\n    await ui.goto();\n\n    await ui.addCounter();\n    await ui.addCounter();\n    await ui.addCounter();\n\n    await ui.clearCounters();\n\n    await expect(ui.total).toHaveText(\"0\");\n    await expect(ui.counters).toHaveText(\"0\");\n  });\n});\n"
  },
  {
    "path": "examples/counters/e2e/tests/decrement_count.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { CountersPage } from \"./fixtures/counters_page\";\n\ntest.describe(\"Decrement Count\", () => {\n  test(\"should decrease the total count\", async ({ page }) => {\n    const ui = new CountersPage(page);\n    await ui.goto();\n    await ui.addCounter();\n\n    await ui.decrementCount();\n    await ui.decrementCount();\n    await ui.decrementCount();\n\n    await expect(ui.total).toHaveText(\"-3\");\n  });\n});\n"
  },
  {
    "path": "examples/counters/e2e/tests/enter_count.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { CountersPage } from \"./fixtures/counters_page\";\n\ntest.describe(\"Enter Count\", () => {\n  test(\"should increase the total count\", async ({ page }) => {\n    const ui = new CountersPage(page);\n    await ui.goto();\n    await ui.addCounter();\n\n    await ui.enterCount(\"5\");\n\n    await expect(ui.total).toHaveText(\"5\");\n    await expect(ui.counters).toHaveText(\"1\");\n  });\n\n  test(\"should decrease the total count\", async ({ page }) => {\n    const ui = new CountersPage(page);\n    await ui.goto();\n    await ui.addCounter();\n    await ui.addCounter();\n    await ui.addCounter();\n\n    await ui.enterCount(\"100\");\n    await ui.enterCount(\"100\", 1);\n    await ui.enterCount(\"100\", 2);\n    await ui.enterCount(\"50\", 1);\n\n    await expect(ui.total).toHaveText(\"250\");\n  });\n});\n"
  },
  {
    "path": "examples/counters/e2e/tests/fixtures/counters_page.ts",
    "content": "import { expect, Locator, Page } from \"@playwright/test\";\n\nexport class CountersPage {\n  readonly page: Page;\n  readonly addCounterButton: Locator;\n  readonly addOneThousandCountersButton: Locator;\n  readonly clearCountersButton: Locator;\n\n  readonly incrementCountButton: Locator;\n  readonly counterInput: Locator;\n  readonly decrementCountButton: Locator;\n  readonly removeCountButton: Locator;\n\n  readonly total: Locator;\n  readonly counters: Locator;\n\n  constructor(page: Page) {\n    this.page = page;\n\n    this.addCounterButton = page.locator(\"button\", { hasText: \"Add Counter\" });\n\n    this.addOneThousandCountersButton = page.locator(\"button\", {\n      hasText: \"Add 1000 Counters\",\n    });\n\n    this.clearCountersButton = page.locator(\"button\", {\n      hasText: \"Clear Counters\",\n    });\n\n    this.decrementCountButton = page.locator(\"button\", {\n      hasText: \"-1\",\n    });\n\n    this.incrementCountButton = page.locator(\"button\", {\n      hasText: \"+1\",\n    });\n\n    this.removeCountButton = page.locator(\"button\", {\n      hasText: \"x\",\n    });\n\n    this.total = page.getByTestId(\"total\");\n\n    this.counters = page.getByTestId(\"counters\");\n\n    this.counterInput = page.getByRole(\"textbox\");\n  }\n\n  async goto() {\n    await this.page.goto(\"/\");\n  }\n\n  async addCounter() {\n    await Promise.all([\n      this.addCounterButton.waitFor(),\n      this.addCounterButton.click(),\n    ]);\n  }\n\n  async addOneThousandCounters() {\n    this.addOneThousandCountersButton.click();\n  }\n\n  async decrementCount(index: number = 0) {\n    await Promise.all([\n      this.decrementCountButton.nth(index).waitFor(),\n      this.decrementCountButton.nth(index).click(),\n    ]);\n  }\n\n  async incrementCount(index: number = 0) {\n    await Promise.all([\n      this.incrementCountButton.nth(index).waitFor(),\n      this.incrementCountButton.nth(index).click(),\n    ]);\n  }\n\n  async clearCounters() {\n    await Promise.all([\n      this.clearCountersButton.waitFor(),\n      this.clearCountersButton.click(),\n    ]);\n  }\n\n  async enterCount(count: string, index: number = 0) {\n    await Promise.all([\n      this.counterInput.nth(index).waitFor(),\n      this.counterInput.nth(index).fill(count),\n    ]);\n  }\n\n  async removeCounter(index: number = 0) {\n    await Promise.all([\n      this.removeCountButton.nth(index).waitFor(),\n      this.removeCountButton.nth(index).click(),\n    ]);\n  }\n}\n"
  },
  {
    "path": "examples/counters/e2e/tests/increment_count.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { CountersPage } from \"./fixtures/counters_page\";\n\ntest.describe(\"Increment Count\", () => {\n  test(\"should increase the total count\", async ({ page }) => {\n    const ui = new CountersPage(page);\n    await ui.goto();\n    await ui.addCounter();\n\n    await ui.incrementCount();\n    await ui.incrementCount();\n    await ui.incrementCount();\n\n    await expect(ui.total).toHaveText(\"3\");\n  });\n});\n"
  },
  {
    "path": "examples/counters/e2e/tests/remove_counter.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { CountersPage } from \"./fixtures/counters_page\";\n\ntest.describe(\"Remove Counter\", () => {\n  test(\"should decrement the number of counters\", async ({ page }) => {\n    const ui = new CountersPage(page);\n    await ui.goto();\n\n    await ui.addCounter();\n    await ui.addCounter();\n    await ui.addCounter();\n\n    await ui.removeCounter(1);\n\n    await expect(ui.counters).toHaveText(\"2\");\n  });\n});\n"
  },
  {
    "path": "examples/counters/e2e/tests/view_counters.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { CountersPage } from \"./fixtures/counters_page\";\n\ntest.describe(\"View Counters\", () => {\n  test(\"should see the title\", async ({ page }) => {\n    const ui = new CountersPage(page);\n    await ui.goto();\n\n    await expect(page).toHaveTitle(\"Counters\");\n  });\n\n  test(\"should see the initial counts\", async ({ page }) => {\n    const counters = new CountersPage(page);\n    await counters.goto();\n\n    await expect(counters.total).toHaveText(\"0\");\n    await expect(counters.counters).toHaveText(\"0\");\n  });\n});\n"
  },
  {
    "path": "examples/counters/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\" data-weak-refs/>\n\t\t<title>Counters</title>\n\t</head>\n\t<body></body>\n</html>\n"
  },
  {
    "path": "examples/counters/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/counters/src/lib.rs",
    "content": "use leptos::prelude::*;\n\nconst MANY_COUNTERS: usize = 1000;\n\ntype CounterHolder = Vec<(usize, ArcRwSignal<i32>)>;\n\n#[derive(Copy, Clone)]\nstruct CounterUpdater {\n    set_counters: WriteSignal<CounterHolder>,\n}\n\n#[component]\npub fn Counters() -> impl IntoView {\n    let (next_counter_id, set_next_counter_id) = signal(0);\n    let (counters, set_counters) = signal::<CounterHolder>(vec![]);\n    provide_context(CounterUpdater { set_counters });\n\n    let add_counter = move |_| {\n        let id = next_counter_id.get();\n        let sig = ArcRwSignal::new(0);\n        set_counters.update(move |counters| counters.push((id, sig)));\n        set_next_counter_id.update(|id| *id += 1);\n    };\n\n    let add_many_counters = move |_| {\n        let next_id = next_counter_id.get();\n        let new_counters = (next_id..next_id + MANY_COUNTERS).map(|id| {\n            let signal = ArcRwSignal::new(0);\n            (id, signal)\n        });\n\n        set_counters.update(move |counters| counters.extend(new_counters));\n        set_next_counter_id.update(|id| *id += MANY_COUNTERS);\n    };\n\n    let clear_counters = move |_| {\n        set_counters.update(|counters| counters.clear());\n    };\n\n    view! {\n        <div>\n            <button on:click=add_counter>\"Add Counter\"</button>\n            <button on:click=add_many_counters>{format!(\"Add {MANY_COUNTERS} Counters\")}</button>\n            <button on:click=clear_counters>\"Clear Counters\"</button>\n            <p>\n                \"Total: \"\n                <span data-testid=\"total\">\n                    {move || {\n                        counters.get().iter().map(|(_, count)| count.get()).sum::<i32>().to_string()\n                    }}\n\n                </span> \" from \"\n                <span data-testid=\"counters\">{move || counters.get().len().to_string()}</span>\n                \" counters.\"\n            </p>\n            <ul>\n                <For\n                    each=move || counters.get()\n                    key=|counter| counter.0\n                    children=move |(id, value)| {\n                        view! { <Counter id value/> }\n                    }\n                />\n\n            </ul>\n        </div>\n    }\n}\n\n#[component]\nfn Counter(id: usize, value: ArcRwSignal<i32>) -> impl IntoView {\n    let value = RwSignal::from(value);\n    let CounterUpdater { set_counters } = use_context().unwrap();\n\n    view! {\n        <li>\n            <button on:click=move |_| value.update(move |value| *value -= 1)>\"-1\"</button>\n            <input\n                type=\"text\"\n                prop:value=value\n                on:input:target=move |ev| {\n                    value.set(ev.target().value().parse::<i32>().unwrap_or_default())\n                }\n            />\n\n            <span>{value}</span>\n            <button on:click=move |_| value.update(move |value| *value += 1)>\"+1\"</button>\n            <button on:click=move |_| {\n                set_counters\n                    .update(move |counters| counters.retain(|(counter_id, _)| counter_id != &id))\n            }>\"x\"</button>\n        </li>\n    }\n}\n"
  },
  {
    "path": "examples/counters/src/main.rs",
    "content": "use counters::Counters;\n\nfn main() {\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(Counters)\n}\n"
  },
  {
    "path": "examples/counters/tests/web.rs",
    "content": "#![allow(dead_code)]\n\nuse wasm_bindgen::JsCast;\nuse wasm_bindgen_test::*;\n\nwasm_bindgen_test_configure!(run_in_browser);\nuse counters::Counters;\nuse leptos::{prelude::*, task::tick};\nuse web_sys::HtmlElement;\n\n#[wasm_bindgen_test]\nasync fn inc() {\n    mount_to_body(Counters);\n\n    let document = document();\n    let div = document.query_selector(\"div\").unwrap().unwrap();\n    let add_counter = div\n        .first_child()\n        .unwrap()\n        .dyn_into::<HtmlElement>()\n        .unwrap();\n\n    assert_eq!(\n        div.inner_html(),\n        \"<button>Add Counter</button><button>Add 1000 \\\n         Counters</button><button>Clear Counters</button><p>Total: <span \\\n         data-testid=\\\"total\\\">0</span> from <span \\\n         data-testid=\\\"counters\\\">0</span> counters.</p><ul><!----></ul>\"\n    );\n\n    // add 3 counters\n    add_counter.click();\n    add_counter.click();\n    add_counter.click();\n\n    tick().await;\n\n    // check HTML\n    assert_eq!(\n        div.inner_html(),\n        \"<button>Add Counter</button><button>Add 1000 \\\n         Counters</button><button>Clear Counters</button><p>Total: <span \\\n         data-testid=\\\"total\\\">0</span> from <span \\\n         data-testid=\\\"counters\\\">3</span> \\\n         counters.</p><ul><li><button>-1</button><input \\\n         type=\\\"text\\\"><span>0</span><button>+1</button><button>x</button></\\\n         li><li><button>-1</button><input \\\n         type=\\\"text\\\"><span>0</span><button>+1</button><button>x</button></\\\n         li><li><button>-1</button><input \\\n         type=\\\"text\\\"><span>0</span><button>+1</button><button>x</button></\\\n         li><!----></ul>\"\n    );\n\n    let counters = div\n        .query_selector(\"ul\")\n        .unwrap()\n        .unwrap()\n        .unchecked_into::<HtmlElement>()\n        .children();\n\n    // click first counter once, second counter twice, etc.\n    // `NodeList` isn't a `Vec` so we iterate over it in this slightly awkward way\n    for idx in 0..counters.length() {\n        let counter = counters.item(idx).unwrap();\n        let inc_button = counter\n            .first_child()\n            .unwrap()\n            .next_sibling()\n            .unwrap()\n            .next_sibling()\n            .unwrap()\n            .next_sibling()\n            .unwrap()\n            .unchecked_into::<HtmlElement>();\n        for _ in 0..=idx {\n            inc_button.click();\n        }\n    }\n\n    tick().await;\n\n    assert_eq!(\n        div.inner_html(),\n        \"<button>Add Counter</button><button>Add 1000 \\\n         Counters</button><button>Clear Counters</button><p>Total: <span \\\n         data-testid=\\\"total\\\">6</span> from <span \\\n         data-testid=\\\"counters\\\">3</span> \\\n         counters.</p><ul><li><button>-1</button><input \\\n         type=\\\"text\\\"><span>1</span><button>+1</button><button>x</button></\\\n         li><li><button>-1</button><input \\\n         type=\\\"text\\\"><span>2</span><button>+1</button><button>x</button></\\\n         li><li><button>-1</button><input \\\n         type=\\\"text\\\"><span>3</span><button>+1</button><button>x</button></\\\n         li><!----></ul>\"\n    );\n\n    // remove the first counter\n    counters\n        .item(0)\n        .unwrap()\n        .last_child()\n        .unwrap()\n        .unchecked_into::<HtmlElement>()\n        .click();\n\n    tick().await;\n\n    assert_eq!(\n        div.inner_html(),\n        \"<button>Add Counter</button><button>Add 1000 \\\n         Counters</button><button>Clear Counters</button><p>Total: <span \\\n         data-testid=\\\"total\\\">5</span> from <span \\\n         data-testid=\\\"counters\\\">2</span> \\\n         counters.</p><ul><li><button>-1</button><input \\\n         type=\\\"text\\\"><span>2</span><button>+1</button><button>x</button></\\\n         li><li><button>-1</button><input \\\n         type=\\\"text\\\"><span>3</span><button>+1</button><button>x</button></\\\n         li><!----></ul>\"\n    );\n}\n"
  },
  {
    "path": "examples/directives/.cargo/config.toml",
    "content": "[build]\nrustflags = [\"--cfg=web_sys_unstable_apis\"]\n"
  },
  {
    "path": "examples/directives/Cargo.toml",
    "content": "[package]\nname = \"directives\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nlog = \"0.4.22\"\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nweb-sys = { version = \"0.3.70\", features = [\"Clipboard\", \"Navigator\"] }\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.42\"\nwasm-bindgen = \"0.2.93\"\nweb-sys = { version = \"0.3.70\", features = [\"NodeList\"] }"
  },
  {
    "path": "examples/directives/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/wasm-test.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/directives/README.md",
    "content": "# Leptos Directives Example\n\nThis example showcases a basic leptos app that shows how to write and use directives.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/directives/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\" data-weak-refs/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/directives/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/directives/src/lib.rs",
    "content": "use leptos::{ev::click, prelude::*};\nuse web_sys::Element;\n\n// no extra parameter\npub fn highlight(el: Element) {\n    let mut highlighted = false;\n\n    let handle = el.clone().on(click, move |_| {\n        highlighted = !highlighted;\n\n        if highlighted {\n            el.style((\"background-color\", \"yellow\"));\n        } else {\n            el.style((\"background-color\", \"transparent\"));\n        }\n    });\n    on_cleanup(move || drop(handle));\n}\n\n// one extra parameter\npub fn copy_to_clipboard(el: Element, content: &str) {\n    let content = content.to_owned();\n    let handle = el.clone().on(click, move |evt| {\n        evt.prevent_default();\n        evt.stop_propagation();\n\n        let _ = window().navigator().clipboard().write_text(&content);\n\n        el.set_inner_html(&format!(\"Copied \\\"{}\\\"\", &content));\n    });\n    on_cleanup(move || drop(handle));\n}\n\n// custom parameter\n\n#[derive(Clone)]\npub struct Amount(usize);\n\nimpl From<usize> for Amount {\n    fn from(value: usize) -> Self {\n        Self(value)\n    }\n}\n\n// a 'default' value if no value is passed in\nimpl From<()> for Amount {\n    fn from(_: ()) -> Self {\n        Self(1)\n    }\n}\n\npub fn add_dot(el: Element, amount: Amount) {\n    use leptos::wasm_bindgen::JsCast;\n    let el = el.unchecked_into::<web_sys::HtmlElement>();\n\n    let handle = el.clone().on(click, move |_| {\n        el.set_inner_text(&format!(\n            \"{}{}\",\n            el.inner_text(),\n            \".\".repeat(amount.0)\n        ))\n    });\n    on_cleanup(move || drop(handle));\n}\n\n#[component]\npub fn SomeComponent() -> impl IntoView {\n    view! {\n        <p>Some paragraphs</p>\n        <p>that can be clicked</p>\n        <p>in order to highlight them</p>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    let data = \"Hello World!\";\n\n    view! {\n        <a href=\"#\" use:copy_to_clipboard=data>\n            \"Copy \\\"\"\n            {data}\n            \"\\\" to clipboard\"\n        </a>\n        // automatically applies the directive to every root element in `SomeComponent`\n        <SomeComponent use:highlight/>\n        // no value will default to `().into()`\n        <button use:add_dot>\"Add a dot\"</button>\n        // can manually call `.into()` to convert to the correct type\n        // (automatically calling `.into()` prevents using generics in directive functions)\n        <button use:add_dot=5.into()>\"Add 5 dots\"</button>\n    }\n}\n"
  },
  {
    "path": "examples/directives/src/main.rs",
    "content": "use directives::App;\nuse leptos::prelude::*;\n\nfn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(|| view! { <App/> })\n}\n"
  },
  {
    "path": "examples/directives/tests/web.rs",
    "content": "#![allow(dead_code)]\n\nuse directives::App;\nuse leptos::{prelude::*, task::tick};\nuse wasm_bindgen::JsCast;\nuse wasm_bindgen_test::*;\nuse web_sys::HtmlElement;\n\nwasm_bindgen_test_configure!(run_in_browser);\n#[wasm_bindgen_test]\nasync fn test_directives() {\n    leptos::mount::mount_to_body(App);\n    tick().await;\n\n    let document = document();\n    let paragraphs = document.query_selector_all(\"p\").unwrap();\n\n    assert_eq!(paragraphs.length(), 3);\n\n    for i in 0..paragraphs.length() {\n        println!(\"i: {}\", i);\n        let p = paragraphs\n            .item(i)\n            .unwrap()\n            .dyn_into::<HtmlElement>()\n            .unwrap();\n        assert_eq!(\n            p.style().get_property_value(\"background-color\").unwrap(),\n            \"\"\n        );\n\n        p.click();\n\n        assert_eq!(\n            p.style().get_property_value(\"background-color\").unwrap(),\n            \"yellow\"\n        );\n\n        p.click();\n\n        assert_eq!(\n            p.style().get_property_value(\"background-color\").unwrap(),\n            \"transparent\"\n        );\n    }\n\n    let a = document\n        .query_selector(\"a\")\n        .unwrap()\n        .unwrap()\n        .dyn_into::<HtmlElement>()\n        .unwrap();\n    assert_eq!(a.inner_html(), \"Copy \\\"Hello World!\\\" to clipboard\");\n\n    a.click();\n    assert_eq!(a.inner_html(), \"Copied \\\"Hello World!\\\"\");\n}\n"
  },
  {
    "path": "examples/error_boundary/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# Support playwright testing\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\npnpm-lock.yaml\n\n# Support trunk\ndist\n"
  },
  {
    "path": "examples/error_boundary/Cargo.toml",
    "content": "[package]\nname = \"error_boundary\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_log = \"1.0\"\nlog = \"0.4.22\"\nconsole_error_panic_hook = \"0.1.7\"\n"
  },
  {
    "path": "examples/error_boundary/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/playwright-trunk-test.toml\" },\n]\n"
  },
  {
    "path": "examples/error_boundary/README.md",
    "content": "# Leptos `<ErrorBoundary/>` Example\n\nThis example shows how to handle basic errors using Leptos.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Testing\n\nThis project is configured to run start and stop of processes for integration tests without the use of Cargo Leptos or Node.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/error_boundary/e2e/.gitignore",
    "content": "node_modules/\n/test-results/\n/playwright-report/\n/playwright/.cache/\n"
  },
  {
    "path": "examples/error_boundary/e2e/package.json",
    "content": "{\n  \"private\": \"true\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.35.1\"\n  }\n}\n"
  },
  {
    "path": "examples/error_boundary/e2e/playwright.config.ts",
    "content": "import { defineConfig, devices } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://github.com/motdotla/dotenv\n */\n// require('dotenv').config();\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nexport default defineConfig({\n  testDir: \"./tests\",\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !process.env.DEV,\n  /* Retry on CI only */\n  retries: process.env.DEV ? 0 : 2,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.DEV ? 1 : 1,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: [[\"html\", { open: \"never\" }], [\"list\"]],\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    baseURL: \"http://127.0.0.1:8080\",\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: \"on-first-retry\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"chromium\",\n      use: { ...devices[\"Desktop Chrome\"] },\n    },\n\n    // {\n    //   name: \"firefox\",\n    //   use: { ...devices[\"Desktop Firefox\"] },\n    // },\n\n    // {\n    //   name: \"webkit\",\n    //   use: { ...devices[\"Desktop Safari\"] },\n    // },\n\n    /* Test against mobile viewports. */\n    // {\n    //   name: 'Mobile Chrome',\n    //   use: { ...devices['Pixel 5'] },\n    // },\n    // {\n    //   name: 'Mobile Safari',\n    //   use: { ...devices['iPhone 12'] },\n    // },\n\n    /* Test against branded browsers. */\n    // {\n    //   name: 'Microsoft Edge',\n    //   use: { ...devices['Desktop Edge'], channel: 'msedge' },\n    // },\n    // {\n    //   name: 'Google Chrome',\n    //   use: { ..devices['Desktop Chrome'], channel: 'chrome' },\n    // },\n  ],\n\n  /* Run your local dev server before starting the tests */\n  // webServer: {\n  //   command: \"cd ../ && trunk serve\",\n  //   url: \"http://127.0.0.1:8080\",\n  //   reuseExistingServer: false, //!process.env.CI,\n  // },\n});\n"
  },
  {
    "path": "examples/error_boundary/e2e/tests/clear_number.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { HomePage } from \"./fixtures/home_page\";\n\ntest.describe(\"Clear Number\", () => {\n  test(\"should see the error message\", async ({ page }) => {\n    const ui = new HomePage(page);\n    await ui.goto();\n\n    await ui.clearInput();\n\n    await expect(ui.errorMessage).toHaveText(\"Not an integer! Errors: \");\n  });\n  test(\"should see the error list\", async ({ page }) => {\n    const ui = new HomePage(page);\n    await ui.goto();\n\n    await ui.clearInput();\n\n    await expect(ui.errorList).toHaveText(\n      \"cannot parse integer from empty string\"\n    );\n  });\n});\n"
  },
  {
    "path": "examples/error_boundary/e2e/tests/click_down_arrow.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { HomePage } from \"./fixtures/home_page\";\n\ntest.describe(\"Click Down Arrow\", () => {\n  test(\"should see the negative number\", async ({ page }) => {\n    const ui = new HomePage(page);\n    await ui.goto();\n\n    await ui.clickDownArrow();\n    await ui.clickDownArrow();\n    await ui.clickDownArrow();\n    await ui.clickDownArrow();\n    await ui.clickDownArrow();\n\n    await expect(ui.successMessage).toHaveText(\"You entered -5\");\n  });\n});\n"
  },
  {
    "path": "examples/error_boundary/e2e/tests/click_up_arrow.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { HomePage } from \"./fixtures/home_page\";\n\ntest.describe(\"Click Up Arrow\", () => {\n  test(\"should see the positive number\", async ({ page }) => {\n    const ui = new HomePage(page);\n    await ui.goto();\n\n    await ui.clickUpArrow();\n    await ui.clickUpArrow();\n    await ui.clickUpArrow();\n\n    await expect(ui.successMessage).toHaveText(\"You entered 3\");\n  });\n});\n"
  },
  {
    "path": "examples/error_boundary/e2e/tests/fixtures/home_page.ts",
    "content": "import { expect, Locator, Page } from \"@playwright/test\";\n\nexport class HomePage {\n  readonly page: Page;\n  readonly pageTitle: Locator;\n  readonly numberInput: Locator;\n  readonly successMessage: Locator;\n  readonly errorMessage: Locator;\n\n  readonly errorList: Locator;\n\n  constructor(page: Page) {\n    this.page = page;\n\n    this.pageTitle = page.locator(\"h1\");\n    this.numberInput = page.getByLabel(\n      \"Type an integer (or something that's not an integer!)\"\n    );\n    this.successMessage = page.locator(\"label p\");\n    this.errorMessage = page.locator(\"div p\");\n    this.errorList = page.getByRole(\"list\");\n  }\n\n  async goto() {\n    await this.page.goto(\"/\");\n  }\n\n  async enterNumber(count: string, index: number = 0) {\n    await Promise.all([\n      this.numberInput.waitFor(),\n      this.numberInput.fill(count),\n    ]);\n  }\n\n  async clickUpArrow() {\n    await Promise.all([\n      this.numberInput.waitFor(),\n      this.numberInput.press(\"ArrowUp\"),\n    ]);\n  }\n\n  async clickDownArrow() {\n    await Promise.all([\n      this.numberInput.waitFor(),\n      this.numberInput.press(\"ArrowDown\"),\n    ]);\n  }\n\n  async clearInput() {\n    await Promise.all([\n      this.numberInput.waitFor(),\n      this.clickUpArrow(),\n      this.numberInput.press(\"Backspace\"),\n    ]);\n  }\n}\n"
  },
  {
    "path": "examples/error_boundary/e2e/tests/open_app.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { HomePage } from \"./fixtures/home_page\";\n\ntest.describe(\"Open App\", () => {\n  test(\"should see the page title\", async ({ page }) => {\n    const ui = new HomePage(page);\n    await ui.goto();\n\n    await expect(ui.pageTitle).toHaveText(\"Error Handling\");\n  });\n});\n"
  },
  {
    "path": "examples/error_boundary/e2e/tests/type_number.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\nimport { HomePage } from \"./fixtures/home_page\";\n\ntest.describe(\"Type Number\", () => {\n  test(\"should see the typed number\", async ({ page }) => {\n    const ui = new HomePage(page);\n    await ui.goto();\n\n    await ui.enterNumber(\"7\");\n\n    await expect(ui.successMessage).toHaveText(\"You entered 7\");\n  });\n});\n"
  },
  {
    "path": "examples/error_boundary/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/error_boundary/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/error_boundary/src/lib.rs",
    "content": "use leptos::prelude::*;\n\n#[component]\npub fn App() -> impl IntoView {\n    let (value, set_value) = signal(\"\".parse::<i32>());\n\n    view! {\n        <h1>\"Error Handling\"</h1>\n        <label>\n            \"Type an integer (or something that's not an integer!)\"\n            <input\n                type=\"number\"\n                value=move || value.get().unwrap_or_default()\n                // when input changes, try to parse a number from the input\n                on:input:target=move |ev| set_value.set(ev.target().value().parse::<i32>())\n            />\n            // If an `Err(_) has been rendered inside the <ErrorBoundary/>,\n            // the fallback will be displayed. Otherwise, the children of the\n            // <ErrorBoundary/> will be displayed.\n            // the fallback receives a signal containing current errors\n            <ErrorBoundary fallback=|errors| {\n                let errors = errors.clone();\n                view! {\n                    <div class=\"error\">\n                        <p>\"Not an integer! Errors: \"</p>\n                        // we can render a list of errors\n                        // as strings, if we'd like\n                        <ul>\n                            {move || {\n                                errors\n                                    .read()\n                                    .iter()\n                                    .map(|(_, e)| view! { <li>{e.to_string()}</li> })\n                                    .collect::<Vec<_>>()\n                            }}\n\n                        </ul>\n                    </div>\n                }\n            }>\n\n                <p>\n                    \"You entered \"\n                    // because `value` is `Result<i32, _>`,\n                    // it will render the `i32` if it is `Ok`,\n                    // and render nothing and trigger the error boundary\n                    // if it is `Err`. It's a signal, so this will dynamically\n                    // update when `value` changes\n                    <strong>{value}</strong>\n                </p>\n            </ErrorBoundary>\n        </label>\n    }\n}\n"
  },
  {
    "path": "examples/error_boundary/src/main.rs",
    "content": "use error_boundary::*;\nuse leptos::prelude::*;\n\npub fn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(App)\n}\n"
  },
  {
    "path": "examples/errors_axum/Cargo.toml",
    "content": "[package]\nname = \"errors_axum\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nleptos = { path = \"../../leptos\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_meta = { path = \"../../meta\" }\nleptos_router = { path = \"../../router\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nhttp = { version = \"1.1\" }\nthiserror = \"2.0.12\"\nwasm-bindgen = \"0.2.93\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:leptos_axum\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"leptos_axum\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"errors_axum\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/errors_axum/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/errors_axum/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"errors_axum\"\n"
  },
  {
    "path": "examples/errors_axum/README.md",
    "content": "# Leptos Errors Demonstration with Axum\n\nThis example demonstrates how Leptos Errors can work with an Axum backend on a server.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/errors_axum/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/errors_axum/src/error_template.rs",
    "content": "use crate::errors::AppError;\nuse leptos::{logging::log, prelude::*};\n#[cfg(feature = \"ssr\")]\nuse leptos_axum::ResponseOptions;\n\n// A basic function to display errors served by the error boundaries.\n// Feel free to do more complicated things here than just displaying them.\n#[component]\npub fn ErrorTemplate(#[prop(into)] errors: Signal<Errors>) -> impl IntoView {\n    // Get Errors from Signal\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors = Memo::new(move |_| {\n        errors\n            .get_untracked()\n            .into_iter()\n            .filter_map(|(_, v)| v.downcast_ref::<AppError>().cloned())\n            .collect::<Vec<_>>()\n    });\n    log!(\"Errors: {:#?}\", &*errors.read_untracked());\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    #[cfg(feature = \"ssr\")]\n    {\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors.read_untracked()[0].status_code());\n        }\n    }\n\n    view! {\n        <h1>{move || {\n            if errors.read().len() > 1 {\n                \"Errors\"\n            } else {\n                \"Error\"\n            }}}\n        </h1>\n        {move || {\n            errors.get()\n                .into_iter()\n                .map(|error| {\n                    let error_string = error.to_string();\n                    let error_code= error.status_code();\n                    view! {\n                        <h2>{error_code.to_string()}</h2>\n                        <p>\"Error: \" {error_string}</p>\n                    }\n                })\n                .collect_view()\n        }}\n    }\n}\n"
  },
  {
    "path": "examples/errors_axum/src/errors.rs",
    "content": "use http::status::StatusCode;\nuse thiserror::Error;\n\n#[derive(Debug, Clone, PartialEq, Eq, Error)]\npub enum AppError {\n    #[error(\"Not Found\")]\n    NotFound,\n    #[error(\"Internal Server Error\")]\n    InternalServerError,\n}\n\nimpl AppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            AppError::NotFound => StatusCode::NOT_FOUND,\n            AppError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,\n        }\n    }\n}\n"
  },
  {
    "path": "examples/errors_axum/src/landing.rs",
    "content": "use crate::{error_template::ErrorTemplate, errors::AppError};\nuse leptos::prelude::*;\nuse leptos_meta::*;\nuse leptos_router::{\n    components::{Route, Router, Routes},\n    StaticSegment,\n};\n\n#[server(CauseInternalServerError, \"/api\")]\npub async fn cause_internal_server_error() -> Result<(), ServerFnError> {\n    // fake API delay\n    std::thread::sleep(std::time::Duration::from_millis(1250));\n\n    Err(ServerFnError::ServerError(\n        \"Generic Server Error\".to_string(),\n    ))\n}\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n    view! {\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/favicon.ico\"/>\n        <Stylesheet id=\"leptos\" href=\"/pkg/errors_axum.css\"/>\n        <Router>\n            <header>\n                <h1>\"Error Examples:\"</h1>\n            </header>\n            <main>\n                <Routes fallback=|| {\n                    let mut errors = Errors::default();\n                    errors.insert_with_default_key(AppError::NotFound);\n                    view! {\n                        <ErrorTemplate errors/>\n                    }\n                    .into_view()\n                }>\n                    <Route path=StaticSegment(\"\") view=ExampleErrors/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n#[component]\npub fn ExampleErrors() -> impl IntoView {\n    let generate_internal_error =\n        ServerAction::<CauseInternalServerError>::new();\n\n    view! {\n        <p>\n            \"These links will load 404 pages since they do not exist. Verify with browser development tools: \" <br/>\n            <a href=\"/404\">\"This links to a page that does not exist\"</a><br/>\n            <a href=\"/404\" target=\"_blank\">\"Same link, but in a new tab\"</a>\n        </p>\n        <p>\n            \"After pressing this button check browser network tools. Can be used even when WASM is blocked:\"\n        </p>\n        <ActionForm action=generate_internal_error>\n            <input name=\"error1\" type=\"submit\" value=\"Generate Internal Server Error\"/>\n        </ActionForm>\n        <p>\"The following <div> will always contain an error and cause this page to produce status 500. Check browser dev tools. \"</p>\n        <div>\n            // note that the error boundaries could be placed above in the Router or lower down\n            // in a particular route. The generated errors on the entire page contribute to the\n            // final status code sent by the server when producing ssr pages.\n            <ErrorBoundary fallback=|errors| view!{ <ErrorTemplate errors/>}>\n                <ReturnsError/>\n            </ErrorBoundary>\n        </div>\n    }\n}\n\n#[component]\npub fn ReturnsError() -> impl IntoView {\n    Err::<String, AppError>(AppError::InternalServerError)\n}\n"
  },
  {
    "path": "examples/errors_axum/src/lib.rs",
    "content": "pub mod error_template;\npub mod errors;\npub mod landing;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::landing::App;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/errors_axum/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\nmod ssr_imports {\n    use axum::extract::State;\n    pub use axum::{\n        body::Body as AxumBody,\n        extract::Path,\n        http::Request,\n        response::{IntoResponse, Response},\n        routing::get,\n        Router,\n    };\n    use errors_axum::landing::shell;\n    pub use errors_axum::landing::App;\n    use leptos::{config::LeptosOptions, context::provide_context};\n    pub use leptos_axum::{generate_route_list, LeptosRoutes};\n\n    // This custom handler lets us provide Axum State via context\n    pub async fn custom_handler(\n        Path(id): Path<String>,\n        State(options): State<LeptosOptions>,\n        req: Request<AxumBody>,\n    ) -> Response {\n        let handler = leptos_axum::render_app_to_stream_with_context(\n            move || {\n                provide_context(id.clone());\n            },\n            move || shell(options.clone()),\n        );\n        handler(req).await.into_response()\n    }\n}\n\n#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use errors_axum::landing::shell;\n    use leptos::config::get_configuration;\n    use ssr_imports::*;\n\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .route(\"/special/{id}\", get(custom_handler))\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    println!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n// this is if we were using client-only rending with Trunk\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // This example cannot be built as a trunk standalone CSR-only app.\n    // The server is needed to demonstrate the error statuses.\n}\n"
  },
  {
    "path": "examples/errors_axum/style.css",
    "content": ".pending {\n\tcolor: purple;\n}"
  },
  {
    "path": "examples/fetch/Cargo.toml",
    "content": "[package]\nname = \"fetch\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\", \"tracing\"] }\nreqwasm = \"0.5.0\"\ngloo-timers = { version = \"0.3.0\", features = [\"futures\"] }\nserde = { version = \"1.0\", features = [\"derive\"] }\nlog = \"0.4.22\"\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nthiserror = \"2.0.12\"\ntracing = \"0.1.40\"\ntracing-subscriber = \"0.3.18\"\ntracing-subscriber-wasm = \"0.1.0\"\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.42\"\n"
  },
  {
    "path": "examples/fetch/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/fetch/README.md",
    "content": "# Client Side Fetch\n\nThis example shows how to fetch data from the client in WebAssembly.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/fetch/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<style>\n\t\timg {\n\t\t\tmax-width: 250px;\n\t\t\theight: auto;\n\t\t}\n\n\t\t.error {\n\t\t\tborder: 1px solid red;\n\t\t\tcolor: red;\n\t\t\tbackground-color: lightpink;\n\t\t}\n\t</style>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/fetch/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/fetch/src/lib.rs",
    "content": "use leptos::prelude::*;\nuse serde::{Deserialize, Serialize};\nuse thiserror::Error;\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct Cat {\n    url: String,\n}\n\n#[derive(Error, Clone, Debug)]\npub enum CatError {\n    #[error(\"Please request more than zero cats.\")]\n    NonZeroCats,\n}\n\ntype CatCount = usize;\n\nasync fn fetch_cats(count: CatCount) -> Result<Vec<String>, Error> {\n    if count > 0 {\n        gloo_timers::future::TimeoutFuture::new(1000).await;\n        // make the request\n        let res = reqwasm::http::Request::get(&format!(\n            \"https://api.thecatapi.com/v1/images/search?limit={count}\",\n        ))\n        .send()\n        .await?\n        // convert it to JSON\n        .json::<Vec<Cat>>()\n        .await?\n        // extract the URL field for each cat\n        .into_iter()\n        .take(count)\n        .map(|cat| cat.url)\n        .collect::<Vec<_>>();\n        Ok(res)\n    } else {\n        Err(CatError::NonZeroCats)?\n    }\n}\n\npub fn fetch_example() -> impl IntoView {\n    let (cat_count, set_cat_count) = signal::<CatCount>(1);\n\n    let cats = LocalResource::new(move || fetch_cats(cat_count.get()));\n\n    let fallback = move |errors: ArcRwSignal<Errors>| {\n        let error_list = move || {\n            errors.with(|errors| {\n                errors\n                    .iter()\n                    .map(|(_, e)| view! { <li>{e.to_string()}</li> })\n                    .collect::<Vec<_>>()\n            })\n        };\n\n        view! {\n            <div class=\"error\">\n                <h2>\"Error\"</h2>\n                <ul>{error_list}</ul>\n            </div>\n        }\n    };\n\n    view! {\n        <div>\n            <label>\n                \"How many cats would you like?\"\n                <input\n                    type=\"number\"\n                    prop:value=move || cat_count.get().to_string()\n                    on:input:target=move |ev| {\n                        let val = ev.target().value().parse::<CatCount>().unwrap_or(0);\n                        set_cat_count.set(val);\n                    }\n                />\n\n            </label>\n            <Transition fallback=|| view! { <div>\"Loading...\"</div> }>\n                <ErrorBoundary fallback>\n                    <ul>\n                        {move || Suspend::new(async move {\n                            cats.await\n                                .map(|cats| {\n                                    cats.iter()\n                                        .map(|s| {\n                                            view! {\n                                                <li>\n                                                    <img src=s.clone() />\n                                                </li>\n                                            }\n                                        })\n                                        .collect::<Vec<_>>()\n                                })\n                        })}\n\n                    </ul>\n                </ErrorBoundary>\n            </Transition>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/fetch/src/main.rs",
    "content": "use fetch::fetch_example;\nuse leptos::prelude::*;\n\npub fn main() {\n    use tracing_subscriber::fmt;\n    use tracing_subscriber_wasm::MakeConsoleWriter;\n\n    fmt()\n        .with_writer(\n            // To avoid trace events in the browser from showing their\n            // JS backtrace, which is very annoying, in my opinion\n            MakeConsoleWriter::default()\n                .map_trace_level_to(tracing::Level::DEBUG),\n        )\n        // For some reason, if we don't do this in the browser, we get\n        // a runtime error.\n        .without_time()\n        .init();\n    console_error_panic_hook::set_once();\n    mount_to_body(fetch_example)\n}\n"
  },
  {
    "path": "examples/hackernews/Cargo.toml",
    "content": "[package]\nname = \"hackernews\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[profile.release]\ncodegen-units = 1\nopt-level = \"z\"\npanic = \"abort\"\nlto = true\n\n[dependencies]\nactix-files = { version = \"0.6.6\", optional = true }\nactix-web = { version = \"4.8\", optional = true, features = [\"macros\"] }\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nleptos = { path = \"../../leptos\" }\nleptos_meta = { path = \"../../meta\" }\nleptos_actix = { path = \"../../integrations/actix\", optional = true }\nleptos_router = { path = \"../../router\" }\nlog = \"0.4.22\"\nserde = { version = \"1.0\", features = [\"derive\"] }\ngloo-net = { version = \"0.6.0\", features = [\"http\"] }\nreqwest = { version = \"0.12.5\", features = [\"json\"] }\nwasm-bindgen = \"0.2.105\"\nweb-sys = { version = \"0.3.70\", features = [\"AbortController\", \"AbortSignal\"] }\nsend_wrapper = \"0.6.0\"\n\n[features]\ndefault = [\"csr\"]\ncsr = [\"leptos/csr\"]\nhydrate = [\"leptos/hydrate\"]\nssr = [\"dep:actix-files\", \"dep:actix-web\", \"dep:leptos_actix\", \"leptos/ssr\"]\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\n\n[package.metadata.cargo-all-features]\ndenylist = [\"actix-files\", \"actix-web\", \"leptos_actix\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"hackernews\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "examples/hackernews/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/hackernews/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"hackernews\"\n"
  },
  {
    "path": "examples/hackernews/README.md",
    "content": "# Leptos Hacker News Example\n\nThis example creates a basic clone of the Hacker News site. It showcases Leptos' ability to create both a client-side rendered app, and a server side rendered app with hydration, in a single repository\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` or `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/hackernews/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\" data-cargo-features=\"csr\"/>\n\t\t<link data-trunk rel=\"css\" href=\"./style.css\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/hackernews/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/hackernews/src/api.rs",
    "content": "use serde::{de::DeserializeOwned, Deserialize, Serialize};\n\npub fn story(path: &str) -> String {\n    format!(\"https://node-hnapi.herokuapp.com/{path}\")\n}\n\npub fn user(path: &str) -> String {\n    format!(\"https://hacker-news.firebaseio.com/v0/user/{path}.json\")\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn fetch_api<T>(\n    path: &str,\n) -> impl std::future::Future<Output = Option<T>> + Send + '_\nwhere\n    T: Serialize + DeserializeOwned,\n{\n    use leptos::prelude::on_cleanup;\n    use send_wrapper::SendWrapper;\n\n    SendWrapper::new(async move {\n        let abort_controller =\n            SendWrapper::new(web_sys::AbortController::new().ok());\n        let abort_signal = abort_controller.as_ref().map(|a| a.signal());\n\n        // abort in-flight requests if, e.g., we've navigated away from this page\n        on_cleanup(move || {\n            if let Some(abort_controller) = abort_controller.take() {\n                abort_controller.abort()\n            }\n        });\n\n        gloo_net::http::Request::get(path)\n            .abort_signal(abort_signal.as_ref())\n            .send()\n            .await\n            .map_err(|e| log::error!(\"{e}\"))\n            .ok()?\n            .json()\n            .await\n            .ok()\n    })\n}\n\n#[cfg(feature = \"ssr\")]\npub async fn fetch_api<T>(path: &str) -> Option<T>\nwhere\n    T: Serialize + DeserializeOwned,\n{\n    reqwest::get(path)\n        .await\n        .map_err(|e| log::error!(\"{e}\"))\n        .ok()?\n        .json()\n        .await\n        .ok()\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct Story {\n    pub id: usize,\n    pub title: String,\n    pub points: Option<i32>,\n    pub user: Option<String>,\n    pub time: usize,\n    pub time_ago: String,\n    #[serde(alias = \"type\")]\n    pub story_type: String,\n    pub url: String,\n    #[serde(default)]\n    pub domain: String,\n    #[serde(default)]\n    pub comments: Option<Vec<Comment>>,\n    pub comments_count: Option<usize>,\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct Comment {\n    pub id: usize,\n    pub level: usize,\n    pub user: Option<String>,\n    pub time: usize,\n    pub time_ago: String,\n    pub content: Option<String>,\n    pub comments: Vec<Comment>,\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct User {\n    pub created: usize,\n    pub id: String,\n    pub karma: i32,\n    pub about: Option<String>,\n}\n"
  },
  {
    "path": "examples/hackernews/src/lib.rs",
    "content": "use leptos::prelude::*;\nmod api;\nmod routes;\nuse leptos_meta::{provide_meta_context, Link, Meta, Stylesheet};\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router, RoutingProgress},\n    OptionalParamSegment, ParamSegment, StaticSegment,\n};\nuse routes::{nav::*, stories::*, story::*, users::*};\nuse std::time::Duration;\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n    let (is_routing, set_is_routing) = signal(false);\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/hackernews.css\"/>\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/favicon.ico\"/>\n        <Meta name=\"description\" content=\"Leptos implementation of a HackerNews demo.\"/>\n        <Router set_is_routing>\n            // shows a progress bar while async data are loading\n            <div class=\"routing-progress\">\n                <RoutingProgress is_routing max_time=Duration::from_millis(250)/>\n            </div>\n            <Nav />\n            <main>\n                <FlatRoutes fallback=|| \"Not found.\">\n                    <Route path=(StaticSegment(\"users\"), ParamSegment(\"id\")) view=User/>\n                    <Route path=(StaticSegment(\"stories\"), ParamSegment(\"id\")) view=Story/>\n                    <Route path=OptionalParamSegment(\"stories\") view=Stories/>\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/hackernews/src/main.rs",
    "content": "// server-only stuff\n#[cfg(feature = \"ssr\")]\nmod ssr_imports {\n    pub use actix_files::Files;\n    pub use actix_web::*;\n    pub use hackernews::App;\n\n    #[get(\"/style.css\")]\n    pub async fn css() -> impl Responder {\n        actix_files::NamedFile::open_async(\"./style.css\").await\n    }\n    #[get(\"/favicon.ico\")]\n    pub async fn favicon() -> impl Responder {\n        actix_files::NamedFile::open_async(\"./target/site//favicon.ico\").await\n    }\n}\n\n#[cfg(feature = \"ssr\")]\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    use leptos::prelude::*;\n    use leptos_actix::{generate_route_list, LeptosRoutes};\n    use leptos_meta::MetaTags;\n    use ssr_imports::*;\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n\n    HttpServer::new(move || {\n        // Generate the list of routes in your Leptos App\n        let routes = generate_route_list(App);\n        let leptos_options = &conf.leptos_options;\n        let site_root = &leptos_options.site_root;\n\n        App::new()\n            .service(css)\n            .service(favicon)\n            .leptos_routes(routes, {\n                let leptos_options = leptos_options.clone();\n                move || {\n                    use leptos::prelude::*;\n\n                    view! {\n                        <!DOCTYPE html>\n                        <html lang=\"en\">\n                            <head>\n                                <meta charset=\"utf-8\"/>\n                                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                                <AutoReload options=leptos_options.clone() />\n                                <HydrationScripts options=leptos_options.clone()/>\n                                <MetaTags/>\n                            </head>\n                            <body>\n                                <App/>\n                            </body>\n                        </html>\n                    }\n            }})\n            .service(Files::new(\"/\", site_root.as_ref()))\n        //.wrap(middleware::Compress::default())\n    })\n    .bind(&addr)?\n    .run()\n    .await\n}\n\n// CSR-only setup\n#[cfg(not(feature = \"ssr\"))]\nfn main() {\n    use hackernews::App;\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(App)\n}\n"
  },
  {
    "path": "examples/hackernews/src/routes/nav.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::components::A;\n\n#[component]\npub fn Nav() -> impl IntoView {\n    view! {\n        <header class=\"header\">\n            <nav class=\"inner\">\n                <A href=\"/home\">\n                    <strong>\"HN\"</strong>\n                </A>\n                <A href=\"/new\">\n                    <strong>\"New\"</strong>\n                </A>\n                <A href=\"/show\">\n                    <strong>\"Show\"</strong>\n                </A>\n                <A href=\"/ask\">\n                    <strong>\"Ask\"</strong>\n                </A>\n                <A href=\"/job\">\n                    <strong>\"Jobs\"</strong>\n                </A>\n                <a\n                    class=\"github\"\n                    href=\"http://github.com/leptos-rs/leptos\"\n                    target=\"_blank\"\n                    rel=\"noreferrer\"\n                >\n                    \"Built with Leptos\"\n                </a>\n            </nav>\n        </header>\n    }\n    .into_any()\n}\n"
  },
  {
    "path": "examples/hackernews/src/routes/stories.rs",
    "content": "use crate::api;\nuse leptos::{either::Either, prelude::*};\nuse leptos_router::{\n    components::A,\n    hooks::{use_params_map, use_query_map},\n};\n\nfn category(from: &str) -> &'static str {\n    match from {\n        \"new\" => \"newest\",\n        \"show\" => \"show\",\n        \"ask\" => \"ask\",\n        \"job\" => \"jobs\",\n        _ => \"news\",\n    }\n}\n\n#[component]\npub fn Stories() -> impl IntoView {\n    let query = use_query_map();\n    let params = use_params_map();\n    let page = move || {\n        query\n            .read()\n            .get(\"page\")\n            .and_then(|page| page.parse::<usize>().ok())\n            .unwrap_or(1)\n    };\n    let story_type = move || {\n        params\n            .read()\n            .get(\"stories\")\n            .unwrap_or_else(|| \"top\".to_string())\n    };\n    let stories = Resource::new(\n        move || (page(), story_type()),\n        move |(page, story_type)| async move {\n            let path = format!(\"{}?page={}\", category(&story_type), page);\n            api::fetch_api::<Vec<api::Story>>(&api::story(&path)).await\n        },\n    );\n    let (pending, set_pending) = signal(false);\n\n    let hide_more_link = move || match &*stories.read() {\n        Some(Some(stories)) => stories.len() < 28,\n        _ => true\n    } || pending.get();\n\n    view! {\n        <div class=\"news-view\">\n            <div class=\"news-list-nav\">\n                <span>\n                    {move || {\n                        if page() > 1 {\n                            Either::Left(\n                                view! {\n                                    <a\n                                        class=\"page-link\"\n                                        href=move || {\n                                            format!(\"/{}?page={}\", story_type(), page() - 1)\n                                        }\n                                        aria-label=\"Previous Page\"\n                                    >\n                                        \"< prev\"\n                                    </a>\n                                },\n                            )\n                        } else {\n                            Either::Right(\n                                view! {\n                                    <span class=\"page-link disabled\" aria-hidden=\"true\">\n                                        \"< prev\"\n                                    </span>\n                                },\n                            )\n                        }\n                    }}\n\n                </span>\n                <span>\"page \" {page}</span>\n                <Suspense>\n                    <span\n                        class=\"page-link\"\n                        class:disabled=hide_more_link\n                        aria-hidden=hide_more_link\n                    >\n                        <a\n                            href=move || format!(\"/{}?page={}\", story_type(), page() + 1)\n                            aria-label=\"Next Page\"\n                        >\n                            \"more >\"\n                        </a>\n                    </span>\n                </Suspense>\n            </div>\n            <main class=\"news-list\">\n                <div>\n                    <Transition fallback=move || view! { <p>\"Loading...\"</p> } set_pending>\n                        <Show when=move || {\n                            stories.read().as_ref().map(Option::is_none).unwrap_or(false)\n                        }>> <p>\"Error loading stories.\"</p></Show>\n                        <ul>\n                            <For\n                                each=move || stories.get().unwrap_or_default().unwrap_or_default()\n                                key=|story| story.id\n                                let:story\n                            >\n                                <Story story/>\n                            </For>\n                        </ul>\n                    </Transition>\n                </div>\n            </main>\n        </div>\n    }\n    .into_any()\n}\n\n#[component]\nfn Story(story: api::Story) -> impl IntoView {\n    view! {\n        <li class=\"news-item\">\n            <span class=\"score\">{story.points}</span>\n            <span class=\"title\">\n                {if !story.url.starts_with(\"item?id=\") {\n                    Either::Left(\n                        view! {\n                            <span>\n                                <a href=story.url target=\"_blank\" rel=\"noreferrer\">\n                                    {story.title.clone()}\n                                </a>\n                                <span class=\"host\">\"(\" {story.domain} \")\"</span>\n                            </span>\n                        },\n                    )\n                } else {\n                    let title = story.title.clone();\n                    Either::Right(view! { <A href=format!(\"/stories/{}\", story.id)>{title}</A> })\n                }}\n\n            </span>\n            <br/>\n            <span class=\"meta\">\n                {if story.story_type != \"job\" {\n                    Either::Left(\n                        view! {\n                            <span>\n                                \"by \"\n                                <ShowLet some=story.user let:user>\n                                    <A href=format!(\"/users/{user}\")>{user.clone()}</A>\n                                </ShowLet>\n                                {format!(\" {} | \", story.time_ago)}\n                                <A href=format!(\n                                    \"/stories/{}\",\n                                    story.id,\n                                )>\n                                    {if story.comments_count.unwrap_or_default() > 0 {\n                                        format!(\n                                            \"{} comments\",\n                                            story.comments_count.unwrap_or_default(),\n                                        )\n                                    } else {\n                                        \"discuss\".into()\n                                    }}\n\n                                </A>\n                            </span>\n                        },\n                    )\n                } else {\n                    let title = story.title.clone();\n                    Either::Right(view! { <A href=format!(\"/item/{}\", story.id)>{title}</A> })\n                }}\n\n            </span>\n            {(story.story_type != \"link\")\n                .then(|| {\n                    view! {\n                        \" \"\n                        <span class=\"label\">{story.story_type}</span>\n                    }\n                })}\n\n        </li>\n    }\n    .into_any()\n}\n"
  },
  {
    "path": "examples/hackernews/src/routes/story.rs",
    "content": "use crate::api;\nuse leptos::{either::Either, prelude::*};\nuse leptos_meta::Meta;\nuse leptos_router::{components::A, hooks::use_params_map};\n\n#[component]\npub fn Story() -> impl IntoView {\n    let params = use_params_map();\n    let story = Resource::new_blocking(\n        move || params.read().get(\"id\").unwrap_or_default(),\n        move |id| async move {\n            if id.is_empty() {\n                None\n            } else {\n                api::fetch_api::<api::Story>(&api::story(&format!(\"item/{id}\")))\n                    .await\n            }\n        },\n    );\n\n    Suspense(SuspenseProps::builder().fallback(|| \"Loading...\").children(ToChildren::to_children(move || Suspend::new(async move {\n        match story.await.clone() {\n            None => Either::Left(\"Story not found.\"),\n            Some(story) => {\n                Either::Right(view! {\n                    <Meta name=\"description\" content=story.title.clone()/>\n                    <div class=\"item-view\">\n                        <div class=\"item-view-header\">\n                            <a href=story.url target=\"_blank\">\n                                <h1>{story.title}</h1>\n                            </a>\n                            <span class=\"host\">\"(\" {story.domain} \")\"</span>\n                            <ShowLet some=story.user let:user>\n                                <p class=\"meta\">\n                                    {story.points} \" points | by \"\n                                    <A href=format!(\"/users/{user}\")>{user.clone()}</A>\n                                    {format!(\" {}\", story.time_ago)}\n                                </p>\n                            </ShowLet>\n                        </div>\n                        <div class=\"item-view-comments\">\n                            <p class=\"item-view-comments-header\">\n                                {if story.comments_count.unwrap_or_default() > 0 {\n                                    format!(\"{} comments\", story.comments_count.unwrap_or_default())\n                                } else {\n                                    \"No comments yet.\".into()\n                                }}\n\n                            </p>\n                            <ul class=\"comment-children\">\n                                <For\n                                    each=move || story.comments.clone().unwrap_or_default()\n                                    key=|comment| comment.id\n                                    let:comment\n                                >\n                                    <Comment comment/>\n                                </For>\n                            </ul>\n                        </div>\n                    </div>\n                })\n            }\n        }\n    }))).build())\n    .into_any()\n}\n\n#[component]\npub fn Comment(comment: api::Comment) -> impl IntoView {\n    let (open, set_open) = signal(true);\n\n    view! {\n        <li class=\"comment\">\n            <div class=\"by\">\n                <A href=format!(\n                    \"/users/{}\",\n                    comment.user.clone().unwrap_or_default(),\n                )>{comment.user.clone()}</A>\n                {format!(\" {}\", comment.time_ago)}\n            </div>\n            <div class=\"text\" inner_html=comment.content></div>\n            {(!comment.comments.is_empty())\n                .then(|| {\n                    view! {\n                        <div>\n                            <div class=\"toggle\" class:open=open>\n                                <a on:click=move |_| {\n                                    set_open.update(|n| *n = !*n)\n                                }>\n\n                                    {\n                                        let comments_len = comment.comments.len();\n                                        move || {\n                                            if open.get() {\n                                                \"[-]\".into()\n                                            } else {\n                                                format!(\n                                                    \"[+] {}{} collapsed\",\n                                                    comments_len,\n                                                    pluralize(comments_len),\n                                                )\n                                            }\n                                        }\n                                    }\n\n                                </a>\n                            </div>\n                            {move || {\n                                open\n                                    .get()\n                                    .then({\n                                        let comments = comment.comments.clone();\n                                        move || {\n                                            view! {\n                                                <ul class=\"comment-children\">\n                                                    <For\n                                                        each=move || comments.clone()\n                                                        key=|comment| comment.id\n                                                        let:comment\n                                                    >\n                                                        <Comment comment/>\n                                                    </For>\n                                                </ul>\n                                            }\n                                        }\n                                    })\n                            }}\n\n                        </div>\n                    }\n                })}\n\n        </li>\n    }.into_any()\n}\n\nfn pluralize(n: usize) -> &'static str {\n    if n == 1 {\n        \" reply\"\n    } else {\n        \" replies\"\n    }\n}\n"
  },
  {
    "path": "examples/hackernews/src/routes/users.rs",
    "content": "use crate::api::{self, User};\nuse leptos::{either::Either, prelude::*, server::Resource};\nuse leptos_router::hooks::use_params_map;\n\n#[component]\npub fn User() -> impl IntoView {\n    let params = use_params_map();\n    let user = Resource::new(\n        move || params.read().get(\"id\").unwrap_or_default(),\n        move |id| async move {\n            if id.is_empty() {\n                None\n            } else {\n                api::fetch_api::<User>(&api::user(&id)).await\n            }\n        },\n    );\n    view! {\n        <div class=\"user-view\">\n            <Suspense fallback=|| {\n                view! { \"Loading...\" }\n            }>\n                {move || Suspend::new(async move {\n                    match user.await.clone() {\n                        None => Either::Left(view! { <h1>\"User not found.\"</h1> }),\n                        Some(user) => {\n                            Either::Right(\n                                view! {\n                                    <div>\n                                        <h1>\"User: \" {user.id.clone()}</h1>\n                                        <ul class=\"meta\">\n                                            <li>\n                                                <span class=\"label\">\"Created: \"</span>\n                                                {user.created}\n                                            </li>\n                                            <li>\n                                                <span class=\"label\">\"Karma: \"</span>\n                                                {user.karma}\n                                            </li>\n                                            <li inner_html=user.about class=\"about\"></li>\n                                        </ul>\n                                        <p class=\"links\">\n                                            <a href=format!(\n                                                \"https://news.ycombinator.com/submitted?id={}\",\n                                                user.id,\n                                            )>\"submissions\"</a>\n                                            \" | \"\n                                            <a href=format!(\n                                                \"https://news.ycombinator.com/threads?id={}\",\n                                                user.id,\n                                            )>\"comments\"</a>\n                                        </p>\n                                    </div>\n                                },\n                            )\n                        }\n                    }\n                })}\n\n            </Suspense>\n        </div>\n    }\n    .into_any()\n}\n"
  },
  {
    "path": "examples/hackernews/src/routes.rs",
    "content": "pub mod nav;\npub mod stories;\npub mod story;\npub mod users;\n"
  },
  {
    "path": "examples/hackernews/style.css",
    "content": "body {\n\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n\tfont-size: 15px;\n\tbackground-color: #f2f3f5;\n\tmargin: 0;\n\tpadding-top: 55px;\n\tcolor: #34495e;\n\toverflow-y: scroll\n}\n\na {\n\tcolor: #34495e;\n\ttext-decoration: none\n}\n\n.header {\n\tbackground-color: #335d92;\n\tposition: fixed;\n\tz-index: 999;\n\theight: 55px;\n\ttop: 0;\n\tleft: 0;\n\tright: 0\n}\n\n.header .inner {\n\tmax-width: 800px;\n\tbox-sizing: border-box;\n\tmargin: 0 auto;\n\tpadding: 15px 5px\n}\n\n.header a {\n\tcolor: rgba(255, 255, 255, .8);\n\tline-height: 24px;\n\ttransition: color .15s ease;\n\tdisplay: inline-block;\n\tvertical-align: middle;\n\tfont-weight: 300;\n\tletter-spacing: .075em;\n\tmargin-right: 1.8em\n}\n\n.header a:hover {\n\tcolor: #fff\n}\n\n.header a.active {\n\tcolor: #fff;\n\tfont-weight: 400\n}\n\n.header a:nth-child(6) {\n\tmargin-right: 0\n}\n\n.header .github {\n\tcolor: #fff;\n\tfont-size: .9em;\n\tmargin: 0;\n\tfloat: right\n}\n\n.logo {\n\twidth: 24px;\n\tmargin-right: 10px;\n\tdisplay: inline-block;\n\tvertical-align: middle\n}\n\n.view {\n\tmax-width: 800px;\n\tmargin: 0 auto;\n\tposition: relative\n}\n\n.fade-enter-active,\n.fade-exit-active {\n\ttransition: all .2s ease\n}\n\n.fade-enter,\n.fade-exit-active {\n\topacity: 0\n}\n\n@media (max-width:860px) {\n\t.header .inner {\n\t\tpadding: 15px 30px\n\t}\n}\n\n@media (max-width:600px) {\n\t.header .inner {\n\t\tpadding: 15px\n\t}\n\n\t.header a {\n\t\tmargin-right: 1em\n\t}\n\n\t.header .github {\n\t\tdisplay: none\n\t}\n}\n\n.news-view {\n\tpadding-top: 45px\n}\n\n.news-list,\n.news-list-nav {\n\tbackground-color: #fff;\n\tborder-radius: 2px\n}\n\n.news-list-nav {\n\tpadding: 15px 30px;\n\tposition: fixed;\n\ttext-align: center;\n\ttop: 55px;\n\tleft: 0;\n\tright: 0;\n\tz-index: 998;\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, .1)\n}\n\n.news-list-nav .page-link {\n\tmargin: 0 1em\n}\n\n.news-list-nav .disabled {\n\tcolor: #aaa\n}\n\n.news-list {\n\tposition: absolute;\n\tmargin: 30px 0;\n\twidth: 100%;\n\ttransition: all .5s cubic-bezier(.55, 0, .1, 1)\n}\n\n.news-list ul {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0\n}\n\n@media (max-width:600px) {\n\t.news-list {\n\t\tmargin: 10px 0\n\t}\n}\n\n.news-item {\n\tbackground-color: #fff;\n\tpadding: 20px 30px 20px 80px;\n\tborder-bottom: 1px solid #eee;\n\tposition: relative;\n\tline-height: 20px\n}\n\n.news-item .score {\n\tcolor: #335d92;\n\tfont-size: 1.1em;\n\tfont-weight: 700;\n\tposition: absolute;\n\ttop: 50%;\n\tleft: 0;\n\twidth: 80px;\n\ttext-align: center;\n\tmargin-top: -10px\n}\n\n.news-item .host,\n.news-item .meta {\n\tfont-size: .85em;\n\tcolor: #626262\n}\n\n.news-item .host a,\n.news-item .meta a {\n\tcolor: #626262;\n\ttext-decoration: underline\n}\n\n.news-item .host a:hover,\n.news-item .meta a:hover {\n\tcolor: #335d92\n}\n\n.item-view-header {\n\tbackground-color: #fff;\n\tpadding: 1.8em 2em 1em;\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, .1)\n}\n\n.item-view-header h1 {\n\tdisplay: inline;\n\tfont-size: 1.5em;\n\tmargin: 0;\n\tmargin-right: .5em\n}\n\n.item-view-header .host,\n.item-view-header .meta,\n.item-view-header .meta a {\n\tcolor: #626262\n}\n\n.item-view-header .meta a {\n\ttext-decoration: underline\n}\n\n.item-view-comments {\n\tbackground-color: #fff;\n\tmargin-top: 10px;\n\tpadding: 0 2em .5em\n}\n\n.item-view-comments-header {\n\tmargin: 0;\n\tfont-size: 1.1em;\n\tpadding: 1em 0;\n\tposition: relative\n}\n\n.item-view-comments-header .spinner {\n\tdisplay: inline-block;\n\tmargin: -15px 0\n}\n\n.comment-children {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0\n}\n\n@media (max-width:600px) {\n\t.item-view-header h1 {\n\t\tfont-size: 1.25em\n\t}\n}\n\n.comment-children .comment-children {\n\tmargin-left: 1.5em\n}\n\n.comment {\n\tborder-top: 1px solid #eee;\n\tposition: relative\n}\n\n.comment .by,\n.comment .text,\n.comment .toggle {\n\tfont-size: .9em;\n\tmargin: 1em 0\n}\n\n.comment .by {\n\tcolor: #626262\n}\n\n.comment .by a {\n\tcolor: #626262;\n\ttext-decoration: underline\n}\n\n.comment .text {\n\toverflow-wrap: break-word\n}\n\n.comment .text a:hover {\n\tcolor: #335d92\n}\n\n.comment .text pre {\n\twhite-space: pre-wrap\n}\n\n.comment .toggle {\n\tbackground-color: #fffbf2;\n\tpadding: .3em .5em;\n\tborder-radius: 4px\n}\n\n.comment .toggle a {\n\tcolor: #626262;\n\tcursor: pointer\n}\n\n.comment .toggle.open {\n\tpadding: 0;\n\tbackground-color: transparent;\n\tmargin-bottom: -.5em\n}\n\n.user-view {\n\tbackground-color: #fff;\n\tbox-sizing: border-box;\n\tpadding: 2em 3em\n}\n\n.user-view h1 {\n\tmargin: 0;\n\tfont-size: 1.5em\n}\n\n.user-view .meta {\n\tlist-style-type: none;\n\tpadding: 0\n}\n\n.user-view .label {\n\tdisplay: inline-block;\n\tmin-width: 4em\n}\n\n.user-view .about {\n\tmargin: 1em 0\n}\n\n.user-view .links a {\n\ttext-decoration: underline\n}\n\n.routing-progress, .routing-progress progress {\n\theight: 10px;\n\twidth: 100%;\n}"
  },
  {
    "path": "examples/hackernews_axum/Cargo.toml",
    "content": "[package]\nname = \"hackernews_axum\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nleptos = { path = \"../../leptos\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_meta = { path = \"../../meta\" }\nleptos_router = { path = \"../../router\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\ntracing = \"0.1.40\"\ngloo-net = { version = \"0.6.0\", features = [\"http\"] }\nreqwest = { version = \"0.12.5\", features = [\"json\"] }\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nhttp = { version = \"1.1\", optional = true }\nweb-sys = { version = \"0.3.70\", features = [\"AbortController\", \"AbortSignal\"] }\nwasm-bindgen = \"0.2.105\"\nsend_wrapper = { version = \"0.6.0\", features = [\"futures\"] }\n\n[features]\ndefault = [\"csr\"]\ncsr = [\"leptos/csr\"]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"dep:http\",\n  \"leptos/ssr\",\n  \"leptos_axum\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"http\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"hackernews_axum\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/hackernews_axum/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/hackernews_axum/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"hackernews_axum\"\n"
  },
  {
    "path": "examples/hackernews_axum/README.md",
    "content": "# Leptos Hacker News Example with Axum\n\nThis example creates a basic clone of the Hacker News site. It showcases Leptos' ability to create both a client-side rendered app, and a server side rendered app with hydration, in a single repository. This repo differs from the main Hacker News example by using Axum as it's server.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` or `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/hackernews_axum/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"css\" href=\"/style.css\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/hackernews_axum/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/hackernews_axum/src/api.rs",
    "content": "use leptos::logging;\nuse serde::{de::DeserializeOwned, Deserialize, Serialize};\n\npub fn story(path: &str) -> String {\n    format!(\"https://node-hnapi.herokuapp.com/{path}\")\n}\n\npub fn user(path: &str) -> String {\n    format!(\"https://hacker-news.firebaseio.com/v0/user/{path}.json\")\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn fetch_api<T>(\n    path: &str,\n) -> impl std::future::Future<Output = Option<T>> + Send + '_\nwhere\n    T: Serialize + DeserializeOwned,\n{\n    use leptos::prelude::on_cleanup;\n    use send_wrapper::SendWrapper;\n\n    SendWrapper::new(async move {\n        let abort_controller =\n            SendWrapper::new(web_sys::AbortController::new().ok());\n        let abort_signal = abort_controller.as_ref().map(|a| a.signal());\n\n        // abort in-flight requests if, e.g., we've navigated away from this page\n        on_cleanup(move || {\n            if let Some(abort_controller) = abort_controller.take() {\n                abort_controller.abort()\n            }\n        });\n\n        gloo_net::http::Request::get(path)\n            .abort_signal(abort_signal.as_ref())\n            .send()\n            .await\n            .map_err(|e| logging::error!(\"{e}\"))\n            .ok()?\n            .json()\n            .await\n            .ok()\n    })\n}\n\n#[cfg(feature = \"ssr\")]\npub async fn fetch_api<T>(path: &str) -> Option<T>\nwhere\n    T: Serialize + DeserializeOwned,\n{\n    reqwest::get(path)\n        .await\n        .map_err(|e| logging::error!(\"{e}\"))\n        .ok()?\n        .json()\n        .await\n        .ok()\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct Story {\n    pub id: usize,\n    pub title: String,\n    pub points: Option<i32>,\n    pub user: Option<String>,\n    pub time: usize,\n    pub time_ago: String,\n    #[serde(alias = \"type\")]\n    pub story_type: String,\n    pub url: String,\n    #[serde(default)]\n    pub domain: String,\n    #[serde(default)]\n    pub comments: Option<Vec<Comment>>,\n    pub comments_count: Option<usize>,\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct Comment {\n    pub id: usize,\n    pub level: usize,\n    pub user: Option<String>,\n    pub time: usize,\n    pub time_ago: String,\n    pub content: Option<String>,\n    pub comments: Vec<Comment>,\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct User {\n    pub created: usize,\n    pub id: String,\n    pub karma: i32,\n    pub about: Option<String>,\n}\n"
  },
  {
    "path": "examples/hackernews_axum/src/error_template.rs",
    "content": "use leptos::{view, Errors, For, IntoView, RwSignal, SignalGet, View};\n\n// A basic function to display errors served by the error boundaries. Feel free to do more complicated things\n// here than just displaying them\npub fn error_template(errors: Option<RwSignal<Errors>>) -> View {\n    let Some(errors) = errors else {\n        panic!(\"No Errors found and we expected errors!\");\n    };\n\n    view! {\n      <h1>\"Errors\"</h1>\n      <For\n          // a function that returns the items we're iterating over; a signal is fine\n          each=move ||  errors.get()\n          // a unique key for each item as a reference\n          key=|(key, _)| key.clone()\n          // renders each item to a view\n          children=move | (_, error)| {\n          let error_string = error.to_string();\n            view! {\n\n              <p>\"Error: \" {error_string}</p>\n            }\n          }\n      />\n    }\n    .into_view()\n}\n"
  },
  {
    "path": "examples/hackernews_axum/src/lib.rs",
    "content": "use leptos::prelude::*;\nmod api;\nmod routes;\nuse leptos_meta::{provide_meta_context, Link, Meta, MetaTags, Stylesheet};\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router, RoutingProgress},\n    Lazy, OptionalParamSegment, ParamSegment, StaticSegment,\n};\nuse routes::{nav::*, stories::*, story::*, users::*};\nuse std::time::Duration;\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n    let (is_routing, set_is_routing) = signal(false);\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/hackernews_axum.css\"/>\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/favicon.ico\"/>\n        <Meta name=\"description\" content=\"Leptos implementation of a HackerNews demo.\"/>\n        <Router set_is_routing>\n            // shows a progress bar while async data are loading\n            <div class=\"routing-progress\">\n                <RoutingProgress is_routing max_time=Duration::from_millis(250)/>\n            </div>\n            <Nav />\n            <main>\n                <FlatRoutes fallback=|| \"Not found.\">\n                    <Route path=(StaticSegment(\"users\"), ParamSegment(\"id\")) view={Lazy::<UserRoute>::new()}/>\n                    <Route path=(StaticSegment(\"stories\"), ParamSegment(\"id\")) view={Lazy::<StoryRoute>::new()}/>\n                    <Route path=OptionalParamSegment(\"stories\") view=Stories/>\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/hackernews_axum/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::{routing::get, Router};\n    use hackernews_axum::{shell, App};\n    use leptos::config::get_configuration;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n\n    let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .route(\n            \"/favicon.ico\",\n            get(|| async {\n                (\n                    [(\"content-type\", \"image/x-icon\")],\n                    include_bytes!(\"../public/favicon.ico\"),\n                )\n            }),\n        )\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    println!(\"listening on {}\", addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n// client-only stuff for Trunk\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    use hackernews_axum::*;\n\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(App);\n}\n"
  },
  {
    "path": "examples/hackernews_axum/src/routes/nav.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::components::A;\n\n#[component]\npub fn Nav() -> impl IntoView {\n    view! {\n        <header class=\"header\">\n            <nav class=\"inner\">\n                <A href=\"/home\">\n                    <strong>\"HN\"</strong>\n                </A>\n                <A href=\"/new\">\n                    <strong>\"New\"</strong>\n                </A>\n                <A href=\"/show\">\n                    <strong>\"Show\"</strong>\n                </A>\n                <A href=\"/ask\">\n                    <strong>\"Ask\"</strong>\n                </A>\n                <A href=\"/job\">\n                    <strong>\"Jobs\"</strong>\n                </A>\n                <a class=\"github\" href=\"http://github.com/leptos-rs/leptos\" target=\"_blank\" rel=\"noreferrer\">\n                    \"Built with Leptos\"\n                </a>\n            </nav>\n        </header>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_axum/src/routes/stories.rs",
    "content": "use crate::api;\nuse leptos::{either::Either, prelude::*};\nuse leptos_router::{\n    components::A,\n    hooks::{use_params_map, use_query_map},\n};\n\nfn category(from: &str) -> &'static str {\n    match from {\n        \"new\" => \"newest\",\n        \"show\" => \"show\",\n        \"ask\" => \"ask\",\n        \"job\" => \"jobs\",\n        _ => \"news\",\n    }\n}\n\n#[component]\npub fn Stories() -> impl IntoView {\n    let query = use_query_map();\n    let params = use_params_map();\n    let page = move || {\n        query\n            .read()\n            .get(\"page\")\n            .and_then(|page| page.parse::<usize>().ok())\n            .unwrap_or(1)\n    };\n    let story_type = move || {\n        params\n            .read()\n            .get(\"stories\")\n            .unwrap_or_else(|| \"top\".to_string())\n    };\n    let stories = Resource::new(\n        move || (page(), story_type()),\n        move |(page, story_type)| async move {\n            let path = format!(\"{}?page={}\", category(&story_type), page);\n            api::fetch_api::<Vec<api::Story>>(&api::story(&path)).await\n        },\n    );\n    let (pending, set_pending) = signal(false);\n\n    let hide_more_link = move || match &*stories.read() {\n        Some(Some(stories)) => stories.len() < 28,\n        _ => true\n    } || pending.get();\n\n    view! {\n        <div class=\"news-view\">\n            <div class=\"news-list-nav\">\n                <span>\n                    {move || if page() > 1 {\n                        Either::Left(view! {\n                            <a class=\"page-link\"\n                                href=move || format!(\"/{}?page={}\", story_type(), page() - 1)\n                                aria-label=\"Previous Page\"\n                            >\n                                \"< prev\"\n                            </a>\n                        })\n                    } else {\n                        Either::Right(view! {\n                            <span class=\"page-link disabled\" aria-hidden=\"true\">\n                                \"< prev\"\n                            </span>\n                        })\n                    }}\n                </span>\n                <span>\"page \" {page}</span>\n                <Suspense>\n                    <span class=\"page-link\"\n                        class:disabled=hide_more_link\n                        aria-hidden=hide_more_link\n                    >\n                        <a href=move || format!(\"/{}?page={}\", story_type(), page() + 1)\n                            aria-label=\"Next Page\"\n                        >\n                            \"more >\"\n                        </a>\n                    </span>\n                </Suspense>\n            </div>\n            <main class=\"news-list\">\n                <div>\n                    <Transition\n                        fallback=move || view! { <p>\"Loading...\"</p> }\n                        set_pending\n                    >\n                        <Show when=move || stories.read().as_ref().map(Option::is_none).unwrap_or(false)>\n                        >\n                            <p>\"Error loading stories.\"</p>\n                        </Show>\n                        <ul>\n                            <For\n                                each=move || stories.get().unwrap_or_default().unwrap_or_default()\n                                key=|story| story.id\n                                let:story\n                            >\n                                <Story story/>\n                            </For>\n                        </ul>\n                    </Transition>\n                </div>\n            </main>\n        </div>\n    }\n}\n\n#[component]\nfn Story(story: api::Story) -> impl IntoView {\n    view! {\n         <li class=\"news-item\">\n            <span class=\"score\">{story.points}</span>\n            <span class=\"title\">\n                {if !story.url.starts_with(\"item?id=\") {\n                    Either::Left(view! {\n                        <span>\n                            <a href=story.url target=\"_blank\" rel=\"noreferrer\">\n                                {story.title.clone()}\n                            </a>\n                            <span class=\"host\">\"(\"{story.domain}\")\"</span>\n                        </span>\n                    })\n                } else {\n                    let title = story.title.clone();\n                    Either::Right(view! { <A href=format!(\"/stories/{}\", story.id)>{title}</A> })\n                }}\n            </span>\n            <br />\n            <span class=\"meta\">\n                {if story.story_type != \"job\" {\n                    Either::Left(view! {\n                        <span>\n                            {\"by \"}\n                            <ShowLet some=story.user let:user>\n                                <A href=format!(\"/users/{user}\")>{user.clone()}</A>\n                            </ShowLet>\n                            {format!(\" {} | \", story.time_ago)}\n                            <A href=format!(\"/stories/{}\", story.id)>\n                                {if story.comments_count.unwrap_or_default() > 0 {\n                                    format!(\"{} comments\", story.comments_count.unwrap_or_default())\n                                } else {\n                                    \"discuss\".into()\n                                }}\n                            </A>\n                        </span>\n                    })\n                } else {\n                    let title = story.title.clone();\n                    Either::Right(view! { <A href=format!(\"/item/{}\", story.id)>{title}</A> })\n                }}\n            </span>\n            {(story.story_type != \"link\").then(|| view! {\n                \" \"\n                <span class=\"label\">{story.story_type}</span>\n            })}\n        </li>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_axum/src/routes/story.rs",
    "content": "use crate::api::{self, Story};\nuse leptos::{either::Either, prelude::*};\nuse leptos_meta::Meta;\nuse leptos_router::{\n    components::A, hooks::use_params_map, lazy_route, LazyRoute,\n};\n\n#[derive(Debug)]\npub struct StoryRoute {\n    story: Resource<Option<Story>>,\n}\n\n#[lazy_route]\nimpl LazyRoute for StoryRoute {\n    fn data() -> Self {\n        let params = use_params_map();\n        let story = Resource::new_blocking(\n            move || params.read().get(\"id\").unwrap_or_default(),\n            move |id| async move {\n                if id.is_empty() {\n                    None\n                } else {\n                    api::fetch_api::<api::Story>(&api::story(&format!(\n                        \"item/{id}\"\n                    )))\n                    .await\n                }\n            },\n        );\n        Self { story }\n    }\n\n    fn view(this: Self) -> AnyView {\n        let StoryRoute { story } = this;\n        Suspense(SuspenseProps::builder().fallback(|| \"Loading...\").children(ToChildren::to_children(move || Suspend::new(async move {\n        match story.await.clone() {\n            None => Either::Left(\"Story not found.\"),\n            Some(story) => {\n                Either::Right(view! {\n                    <Meta name=\"description\" content=story.title.clone()/>\n                    <div class=\"item-view\">\n                        <div class=\"item-view-header\">\n                            <a href=story.url target=\"_blank\">\n                                <h1>{story.title}</h1>\n                            </a>\n                            <span class=\"host\">\n                                \"(\"{story.domain}\")\"\n                            </span>\n                            <ShowLet some=story.user let:user>\n                                <p class=\"meta\">\n                                    {story.points}\n                                    \" points | by \"\n                                    <A href=format!(\"/users/{user}\")>{user.clone()}</A>\n                                    {format!(\" {}\", story.time_ago)}\n                                </p>\n                            </ShowLet>\n                        </div>\n                        <div class=\"item-view-comments\">\n                            <p class=\"item-view-comments-header\">\n                                {if story.comments_count.unwrap_or_default() > 0 {\n                                    format!(\"{} comments\", story.comments_count.unwrap_or_default())\n                                } else {\n                                    \"No comments yet.\".into()\n                                }}\n                            </p>\n                            <ul class=\"comment-children\">\n                                <For\n                                    each=move || story.comments.clone().unwrap_or_default()\n                                    key=|comment| comment.id\n                                    let:comment\n                                >\n                                    <Comment comment />\n                                </For>\n                            </ul>\n                        </div>\n                    </div>\n                })\n            }\n        }\n    }))).build()).into_any()\n    }\n}\n\n#[component]\npub fn Comment(comment: api::Comment) -> impl IntoView {\n    let (open, set_open) = signal(true);\n\n    view! {\n        <li class=\"comment\">\n        <div class=\"by\">\n            <A href=format!(\"/users/{}\", comment.user.clone().unwrap_or_default())>{comment.user.clone()}</A>\n            {format!(\" {}\", comment.time_ago)}\n        </div>\n        <div class=\"text\" inner_html=comment.content></div>\n        {(!comment.comments.is_empty()).then(|| {\n            view! {\n                <div>\n                    <div class=\"toggle\" class:open=open>\n                        <a on:click=move |_| set_open.update(|n| *n = !*n)>\n                            {\n                                let comments_len = comment.comments.len();\n                                move || if open.get() {\n                                    \"[-]\".into()\n                                } else {\n                                    format!(\"[+] {}{} collapsed\", comments_len, pluralize(comments_len))\n                                }\n                            }\n                        </a>\n                    </div>\n                    {move || open.get().then({\n                        let comments = comment.comments.clone();\n                        move || view! {\n                            <ul class=\"comment-children\">\n                                <For\n                                    each=move || comments.clone()\n                                    key=|comment| comment.id\n                                    let:comment\n                                >\n                                    <Comment comment />\n                                </For>\n                            </ul>\n                        }\n                    })}\n                </div>\n            }\n        })}\n        </li>\n    }.into_any()\n}\n\nfn pluralize(n: usize) -> &'static str {\n    if n == 1 {\n        \" reply\"\n    } else {\n        \" replies\"\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_axum/src/routes/users.rs",
    "content": "use crate::api::{self, User};\nuse leptos::{either::Either, prelude::*, server::Resource};\nuse leptos_router::{hooks::use_params_map, lazy_route, LazyRoute};\n\n#[derive(Debug)]\npub struct UserRoute {\n    user: Resource<Option<User>>,\n}\n\n#[lazy_route]\nimpl LazyRoute for UserRoute {\n    fn data() -> Self {\n        let params = use_params_map();\n        let user = Resource::new(\n            move || params.read().get(\"id\").unwrap_or_default(),\n            move |id| async move {\n                if id.is_empty() {\n                    None\n                } else {\n                    api::fetch_api::<User>(&api::user(&id)).await\n                }\n            },\n        );\n        UserRoute { user }\n    }\n\n    fn view(this: Self) -> AnyView {\n        let UserRoute { user } = this;\n        view! {\n            <div class=\"user-view\">\n                <Suspense fallback=|| view! { \"Loading...\" }>\n                    {move || Suspend::new(async move { match user.await.clone() {\n                        None => Either::Left(view! {  <h1>\"User not found.\"</h1> }),\n                        Some(user) => Either::Right(view! {\n                            <div>\n                                <h1>\"User: \" {user.id.clone()}</h1>\n                                <ul class=\"meta\">\n                                    <li>\n                                        <span class=\"label\">\"Created: \"</span> {user.created}\n                                    </li>\n                                    <li>\n                                    <span class=\"label\">\"Karma: \"</span> {user.karma}\n                                    </li>\n                                    <li inner_html={user.about} class=\"about\"></li>\n                                </ul>\n                                <p class=\"links\">\n                                    <a href=format!(\"https://news.ycombinator.com/submitted?id={}\", user.id)>\"submissions\"</a>\n                                    \" | \"\n                                    <a href=format!(\"https://news.ycombinator.com/threads?id={}\", user.id)>\"comments\"</a>\n                                </p>\n                            </div>\n                        })\n                    }})}\n                </Suspense>\n            </div>\n        }.into_any()\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_axum/src/routes.rs",
    "content": "pub mod nav;\npub mod stories;\npub mod story;\npub mod users;\n"
  },
  {
    "path": "examples/hackernews_axum/style.css",
    "content": "body {\n\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n\tfont-size: 15px;\n\tbackground-color: #f2f3f5;\n\tmargin: 0;\n\tpadding-top: 55px;\n\tcolor: #34495e;\n\toverflow-y: scroll\n}\n\na {\n\tcolor: #34495e;\n\ttext-decoration: none\n}\n\n.header {\n\tbackground-color: #335d92;\n\tposition: fixed;\n\tz-index: 999;\n\theight: 55px;\n\ttop: 0;\n\tleft: 0;\n\tright: 0\n}\n\n.header .inner {\n\tmax-width: 800px;\n\tbox-sizing: border-box;\n\tmargin: 0 auto;\n\tpadding: 15px 5px\n}\n\n.header a {\n\tcolor: rgba(255, 255, 255, .8);\n\tline-height: 24px;\n\ttransition: color .15s ease;\n\tdisplay: inline-block;\n\tvertical-align: middle;\n\tfont-weight: 300;\n\tletter-spacing: .075em;\n\tmargin-right: 1.8em\n}\n\n.header a:hover {\n\tcolor: #fff\n}\n\n.header a.active {\n\tcolor: #fff;\n\tfont-weight: 400\n}\n\n.header a:nth-child(6) {\n\tmargin-right: 0\n}\n\n.header .github {\n\tcolor: #fff;\n\tfont-size: .9em;\n\tmargin: 0;\n\tfloat: right\n}\n\n.logo {\n\twidth: 24px;\n\tmargin-right: 10px;\n\tdisplay: inline-block;\n\tvertical-align: middle\n}\n\n.view {\n\tmax-width: 800px;\n\tmargin: 0 auto;\n\tposition: relative\n}\n\n.fade-enter-active,\n.fade-exit-active {\n\ttransition: all .2s ease\n}\n\n.fade-enter,\n.fade-exit-active {\n\topacity: 0\n}\n\n@media (max-width:860px) {\n\t.header .inner {\n\t\tpadding: 15px 30px\n\t}\n}\n\n@media (max-width:600px) {\n\t.header .inner {\n\t\tpadding: 15px\n\t}\n\n\t.header a {\n\t\tmargin-right: 1em\n\t}\n\n\t.header .github {\n\t\tdisplay: none\n\t}\n}\n\n.news-view {\n\tpadding-top: 45px\n}\n\n.news-list,\n.news-list-nav {\n\tbackground-color: #fff;\n\tborder-radius: 2px\n}\n\n.news-list-nav {\n\tpadding: 15px 30px;\n\tposition: fixed;\n\ttext-align: center;\n\ttop: 55px;\n\tleft: 0;\n\tright: 0;\n\tz-index: 998;\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, .1)\n}\n\n.news-list-nav .page-link {\n\tmargin: 0 1em\n}\n\n.news-list-nav .disabled {\n\tcolor: #aaa\n}\n\n.news-list {\n\tposition: absolute;\n\tmargin: 30px 0;\n\twidth: 100%;\n\ttransition: all .5s cubic-bezier(.55, 0, .1, 1)\n}\n\n.news-list ul {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0\n}\n\n@media (max-width:600px) {\n\t.news-list {\n\t\tmargin: 10px 0\n\t}\n}\n\n.news-item {\n\tbackground-color: #fff;\n\tpadding: 20px 30px 20px 80px;\n\tborder-bottom: 1px solid #eee;\n\tposition: relative;\n\tline-height: 20px\n}\n\n.news-item .score {\n\tcolor: #335d92;\n\tfont-size: 1.1em;\n\tfont-weight: 700;\n\tposition: absolute;\n\ttop: 50%;\n\tleft: 0;\n\twidth: 80px;\n\ttext-align: center;\n\tmargin-top: -10px\n}\n\n.news-item .host,\n.news-item .meta {\n\tfont-size: .85em;\n\tcolor: #626262\n}\n\n.news-item .host a,\n.news-item .meta a {\n\tcolor: #626262;\n\ttext-decoration: underline\n}\n\n.news-item .host a:hover,\n.news-item .meta a:hover {\n\tcolor: #335d92\n}\n\n.item-view-header {\n\tbackground-color: #fff;\n\tpadding: 1.8em 2em 1em;\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, .1)\n}\n\n.item-view-header h1 {\n\tdisplay: inline;\n\tfont-size: 1.5em;\n\tmargin: 0;\n\tmargin-right: .5em\n}\n\n.item-view-header .host,\n.item-view-header .meta,\n.item-view-header .meta a {\n\tcolor: #626262\n}\n\n.item-view-header .meta a {\n\ttext-decoration: underline\n}\n\n.item-view-comments {\n\tbackground-color: #fff;\n\tmargin-top: 10px;\n\tpadding: 0 2em .5em\n}\n\n.item-view-comments-header {\n\tmargin: 0;\n\tfont-size: 1.1em;\n\tpadding: 1em 0;\n\tposition: relative\n}\n\n.item-view-comments-header .spinner {\n\tdisplay: inline-block;\n\tmargin: -15px 0\n}\n\n.comment-children {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0\n}\n\n@media (max-width:600px) {\n\t.item-view-header h1 {\n\t\tfont-size: 1.25em\n\t}\n}\n\n.comment-children .comment-children {\n\tmargin-left: 1.5em\n}\n\n.comment {\n\tborder-top: 1px solid #eee;\n\tposition: relative\n}\n\n.comment .by,\n.comment .text,\n.comment .toggle {\n\tfont-size: .9em;\n\tmargin: 1em 0\n}\n\n.comment .by {\n\tcolor: #626262\n}\n\n.comment .by a {\n\tcolor: #626262;\n\ttext-decoration: underline\n}\n\n.comment .text {\n\toverflow-wrap: break-word\n}\n\n.comment .text a:hover {\n\tcolor: #335d92\n}\n\n.comment .text pre {\n\twhite-space: pre-wrap\n}\n\n.comment .toggle {\n\tbackground-color: #fffbf2;\n\tpadding: .3em .5em;\n\tborder-radius: 4px\n}\n\n.comment .toggle a {\n\tcolor: #626262;\n\tcursor: pointer\n}\n\n.comment .toggle.open {\n\tpadding: 0;\n\tbackground-color: transparent;\n\tmargin-bottom: -.5em\n}\n\n.user-view {\n\tbackground-color: #fff;\n\tbox-sizing: border-box;\n\tpadding: 2em 3em\n}\n\n.user-view h1 {\n\tmargin: 0;\n\tfont-size: 1.5em\n}\n\n.user-view .meta {\n\tlist-style-type: none;\n\tpadding: 0\n}\n\n.user-view .label {\n\tdisplay: inline-block;\n\tmin-width: 4em\n}\n\n.user-view .about {\n\tmargin: 1em 0\n}\n\n.user-view .links a {\n\ttext-decoration: underline\n}"
  },
  {
    "path": "examples/hackernews_islands_axum/.cargo/config.wasm.toml",
    "content": "[unstable]\nbuild-std = [\"std\", \"panic_abort\", \"core\", \"alloc\"]\nbuild-std-features = [\"panic_immediate_abort\"]\n"
  },
  {
    "path": "examples/hackernews_islands_axum/Cargo.toml",
    "content": "[package]\nname = \"hackernews_islands\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nleptos = { path = \"../../leptos\", features = [\"islands\"] }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_meta = { path = \"../../meta\" }\nleptos_router = { path = \"../../router\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\ntracing = \"0.1.40\"\ngloo-net = { version = \"0.6.0\", features = [\"http\"] }\nreqwest = { version = \"0.12.5\", features = [\"json\"] }\naxum = { version = \"0.8.1\", optional = true, features = [\"http2\"] }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\n  \"fs\",\n  \"compression-gzip\",\n  \"compression-br\",\n], optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nhttp = { version = \"1.1\", optional = true }\nweb-sys = { version = \"0.3.70\", features = [\"AbortController\", \"AbortSignal\"] }\nwasm-bindgen = \"0.2.93\"\nrust-embed = { version = \"8.5\", features = [\n  \"axum\",\n  \"mime_guess\",\n  \"tokio\",\n], optional = true }\nmime_guess = { version = \"2.0\", optional = true }\n\n[features]\ncsr = [\"leptos/csr\"]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"dep:http\",\n  \"dep:rust-embed\",\n  \"dep:mime_guess\",\n  \"leptos/ssr\",\n  \"leptos_axum\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"http\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\nmax_combination_size = 2\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"hackernews\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\n#site-addr = \"127.0.0.1:3000\"\nsite-addr = \"0.0.0.0:8080\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# This feature will add a hash to the filename of assets.\n# This is useful here because our files are precompressed and use a `Cache-Control` policy to reduce HTTP requests\n#\n# Optional. Defaults to false.\nhash_file = true\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/hackernews_islands_axum/Dockerfile",
    "content": "FROM rustlang/rust:nightly-bullseye as builder\nRUN wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz\n#RUN tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz\n#RUN cp cargo-binstall /usr/local/cargo/bin \n#RUN cargo binstall cargo-leptos -y\n#RUN rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu\n#RUN rustup target add wasm32-unknown-unknown\nRUN mkdir -p /app\nWORKDIR /app\nCOPY . .\nRUN cargo build --release --no-default-features --features=ssr\nRUN ls -l /app/target\n\nFROM rustlang/rust:nightly-bullseye as runner\nCOPY --from=builder /app/target/release/hackernews /app/\nCOPY --from=builder /app/pkg /app\nCOPY --from=builder /app/Cargo.toml /app/\nWORKDIR /app\nENV RUST_LOG=\"info\"\nENV LEPTOS_OUTPUT_NAME=\"hackernews\"\nENV APP_ENVIRONMENT=\"production\"\nENV LEPTOS_SITE_ADDR=\"0.0.0.0:8080\"\nENV LEPTOS_SITE_ROOT=\"site\"\nEXPOSE 8080\nCMD [\"/app/hackernews\"]"
  },
  {
    "path": "examples/hackernews_islands_axum/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/hackernews_islands_axum/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/cargo-leptos-compress.toml\" },\n]\n\n[tasks.ci]\ndependencies = [\n  \"prepare\",\n  \"make-target-site-dir\",\n  \"lint\",\n  \"test-flow\",\n  \"integration-test\",\n]\n\n[env]\nCLIENT_PROCESS_NAME = \"hackernews_islands\"\n"
  },
  {
    "path": "examples/hackernews_islands_axum/README.md",
    "content": "# Leptos Hacker News Example with Axum\n\nThis example creates a basic clone of the Hacker News site. It showcases Leptos' ability to:\n- Create a client-side rendered app\n- Create a server side rendered app with hydration\n- Precompress static assets and bundle those in with the server binary\n\nThis repo differs from the main Hacker News example by using Axum as it's server, precompressing and embedding static assets into the binary, and dynamically compressing the generated HTML.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `cargo leptos watch --release -P` to run this example.\n"
  },
  {
    "path": "examples/hackernews_islands_axum/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"css\" href=\"/style.css\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/hackernews_islands_axum/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/api.rs",
    "content": "#[cfg(feature = \"ssr\")]\nuse serde::de::DeserializeOwned;\nuse serde::{Deserialize, Serialize};\n\n#[cfg(feature = \"ssr\")]\npub fn story(path: &str) -> String {\n    format!(\"https://node-hnapi.herokuapp.com/{path}\")\n}\n\n#[cfg(feature = \"ssr\")]\npub fn user(path: &str) -> String {\n    format!(\"https://hacker-news.firebaseio.com/v0/user/{path}.json\")\n}\n\n#[cfg(feature = \"ssr\")]\npub async fn fetch_api<T>(path: &str) -> Option<T>\nwhere\n    T: Serialize + DeserializeOwned,\n{\n    use leptos::logging;\n    reqwest::get(path)\n        .await\n        .map_err(|e| logging::error!(\"{e}\"))\n        .ok()?\n        .json()\n        .await\n        .ok()\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct Story {\n    pub id: usize,\n    pub title: String,\n    pub points: Option<i32>,\n    pub user: Option<String>,\n    pub time: usize,\n    pub time_ago: String,\n    #[serde(alias = \"type\")]\n    pub story_type: String,\n    pub url: String,\n    #[serde(default)]\n    pub domain: String,\n    #[serde(default)]\n    pub comments: Option<Vec<Comment>>,\n    pub comments_count: Option<usize>,\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct Comment {\n    pub id: usize,\n    pub level: usize,\n    pub user: Option<String>,\n    pub time: usize,\n    pub time_ago: String,\n    pub content: Option<String>,\n    pub comments: Vec<Comment>,\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct User {\n    pub created: usize,\n    pub id: String,\n    pub karma: i32,\n    pub about: Option<String>,\n}\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/fallback.rs",
    "content": "use axum::{\n    body::Body,\n    http::{header, Request, Response, StatusCode, Uri},\n    response::{IntoResponse, Response as AxumResponse},\n};\nuse rust_embed::Embed;\nuse std::borrow::Cow;\n\n#[cfg(not(debug_assertions))]\nconst DEV_MODE: bool = false;\n\n#[cfg(debug_assertions)]\nconst DEV_MODE: bool = true;\n\n#[derive(Embed)]\n#[folder = \"target/site/\"]\nstruct Assets;\n\npub async fn file_and_error_handler(\n    uri: Uri,\n    req: Request<Body>,\n) -> AxumResponse {\n    let accept_encoding = req\n        .headers()\n        .get(\"accept-encoding\")\n        .map(|h| h.to_str().unwrap_or(\"none\"))\n        .unwrap_or(\"none\")\n        .to_string();\n    let static_result = get_static_file(uri.clone(), accept_encoding).await;\n\n    match static_result {\n        Ok(res) => {\n            if res.status() == StatusCode::OK {\n                res.into_response()\n            } else {\n                (StatusCode::NOT_FOUND, \"Not found.\").into_response()\n            }\n        }\n        Err(e) => e.into_response(),\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    accept_encoding: String,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let (_, path) = uri.path().split_at(1); // split off the first `/`\n    let mime = mime_guess::from_path(path);\n\n    let (path, encoding) = if DEV_MODE {\n        // during DEV, don't care about the precompression -> faster workflow\n        (Cow::from(path), \"none\")\n    } else if accept_encoding.contains(\"br\") {\n        (Cow::from(format!(\"{}.br\", path)), \"br\")\n    } else if accept_encoding.contains(\"gzip\") {\n        (Cow::from(format!(\"{}.gz\", path)), \"gzip\")\n    } else {\n        (Cow::from(path), \"none\")\n    };\n\n    match Assets::get(path.as_ref()) {\n        Some(content) => {\n            let body = Body::from(content.data);\n\n            let res = match DEV_MODE {\n                true => Response::builder()\n                    .header(\n                        header::CONTENT_TYPE,\n                        mime.first_or_octet_stream().as_ref(),\n                    )\n                    .header(header::CONTENT_ENCODING, encoding)\n                    .body(body)\n                    .unwrap(),\n                false => Response::builder()\n                    .header(header::CACHE_CONTROL, \"max-age=86400\")\n                    .header(\n                        header::CONTENT_TYPE,\n                        mime.first_or_octet_stream().as_ref(),\n                    )\n                    .header(header::CONTENT_ENCODING, encoding)\n                    .body(body)\n                    .unwrap(),\n            };\n\n            Ok(res.into_response())\n        }\n\n        None => {\n            eprintln!(\">> Asset {} not found\", path);\n            for a in Assets::iter() {\n                eprintln!(\"Available asset: {}\", a);\n            }\n\n            Err((StatusCode::NOT_FOUND, \"Not found\".to_string()))\n        }\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/lib.rs",
    "content": "use leptos::prelude::*;\nmod api;\nmod routes;\nuse leptos_meta::{provide_meta_context, Link, Meta, MetaTags, Stylesheet};\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router},\n    OptionalParamSegment, ParamSegment, StaticSegment,\n};\nuse routes::{nav::*, stories::*, story::*, users::*};\n#[cfg(feature = \"ssr\")]\npub mod fallback;\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options islands=true/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/hackernews.css\"/>\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/favicon.ico\"/>\n        <Meta name=\"description\" content=\"Leptos implementation of a HackerNews demo.\"/>\n        <Router>\n            <Nav />\n            <main>\n                <FlatRoutes fallback=|| \"Not found.\">\n                    <Route path=(StaticSegment(\"users\"), ParamSegment(\"id\")) view=User/>\n                    <Route path=(StaticSegment(\"stories\"), ParamSegment(\"id\")) view=Story/>\n                    <Route path=OptionalParamSegment(\"stories\") view=Stories/>\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_islands();\n}\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::routing::get;\n    pub use axum::Router;\n    use hackernews_islands::*;\n    pub use leptos::config::get_configuration;\n    pub use leptos_axum::{generate_route_list, LeptosRoutes};\n    use tower_http::compression::{\n        predicate::{NotForContentType, SizeAbove},\n        CompressionLayer, CompressionLevel, Predicate,\n    };\n\n    let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    let predicate = SizeAbove::new(1500) // files smaller than 1501 bytes are not compressed, since the MTU (Maximum Transmission Unit) of a TCP packet is 1500 bytes\n        .and(NotForContentType::GRPC)\n        .and(NotForContentType::IMAGES)\n        // prevent compressing assets that are already statically compressed\n        .and(NotForContentType::const_new(\"application/javascript\"))\n        .and(NotForContentType::const_new(\"application/wasm\"))\n        .and(NotForContentType::const_new(\"text/css\"));\n\n    // build our application with a route\n    let app = Router::new()\n        .route(\"/favicon.ico\", get(fallback::file_and_error_handler))\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .layer(\n            CompressionLayer::new()\n                .quality(CompressionLevel::Fastest)\n                .compress_when(predicate),\n        )\n        .fallback(fallback::file_and_error_handler)\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    println!(\"listening on {}\", addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n// client-only stuff for Trunk\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    use hackernews_islands::*;\n    use leptos::prelude::*;\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(App);\n}\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/routes/nav.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::components::A;\n\n#[component]\npub fn Nav() -> impl IntoView {\n    view! {\n        <header class=\"header\">\n            <nav class=\"inner\">\n                <A href=\"/home\">\n                    <strong>\"HN\"</strong>\n                </A>\n                <A href=\"/new\">\n                    <strong>\"New\"</strong>\n                </A>\n                <A href=\"/show\">\n                    <strong>\"Show\"</strong>\n                </A>\n                <A href=\"/ask\">\n                    <strong>\"Ask\"</strong>\n                </A>\n                <A href=\"/job\">\n                    <strong>\"Jobs\"</strong>\n                </A>\n                <a class=\"github\" href=\"http://github.com/leptos-rs/leptos\" target=\"_blank\" rel=\"noreferrer\">\n                    \"Built with Leptos\"\n                </a>\n            </nav>\n        </header>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/routes/stories.rs",
    "content": "use crate::api;\nuse leptos::{either::Either, prelude::*};\nuse leptos_router::{\n    components::A,\n    hooks::{use_params_map, use_query_map},\n};\n\nfn category(from: &str) -> String {\n    match from {\n        \"new\" => \"newest\",\n        \"show\" => \"show\",\n        \"ask\" => \"ask\",\n        \"job\" => \"jobs\",\n        _ => \"news\",\n    }\n    .to_string()\n}\n\n#[server]\npub async fn fetch_stories(\n    story_type: String,\n    page: usize,\n) -> Result<Vec<api::Story>, ServerFnError> {\n    let path = format!(\"{}?page={}\", category(&story_type), page);\n    Ok(api::fetch_api::<Vec<api::Story>>(&api::story(&path))\n        .await\n        .unwrap_or_default())\n}\n\n#[component]\npub fn Stories() -> impl IntoView {\n    let query = use_query_map();\n    let params = use_params_map();\n    let page = move || {\n        query\n            .read()\n            .get(\"page\")\n            .and_then(|page| page.parse::<usize>().ok())\n            .unwrap_or(1)\n    };\n    let story_type = move || {\n        params\n            .read()\n            .get(\"stories\")\n            .unwrap_or_else(|| \"top\".to_string())\n    };\n    let stories = Resource::new(\n        move || (page(), story_type()),\n        move |(page, story_type)| async move {\n            fetch_stories(story_type, page).await.ok()\n        },\n    );\n    let (pending, set_pending) = signal(false);\n\n    let hide_more_link = move || match &*stories.read() {\n        Some(Some(stories)) => stories.len() < 28,\n        _ => true\n    } || pending.get();\n\n    view! {\n        <div class=\"news-view\">\n            <div class=\"news-list-nav\">\n                <span>\n                    {move || if page() > 1 {\n                        Either::Left(view! {\n                            <a class=\"page-link\"\n                                href=move || format!(\"/{}?page={}\", story_type(), page() - 1)\n                                aria-label=\"Previous Page\"\n                            >\n                                \"< prev\"\n                            </a>\n                        })\n                    } else {\n                        Either::Right(view! {\n                            <span class=\"page-link disabled\" aria-hidden=\"true\">\n                                \"< prev\"\n                            </span>\n                        })\n                    }}\n                </span>\n                <span>\"page \" {page}</span>\n                <Suspense>\n                    <span class=\"page-link\"\n                        class:disabled=hide_more_link\n                        aria-hidden=hide_more_link\n                    >\n                        <a href=move || format!(\"/{}?page={}\", story_type(), page() + 1)\n                            aria-label=\"Next Page\"\n                        >\n                            \"more >\"\n                        </a>\n                    </span>\n                </Suspense>\n            </div>\n            <main class=\"news-list\">\n                <div>\n                    <Transition\n                        fallback=move || view! { <p>\"Loading...\"</p> }\n                        set_pending\n                    >\n                        <Show when=move || stories.read().as_ref().map(Option::is_none).unwrap_or(false)>\n                        >\n                            <p>\"Error loading stories.\"</p>\n                        </Show>\n                        <ul>\n                            <For\n                                each=move || stories.get().unwrap_or_default().unwrap_or_default()\n                                key=|story| story.id\n                                let:story\n                            >\n                                <Story story/>\n                            </For>\n                        </ul>\n                    </Transition>\n                </div>\n            </main>\n        </div>\n    }\n}\n\n#[component]\nfn Story(story: api::Story) -> impl IntoView {\n    view! {\n         <li class=\"news-item\">\n            <span class=\"score\">{story.points}</span>\n            <span class=\"title\">\n                {if !story.url.starts_with(\"item?id=\") {\n                    Either::Left(view! {\n                        <span>\n                            <a href=story.url target=\"_blank\" rel=\"noreferrer\">\n                                {story.title.clone()}\n                            </a>\n                            <span class=\"host\">\"(\"{story.domain}\")\"</span>\n                        </span>\n                    })\n                } else {\n                    let title = story.title.clone();\n                    Either::Right(view! { <A href=format!(\"/stories/{}\", story.id)>{title}</A> })\n                }}\n            </span>\n            <br />\n            <span class=\"meta\">\n                {if story.story_type != \"job\" {\n                    Either::Left(view! {\n                        <span>\n                            \"by \"\n                            <ShowLet some=story.user let:user>\n                                <A href=format!(\"/users/{user}\")>{user.clone()}</A>\n                            </ShowLet>\n                            {format!(\" {} | \", story.time_ago)}\n                            <A href=format!(\"/stories/{}\", story.id)>\n                                {if story.comments_count.unwrap_or_default() > 0 {\n                                    format!(\"{} comments\", story.comments_count.unwrap_or_default())\n                                } else {\n                                    \"discuss\".into()\n                                }}\n                            </A>\n                        </span>\n                    })\n                } else {\n                    let title = story.title.clone();\n                    Either::Right(view! { <A href=format!(\"/item/{}\", story.id)>{title}</A> })\n                }}\n            </span>\n            {(story.story_type != \"link\").then(|| view! {\n                \" \"\n                <span class=\"label\">{story.story_type}</span>\n            })}\n        </li>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/routes/story.rs",
    "content": "use crate::api;\nuse leptos::{either::Either, prelude::*};\nuse leptos_meta::Meta;\nuse leptos_router::{components::A, hooks::use_params_map};\n\n#[server]\npub async fn fetch_story(\n    id: String,\n) -> Result<Option<api::Story>, ServerFnError> {\n    Ok(api::fetch_api::<api::Story>(&api::story(&format!(\"item/{id}\"))).await)\n}\n\n#[component]\npub fn Story() -> impl IntoView {\n    let params = use_params_map();\n    let story = Resource::new_blocking(\n        move || params.read().get(\"id\").unwrap_or_default(),\n        move |id| async move {\n            if id.is_empty() {\n                Ok(None)\n            } else {\n                fetch_story(id).await\n            }\n        },\n    );\n\n    Suspense(SuspenseProps::builder().fallback(|| \"Loading...\").children(ToChildren::to_children(move || Suspend::new(async move {\n        match story.await.ok().flatten() {\n            None => Either::Left(\"Story not found.\"),\n            Some(story) => {\n                Either::Right(view! {\n                    <Meta name=\"description\" content=story.title.clone()/>\n                    <div class=\"item-view\">\n                        <div class=\"item-view-header\">\n                            <a href=story.url target=\"_blank\">\n                                <h1>{story.title}</h1>\n                            </a>\n                            <span class=\"host\">\n                                \"(\"{story.domain}\")\"\n                            </span>\n                            <ShowLet some=story.user let:user>\n                                <p class=\"meta\">\n                                    {story.points}\n                                    \" points | by \"\n                                    <A href=format!(\"/users/{user}\")>{user.clone()}</A>\n                                    {format!(\" {}\", story.time_ago)}\n                                </p>\n                            </ShowLet>\n                        </div>\n                        <div class=\"item-view-comments\">\n                            <p class=\"item-view-comments-header\">\n                                {if story.comments_count.unwrap_or_default() > 0 {\n                                    format!(\"{} comments\", story.comments_count.unwrap_or_default())\n                                } else {\n                                    \"No comments yet.\".into()\n                                }}\n                            </p>\n                            <ul class=\"comment-children\">\n                                <For\n                                    each=move || story.comments.clone().unwrap_or_default()\n                                    key=|comment| comment.id\n                                    let:comment\n                                >\n                                    <Comment comment />\n                                </For>\n                            </ul>\n                        </div>\n                    </div>\n                })\n            }\n        }\n    }))).build())\n}\n\n#[component]\npub fn Comment(comment: api::Comment) -> impl IntoView {\n    view! {\n        <li class=\"comment\">\n            <div class=\"by\">\n                <A href=format!(\"/users/{}\", comment.user.clone().unwrap_or_default())>{comment.user.clone()}</A>\n                {format!(\" {}\", comment.time_ago)}\n            </div>\n            <div class=\"text\" inner_html=comment.content></div>\n            {(!comment.comments.is_empty()).then(|| {\n                view! {\n                    <Toggle>\n                        {comment.comments.into_iter()\n                            .map(|comment: api::Comment| view! { <Comment comment /> })\n                            .collect_view()}\n                    </Toggle>\n                }\n            })}\n        </li>\n    }\n}\n\n#[island]\npub fn Toggle(children: Children) -> impl IntoView {\n    let (open, set_open) = signal(true);\n    view! {\n        <div class=\"toggle\" class:open=open>\n            <a on:click=move |_| set_open.update(|n| *n = !*n)>\n                {move || if open.get() {\n                    \"[-]\"\n                } else {\n                    \"[+] comments collapsed\"\n                }}\n            </a>\n        </div>\n        <ul\n            class=\"comment-children\"\n            style:display=move || if open.get() {\n                \"block\"\n            } else {\n                \"none\"\n            }\n        >\n            {children()}\n        </ul>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/routes/users.rs",
    "content": "use crate::api;\nuse leptos::{either::Either, prelude::*, server::Resource};\nuse leptos_router::hooks::use_params_map;\n\n#[server]\npub async fn fetch_user(\n    id: String,\n) -> Result<Option<api::User>, ServerFnError> {\n    Ok(api::fetch_api::<api::User>(&api::user(&id)).await)\n}\n\n#[component]\npub fn User() -> impl IntoView {\n    let params = use_params_map();\n    let user = Resource::new(\n        move || params.read().get(\"id\").unwrap_or_default(),\n        move |id| async move {\n            if id.is_empty() {\n                Ok(None)\n            } else {\n                fetch_user(id).await\n            }\n        },\n    );\n    view! {\n        <div class=\"user-view\">\n            <Suspense fallback=|| view! { \"Loading...\" }>\n                {move || Suspend::new(async move { match user.await.ok().flatten() {\n                    None => Either::Left(view! {  <h1>\"User not found.\"</h1> }),\n                    Some(user) => Either::Right(view! {\n                        <div>\n                            <h1>\"User: \" {user.id.clone()}</h1>\n                            <ul class=\"meta\">\n                                <li>\n                                    <span class=\"label\">\"Created: \"</span> {user.created}\n                                </li>\n                                <li>\n                                <span class=\"label\">\"Karma: \"</span> {user.karma}\n                                </li>\n                                <li inner_html={user.about} class=\"about\"></li>\n                            </ul>\n                            <p class=\"links\">\n                                <a href=format!(\"https://news.ycombinator.com/submitted?id={}\", user.id)>\"submissions\"</a>\n                                \" | \"\n                                <a href=format!(\"https://news.ycombinator.com/threads?id={}\", user.id)>\"comments\"</a>\n                            </p>\n                        </div>\n                    })\n                }})}\n            </Suspense>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_islands_axum/src/routes.rs",
    "content": "pub mod nav;\npub mod stories;\npub mod story;\npub mod users;\n"
  },
  {
    "path": "examples/hackernews_islands_axum/style.css",
    "content": "body {\n\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n\tfont-size: 15px;\n\tbackground-color: #f2f3f5;\n\tmargin: 0;\n\tpadding-top: 55px;\n\tcolor: #34495e;\n\toverflow-y: scroll\n}\n\na {\n\tcolor: #34495e;\n\ttext-decoration: none\n}\n\n.header {\n\tbackground-color: #335d92;\n\tposition: fixed;\n\tz-index: 999;\n\theight: 55px;\n\ttop: 0;\n\tleft: 0;\n\tright: 0\n}\n\n.header .inner {\n\tmax-width: 800px;\n\tbox-sizing: border-box;\n\tmargin: 0 auto;\n\tpadding: 15px 5px\n}\n\n.header a {\n\tcolor: rgba(255, 255, 255, .8);\n\tline-height: 24px;\n\ttransition: color .15s ease;\n\tdisplay: inline-block;\n\tvertical-align: middle;\n\tfont-weight: 300;\n\tletter-spacing: .075em;\n\tmargin-right: 1.8em\n}\n\n.header a:hover {\n\tcolor: #fff\n}\n\n.header a.active {\n\tcolor: #fff;\n\tfont-weight: 400\n}\n\n.header a:nth-child(6) {\n\tmargin-right: 0\n}\n\n.header .github {\n\tcolor: #fff;\n\tfont-size: .9em;\n\tmargin: 0;\n\tfloat: right\n}\n\n.logo {\n\twidth: 24px;\n\tmargin-right: 10px;\n\tdisplay: inline-block;\n\tvertical-align: middle\n}\n\n.view {\n\tmax-width: 800px;\n\tmargin: 0 auto;\n\tposition: relative\n}\n\n.fade-enter-active,\n.fade-exit-active {\n\ttransition: all .2s ease\n}\n\n.fade-enter,\n.fade-exit-active {\n\topacity: 0\n}\n\n@media (max-width:860px) {\n\t.header .inner {\n\t\tpadding: 15px 30px\n\t}\n}\n\n@media (max-width:600px) {\n\t.header .inner {\n\t\tpadding: 15px\n\t}\n\n\t.header a {\n\t\tmargin-right: 1em\n\t}\n\n\t.header .github {\n\t\tdisplay: none\n\t}\n}\n\n.news-view {\n\tpadding-top: 45px\n}\n\n.news-list,\n.news-list-nav {\n\tbackground-color: #fff;\n\tborder-radius: 2px\n}\n\n.news-list-nav {\n\tpadding: 15px 30px;\n\tposition: fixed;\n\ttext-align: center;\n\ttop: 55px;\n\tleft: 0;\n\tright: 0;\n\tz-index: 998;\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, .1)\n}\n\n.news-list-nav .page-link {\n\tmargin: 0 1em\n}\n\n.news-list-nav .disabled {\n\tcolor: #aaa\n}\n\n.news-list {\n\tposition: absolute;\n\tmargin: 30px 0;\n\twidth: 100%;\n\ttransition: all .5s cubic-bezier(.55, 0, .1, 1)\n}\n\n.news-list ul {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0\n}\n\n@media (max-width:600px) {\n\t.news-list {\n\t\tmargin: 10px 0\n\t}\n}\n\n.news-item {\n\tbackground-color: #fff;\n\tpadding: 20px 30px 20px 80px;\n\tborder-bottom: 1px solid #eee;\n\tposition: relative;\n\tline-height: 20px\n}\n\n.news-item .score {\n\tcolor: #335d92;\n\tfont-size: 1.1em;\n\tfont-weight: 700;\n\tposition: absolute;\n\ttop: 50%;\n\tleft: 0;\n\twidth: 80px;\n\ttext-align: center;\n\tmargin-top: -10px\n}\n\n.news-item .host,\n.news-item .meta {\n\tfont-size: .85em;\n\tcolor: #626262\n}\n\n.news-item .host a,\n.news-item .meta a {\n\tcolor: #626262;\n\ttext-decoration: underline\n}\n\n.news-item .host a:hover,\n.news-item .meta a:hover {\n\tcolor: #335d92\n}\n\n.item-view-header {\n\tbackground-color: #fff;\n\tpadding: 1.8em 2em 1em;\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, .1)\n}\n\n.item-view-header h1 {\n\tdisplay: inline;\n\tfont-size: 1.5em;\n\tmargin: 0;\n\tmargin-right: .5em\n}\n\n.item-view-header .host,\n.item-view-header .meta,\n.item-view-header .meta a {\n\tcolor: #626262\n}\n\n.item-view-header .meta a {\n\ttext-decoration: underline\n}\n\n.item-view-comments {\n\tbackground-color: #fff;\n\tmargin-top: 10px;\n\tpadding: 0 2em .5em\n}\n\n.item-view-comments-header {\n\tmargin: 0;\n\tfont-size: 1.1em;\n\tpadding: 1em 0;\n\tposition: relative\n}\n\n.item-view-comments-header .spinner {\n\tdisplay: inline-block;\n\tmargin: -15px 0\n}\n\n.comment-children {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0\n}\n\n@media (max-width:600px) {\n\t.item-view-header h1 {\n\t\tfont-size: 1.25em\n\t}\n}\n\n.comment-children .comment-children {\n\tmargin-left: 1.5em\n}\n\n.comment {\n\tborder-top: 1px solid #eee;\n\tposition: relative\n}\n\n.comment .by,\n.comment .text,\n.comment .toggle {\n\tfont-size: .9em;\n\tmargin: 1em 0\n}\n\n.comment .by {\n\tcolor: #626262\n}\n\n.comment .by a {\n\tcolor: #626262;\n\ttext-decoration: underline\n}\n\n.comment .text {\n\toverflow-wrap: break-word\n}\n\n.comment .text a:hover {\n\tcolor: #335d92\n}\n\n.comment .text pre {\n\twhite-space: pre-wrap\n}\n\n.comment .toggle {\n\tbackground-color: #fffbf2;\n\tpadding: .3em .5em;\n\tborder-radius: 4px\n}\n\n.comment .toggle a {\n\tcolor: #626262;\n\tcursor: pointer\n}\n\n.comment .toggle.open {\n\tpadding: 0;\n\tbackground-color: transparent;\n\tmargin-bottom: -.5em\n}\n\n.user-view {\n\tbackground-color: #fff;\n\tbox-sizing: border-box;\n\tpadding: 2em 3em\n}\n\n.user-view h1 {\n\tmargin: 0;\n\tfont-size: 1.5em\n}\n\n.user-view .meta {\n\tlist-style-type: none;\n\tpadding: 0\n}\n\n.user-view .label {\n\tdisplay: inline-block;\n\tmin-width: 4em\n}\n\n.user-view .about {\n\tmargin: 1em 0\n}\n\n.user-view .links a {\n\ttext-decoration: underline\n}"
  },
  {
    "path": "examples/hackernews_js_fetch/Cargo.toml",
    "content": "[package]\nname = \"hackernews_js_fetch\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\nlog = \"0.4.22\"\nleptos = { path = \"../../leptos\" }\nleptos_axum = { path = \"../../integrations/axum\", default-features = false, optional = true }\nleptos_meta = { path = \"../../meta\" }\nleptos_router = { path = \"../../router\" }\nleptos_server = { path = \"../../leptos_server\", optional = true }\nserde = { version = \"1.0\", features = [\"derive\"] }\ntracing = \"0.1.40\"\ngloo-net = { version = \"0.6.0\", features = [\"http\"] }\nreqwest = { version = \"0.12.5\", features = [\"json\"] }\naxum = { version = \"0.8.1\", default-features = false, optional = true }\ntower = { version = \"0.4.13\", optional = true }\nhttp = { version = \"1.1\", optional = true }\nweb-sys = { version = \"0.3.70\", features = [\n  \"AbortController\",\n  \"AbortSignal\",\n  \"Request\",\n  \"Response\",\n] }\ngetrandom = { version = \"0.2.15\", features = [\"js\"] }\nwasm-bindgen = \"0.2.93\"\nwasm-bindgen-futures = { version = \"0.4.42\", features = [\n  \"futures-core-03-stream\",\n], optional = true }\naxum-js-fetch = { git = \"https://github.com/seanaye/axum-js-fetch\", optional = true }\nsend_wrapper = { version = \"0.6.0\", features = [\"futures\"] }\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:http\",\n  \"dep:wasm-bindgen-futures\",\n  \"dep:axum-js-fetch\",\n  \"leptos/ssr\",\n  \"leptos_axum/wasm\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"leptos_server/serde-wasm-bindgen\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"http\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"hackernews_js_fetch\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/hackernews_js_fetch/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/hackernews_js_fetch/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/deno-build.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"deno\"\n"
  },
  {
    "path": "examples/hackernews_js_fetch/README.md",
    "content": "# Leptos Hacker News Example with Axum\n\nThis example uses the basic Hacker News example as its basis, but shows how to run the server side as WASM running in a JS environment. In this example, Deno is used as the runtime.\n\n## Server Side Rendering with Deno\n\nTo run the Deno version, run\n\n```bash\ndeno task build\ndeno task start\n```\n"
  },
  {
    "path": "examples/hackernews_js_fetch/deno.jsonc",
    "content": "{\n  \"tasks\": {\n    \"build:server\": \"wasm-pack build --release --target web --out-name server --features ssr --no-default-features\",\n    \"build:client\": \"wasm-pack build --release --target web --out-name client --features hydrate --no-default-features\",\n    \"build\": \"deno task build:server & deno task build:client\",\n    \"start\": \"deno run --allow-read --allow-net run.ts\"\n  }\n}\n"
  },
  {
    "path": "examples/hackernews_js_fetch/public/style.css",
    "content": "body {\n\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n\tfont-size: 15px;\n\tbackground-color: #f2f3f5;\n\tmargin: 0;\n\tpadding-top: 55px;\n\tcolor: #34495e;\n\toverflow-y: scroll\n}\n\na {\n\tcolor: #34495e;\n\ttext-decoration: none\n}\n\n.header {\n\tbackground-color: #335d92;\n\tposition: fixed;\n\tz-index: 999;\n\theight: 55px;\n\ttop: 0;\n\tleft: 0;\n\tright: 0\n}\n\n.header .inner {\n\tmax-width: 800px;\n\tbox-sizing: border-box;\n\tmargin: 0 auto;\n\tpadding: 15px 5px\n}\n\n.header a {\n\tcolor: rgba(255, 255, 255, .8);\n\tline-height: 24px;\n\ttransition: color .15s ease;\n\tdisplay: inline-block;\n\tvertical-align: middle;\n\tfont-weight: 300;\n\tletter-spacing: .075em;\n\tmargin-right: 1.8em\n}\n\n.header a:hover {\n\tcolor: #fff\n}\n\n.header a.active {\n\tcolor: #fff;\n\tfont-weight: 400\n}\n\n.header a:nth-child(6) {\n\tmargin-right: 0\n}\n\n.header .github {\n\tcolor: #fff;\n\tfont-size: .9em;\n\tmargin: 0;\n\tfloat: right\n}\n\n.logo {\n\twidth: 24px;\n\tmargin-right: 10px;\n\tdisplay: inline-block;\n\tvertical-align: middle\n}\n\n.view {\n\tmax-width: 800px;\n\tmargin: 0 auto;\n\tposition: relative\n}\n\n.fade-enter-active,\n.fade-exit-active {\n\ttransition: all .2s ease\n}\n\n.fade-enter,\n.fade-exit-active {\n\topacity: 0\n}\n\n@media (max-width:860px) {\n\t.header .inner {\n\t\tpadding: 15px 30px\n\t}\n}\n\n@media (max-width:600px) {\n\t.header .inner {\n\t\tpadding: 15px\n\t}\n\n\t.header a {\n\t\tmargin-right: 1em\n\t}\n\n\t.header .github {\n\t\tdisplay: none\n\t}\n}\n\n.news-view {\n\tpadding-top: 45px\n}\n\n.news-list,\n.news-list-nav {\n\tbackground-color: #fff;\n\tborder-radius: 2px\n}\n\n.news-list-nav {\n\tpadding: 15px 30px;\n\tposition: fixed;\n\ttext-align: center;\n\ttop: 55px;\n\tleft: 0;\n\tright: 0;\n\tz-index: 998;\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, .1)\n}\n\n.news-list-nav .page-link {\n\tmargin: 0 1em\n}\n\n.news-list-nav .disabled {\n\tcolor: #aaa\n}\n\n.news-list {\n\tposition: absolute;\n\tmargin: 30px 0;\n\twidth: 100%;\n\ttransition: all .5s cubic-bezier(.55, 0, .1, 1)\n}\n\n.news-list ul {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0\n}\n\n@media (max-width:600px) {\n\t.news-list {\n\t\tmargin: 10px 0\n\t}\n}\n\n.news-item {\n\tbackground-color: #fff;\n\tpadding: 20px 30px 20px 80px;\n\tborder-bottom: 1px solid #eee;\n\tposition: relative;\n\tline-height: 20px\n}\n\n.news-item .score {\n\tcolor: #335d92;\n\tfont-size: 1.1em;\n\tfont-weight: 700;\n\tposition: absolute;\n\ttop: 50%;\n\tleft: 0;\n\twidth: 80px;\n\ttext-align: center;\n\tmargin-top: -10px\n}\n\n.news-item .host,\n.news-item .meta {\n\tfont-size: .85em;\n\tcolor: #626262\n}\n\n.news-item .host a,\n.news-item .meta a {\n\tcolor: #626262;\n\ttext-decoration: underline\n}\n\n.news-item .host a:hover,\n.news-item .meta a:hover {\n\tcolor: #335d92\n}\n\n.item-view-header {\n\tbackground-color: #fff;\n\tpadding: 1.8em 2em 1em;\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, .1)\n}\n\n.item-view-header h1 {\n\tdisplay: inline;\n\tfont-size: 1.5em;\n\tmargin: 0;\n\tmargin-right: .5em\n}\n\n.item-view-header .host,\n.item-view-header .meta,\n.item-view-header .meta a {\n\tcolor: #626262\n}\n\n.item-view-header .meta a {\n\ttext-decoration: underline\n}\n\n.item-view-comments {\n\tbackground-color: #fff;\n\tmargin-top: 10px;\n\tpadding: 0 2em .5em\n}\n\n.item-view-comments-header {\n\tmargin: 0;\n\tfont-size: 1.1em;\n\tpadding: 1em 0;\n\tposition: relative\n}\n\n.item-view-comments-header .spinner {\n\tdisplay: inline-block;\n\tmargin: -15px 0\n}\n\n.comment-children {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0\n}\n\n@media (max-width:600px) {\n\t.item-view-header h1 {\n\t\tfont-size: 1.25em\n\t}\n}\n\n.comment-children .comment-children {\n\tmargin-left: 1.5em\n}\n\n.comment {\n\tborder-top: 1px solid #eee;\n\tposition: relative\n}\n\n.comment .by,\n.comment .text,\n.comment .toggle {\n\tfont-size: .9em;\n\tmargin: 1em 0\n}\n\n.comment .by {\n\tcolor: #626262\n}\n\n.comment .by a {\n\tcolor: #626262;\n\ttext-decoration: underline\n}\n\n.comment .text {\n\toverflow-wrap: break-word\n}\n\n.comment .text a:hover {\n\tcolor: #335d92\n}\n\n.comment .text pre {\n\twhite-space: pre-wrap\n}\n\n.comment .toggle {\n\tbackground-color: #fffbf2;\n\tpadding: .3em .5em;\n\tborder-radius: 4px\n}\n\n.comment .toggle a {\n\tcolor: #626262;\n\tcursor: pointer\n}\n\n.comment .toggle.open {\n\tpadding: 0;\n\tbackground-color: transparent;\n\tmargin-bottom: -.5em\n}\n\n.user-view {\n\tbackground-color: #fff;\n\tbox-sizing: border-box;\n\tpadding: 2em 3em\n}\n\n.user-view h1 {\n\tmargin: 0;\n\tfont-size: 1.5em\n}\n\n.user-view .meta {\n\tlist-style-type: none;\n\tpadding: 0\n}\n\n.user-view .label {\n\tdisplay: inline-block;\n\tmin-width: 4em\n}\n\n.user-view .about {\n\tmargin: 1em 0\n}\n\n.user-view .links a {\n\ttext-decoration: underline\n}"
  },
  {
    "path": "examples/hackernews_js_fetch/run.ts",
    "content": "import init, { Handler } from \"./pkg/server.js\";\nimport { serveDir } from \"https://deno.land/std/http/file_server.ts\";\n\nawait init();\nconst handler = await Handler.new();\n\nDeno.serve((req) => {\n  const u = new URL(req.url);\n  if (u.pathname.startsWith(\"/pkg\") || u.pathname.startsWith(\"/public\")) {\n    return serveDir(req);\n  }\n  return handler.serve(req);\n});\n"
  },
  {
    "path": "examples/hackernews_js_fetch/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/hackernews_js_fetch/src/api.rs",
    "content": "use leptos::logging;\nuse serde::{de::DeserializeOwned, Deserialize, Serialize};\n\npub fn story(path: &str) -> String {\n    format!(\"https://node-hnapi.herokuapp.com/{path}\")\n}\n\npub fn user(path: &str) -> String {\n    format!(\"https://hacker-news.firebaseio.com/v0/user/{path}.json\")\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn fetch_api<T>(\n    path: &str,\n) -> impl std::future::Future<Output = Option<T>> + Send + '_\nwhere\n    T: Serialize + DeserializeOwned,\n{\n    use leptos::prelude::on_cleanup;\n    use send_wrapper::SendWrapper;\n\n    SendWrapper::new(async move {\n        let abort_controller =\n            SendWrapper::new(web_sys::AbortController::new().ok());\n        let abort_signal = abort_controller.as_ref().map(|a| a.signal());\n\n        // abort in-flight requests if, e.g., we've navigated away from this page\n        on_cleanup(move || {\n            if let Some(abort_controller) = abort_controller.take() {\n                abort_controller.abort()\n            }\n        });\n\n        gloo_net::http::Request::get(path)\n            .abort_signal(abort_signal.as_ref())\n            .send()\n            .await\n            .map_err(|e| logging::error!(\"{e}\"))\n            .ok()?\n            .json()\n            .await\n            .ok()\n    })\n}\n\n#[cfg(feature = \"ssr\")]\npub async fn fetch_api<T>(path: &str) -> Option<T>\nwhere\n    T: Serialize + DeserializeOwned,\n{\n    reqwest::get(path)\n        .await\n        .map_err(|e| logging::error!(\"{e}\"))\n        .ok()?\n        .json()\n        .await\n        .ok()\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct Story {\n    pub id: usize,\n    pub title: String,\n    pub points: Option<i32>,\n    pub user: Option<String>,\n    pub time: usize,\n    pub time_ago: String,\n    #[serde(alias = \"type\")]\n    pub story_type: String,\n    pub url: String,\n    #[serde(default)]\n    pub domain: String,\n    #[serde(default)]\n    pub comments: Option<Vec<Comment>>,\n    pub comments_count: Option<usize>,\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct Comment {\n    pub id: usize,\n    pub level: usize,\n    pub user: Option<String>,\n    pub time: usize,\n    pub time_ago: String,\n    pub content: Option<String>,\n    pub comments: Vec<Comment>,\n}\n\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]\npub struct User {\n    pub created: usize,\n    pub id: String,\n    pub karma: i32,\n    pub about: Option<String>,\n}\n"
  },
  {
    "path": "examples/hackernews_js_fetch/src/lib.rs",
    "content": "use leptos::prelude::*;\nmod api;\nmod routes;\nuse leptos_meta::{provide_meta_context, Link, Meta, MetaTags, Stylesheet};\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router, RoutingProgress},\n    OptionalParamSegment, ParamSegment, StaticSegment,\n};\nuse routes::{nav::*, stories::*, story::*, users::*};\nuse std::time::Duration;\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n    let (is_routing, set_is_routing) = signal(false);\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/public/style.css\"/>\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/public/favicon.ico\"/>\n        <Meta name=\"description\" content=\"Leptos implementation of a HackerNews demo.\"/>\n        <Router set_is_routing>\n            // shows a progress bar while async data are loading\n            <div class=\"routing-progress\">\n                <RoutingProgress is_routing max_time=Duration::from_millis(250)/>\n            </div>\n            <Nav/>\n            <main>\n                <FlatRoutes fallback=|| \"Not found.\">\n                    <Route path=(StaticSegment(\"users\"), ParamSegment(\"id\")) view=User/>\n                    <Route path=(StaticSegment(\"stories\"), ParamSegment(\"id\")) view=Story/>\n                    <Route path=OptionalParamSegment(\"stories\") view=Stories/>\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n\n#[cfg(feature = \"ssr\")]\nmod ssr_imports {\n    use crate::{shell, App};\n    use axum::Router;\n    use leptos::prelude::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use log::{info, Level};\n    use wasm_bindgen::prelude::wasm_bindgen;\n\n    #[wasm_bindgen]\n    pub struct Handler(axum_js_fetch::App);\n\n    #[wasm_bindgen]\n    impl Handler {\n        pub async fn new() -> Self {\n            _ = console_log::init_with_level(Level::Debug);\n            console_error_panic_hook::set_once();\n\n            let leptos_options = LeptosOptions::builder()\n                .output_name(\"client\")\n                .site_pkg_dir(\"pkg\")\n                .build();\n\n            let routes = generate_route_list(App);\n\n            // build our application with a route\n            let app = Router::new()\n                .leptos_routes(&leptos_options, routes, {\n                    let leptos_options = leptos_options.clone();\n                    move || shell(leptos_options.clone())\n                })\n                .with_state(leptos_options);\n\n            info!(\"creating handler instance\");\n\n            Self(axum_js_fetch::App::new(app))\n        }\n\n        pub async fn serve(&self, req: web_sys::Request) -> web_sys::Response {\n            self.0.oneshot(req).await\n        }\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_js_fetch/src/routes/nav.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::components::A;\n\n#[component]\npub fn Nav() -> impl IntoView {\n    view! {\n        <header class=\"header\">\n            <nav class=\"inner\">\n                <A href=\"/home\">\n                    <strong>\"HN\"</strong>\n                </A>\n                <A href=\"/new\">\n                    <strong>\"New\"</strong>\n                </A>\n                <A href=\"/show\">\n                    <strong>\"Show\"</strong>\n                </A>\n                <A href=\"/ask\">\n                    <strong>\"Ask\"</strong>\n                </A>\n                <A href=\"/job\">\n                    <strong>\"Jobs\"</strong>\n                </A>\n                <a\n                    class=\"github\"\n                    href=\"http://github.com/leptos-rs/leptos\"\n                    target=\"_blank\"\n                    rel=\"noreferrer\"\n                >\n                    \"Built with Leptos\"\n                </a>\n            </nav>\n        </header>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_js_fetch/src/routes/stories.rs",
    "content": "use crate::api;\nuse leptos::{either::Either, prelude::*};\nuse leptos_router::{\n    components::A,\n    hooks::{use_params_map, use_query_map},\n};\nuse send_wrapper::SendWrapper;\n\nfn category(from: &str) -> &'static str {\n    match from {\n        \"new\" => \"newest\",\n        \"show\" => \"show\",\n        \"ask\" => \"ask\",\n        \"job\" => \"jobs\",\n        _ => \"news\",\n    }\n}\n\n#[component]\npub fn Stories() -> impl IntoView {\n    let query = use_query_map();\n    let params = use_params_map();\n    let page = move || {\n        query\n            .read()\n            .get(\"page\")\n            .and_then(|page| page.parse::<usize>().ok())\n            .unwrap_or(1)\n    };\n    let story_type = move || {\n        params\n            .read()\n            .get(\"stories\")\n            .unwrap_or_else(|| \"top\".to_string())\n    };\n    let stories = Resource::new(\n        move || (page(), story_type()),\n        move |(page, story_type)| {\n            SendWrapper::new(async move {\n                let path = format!(\"{}?page={}\", category(&story_type), page);\n                api::fetch_api::<Vec<api::Story>>(&api::story(&path)).await\n            })\n        },\n    );\n    let (pending, set_pending) = signal(false);\n\n    let hide_more_link = move || match &*stories.read() {\n        Some(Some(stories)) => stories.len() < 28,\n        _ => true\n    } || pending.get();\n\n    view! {\n        <div class=\"news-view\">\n            <div class=\"news-list-nav\">\n                <span>\n                    {move || {\n                        if page() > 1 {\n                            Either::Left(\n                                view! {\n                                    <a\n                                        class=\"page-link\"\n                                        href=move || {\n                                            format!(\"/{}?page={}\", story_type(), page() - 1)\n                                        }\n                                        aria-label=\"Previous Page\"\n                                    >\n                                        \"< prev\"\n                                    </a>\n                                },\n                            )\n                        } else {\n                            Either::Right(\n                                view! {\n                                    <span class=\"page-link disabled\" aria-hidden=\"true\">\n                                        \"< prev\"\n                                    </span>\n                                },\n                            )\n                        }\n                    }}\n                </span>\n                <span>\"page \" {page}</span>\n                <span class=\"page-link\" class:disabled=hide_more_link aria-hidden=hide_more_link>\n                    <a\n                        href=move || format!(\"/{}?page={}\", story_type(), page() + 1)\n                        aria-label=\"Next Page\"\n                    >\n                        \"more >\"\n                    </a>\n                </span>\n            </div>\n            <main class=\"news-list\">\n                <div>\n                    <Transition fallback=move || view! { <p>\"Loading...\"</p> } set_pending>\n                        <Show when=move || {\n                            stories.read().as_ref().map(Option::is_none).unwrap_or(false)\n                        }>> <p>\"Error loading stories.\"</p></Show>\n                        <ul>\n                            <For\n                                each=move || stories.get().unwrap_or_default().unwrap_or_default()\n                                key=|story| story.id\n                                let:story\n                            >\n                                <Story story/>\n                            </For>\n                        </ul>\n                    </Transition>\n                </div>\n            </main>\n        </div>\n    }\n}\n\n#[component]\nfn Story(story: api::Story) -> impl IntoView {\n    view! {\n        <li class=\"news-item\">\n            <span class=\"score\">{story.points}</span>\n            <span class=\"title\">\n                {if !story.url.starts_with(\"item?id=\") {\n                    Either::Left(\n                        view! {\n                            <span>\n                                <a href=story.url target=\"_blank\" rel=\"noreferrer\">\n                                    {story.title.clone()}\n                                </a>\n                                <span class=\"host\">\"(\"{story.domain}\")\"</span>\n                            </span>\n                        },\n                    )\n                } else {\n                    let title = story.title.clone();\n                    Either::Right(view! { <A href=format!(\"/stories/{}\", story.id)>{title}</A> })\n                }}\n            </span>\n            <br/>\n            <span class=\"meta\">\n                {if story.story_type != \"job\" {\n                    Either::Left(\n                        view! {\n                            <span>\n                                \"by \"\n                                <ShowLet some=story.user let:user>\n                                    <A href=format!(\"/users/{user}\")>{user.clone()}</A>\n                                </ShowLet>\n                                {format!(\" {} | \", story.time_ago)}\n                                <A href=format!(\n                                    \"/stories/{}\",\n                                    story.id,\n                                )>\n                                    {if story.comments_count.unwrap_or_default() > 0 {\n                                        format!(\n                                            \"{} comments\",\n                                            story.comments_count.unwrap_or_default(),\n                                        )\n                                    } else {\n                                        \"discuss\".into()\n                                    }}\n                                </A>\n                            </span>\n                        },\n                    )\n                } else {\n                    let title = story.title.clone();\n                    Either::Right(view! { <A href=format!(\"/item/{}\", story.id)>{title}</A> })\n                }}\n            </span>\n            {(story.story_type != \"link\")\n                .then(|| {\n                    view! {\n                        \" \"\n                        <span class=\"label\">{story.story_type}</span>\n                    }\n                })}\n        </li>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_js_fetch/src/routes/story.rs",
    "content": "use crate::api;\nuse leptos::{either::Either, prelude::*};\nuse leptos_meta::Meta;\nuse leptos_router::{components::A, hooks::use_params_map};\nuse send_wrapper::SendWrapper;\n\n#[component]\npub fn Story() -> impl IntoView {\n    let params = use_params_map();\n    let story = Resource::new_blocking(\n        move || params.read().get(\"id\").unwrap_or_default(),\n        move |id| {\n            SendWrapper::new(async move {\n                if id.is_empty() {\n                    None\n                } else {\n                    api::fetch_api::<api::Story>(&api::story(&format!(\n                        \"item/{id}\"\n                    )))\n                    .await\n                }\n            })\n        },\n    );\n\n    Suspense(SuspenseProps::builder().fallback(|| \"Loading...\").children(ToChildren::to_children(move || Suspend::new(async move {\n        match story.await.clone() {\n            None => Either::Left(\"Story not found.\"),\n            Some(story) => {\n                Either::Right(view! {\n                    <Meta name=\"description\" content=story.title.clone()/>\n                    <div class=\"item-view\">\n                        <div class=\"item-view-header\">\n                            <a href=story.url target=\"_blank\">\n                                <h1>{story.title}</h1>\n                            </a>\n                            <span class=\"host\">\"(\"{story.domain}\")\"</span>\n                            <ShowLet some=story.user let:user>\n                                <p class=\"meta\">\n                                    {story.points} \" points | by \"\n                                    <A href=format!(\"/users/{user}\")>{user.clone()}</A>\n                                    {format!(\" {}\", story.time_ago)}\n                                </p>\n                            </ShowLet>\n                        </div>\n                        <div class=\"item-view-comments\">\n                            <p class=\"item-view-comments-header\">\n                                {if story.comments_count.unwrap_or_default() > 0 {\n                                    format!(\"{} comments\", story.comments_count.unwrap_or_default())\n                                } else {\n                                    \"No comments yet.\".into()\n                                }}\n                            </p>\n                            <ul class=\"comment-children\">\n                                <For\n                                    each=move || story.comments.clone().unwrap_or_default()\n                                    key=|comment| comment.id\n                                    let:comment\n                                >\n                                    <Comment comment/>\n                                </For>\n                            </ul>\n                        </div>\n                    </div>\n                })\n            }\n        }\n    }))).build())\n}\n\n#[component]\npub fn Comment(comment: api::Comment) -> impl IntoView {\n    let (open, set_open) = signal(true);\n\n    view! {\n        <li class=\"comment\">\n            <div class=\"by\">\n                <A href=format!(\n                    \"/users/{}\",\n                    comment.user.clone().unwrap_or_default(),\n                )>{comment.user.clone()}</A>\n                {format!(\" {}\", comment.time_ago)}\n            </div>\n            <div class=\"text\" inner_html=comment.content></div>\n            {(!comment.comments.is_empty())\n                .then(|| {\n                    view! {\n                        <div>\n                            <div class=\"toggle\" class:open=open>\n                                <a on:click=move |_| {\n                                    set_open.update(|n| *n = !*n)\n                                }>\n                                    {\n                                        let comments_len = comment.comments.len();\n                                        move || {\n                                            if open.get() {\n                                                \"[-]\".into()\n                                            } else {\n                                                format!(\n                                                    \"[+] {}{} collapsed\",\n                                                    comments_len,\n                                                    pluralize(comments_len),\n                                                )\n                                            }\n                                        }\n                                    }\n                                </a>\n                            </div>\n                            {move || {\n                                open.get()\n                                    .then({\n                                        let comments = comment.comments.clone();\n                                        move || {\n                                            view! {\n                                                <ul class=\"comment-children\">\n                                                    <For\n                                                        each=move || comments.clone()\n                                                        key=|comment| comment.id\n                                                        let:comment\n                                                    >\n                                                        <Comment comment/>\n                                                    </For>\n                                                </ul>\n                                            }\n                                        }\n                                    })\n                            }}\n                        </div>\n                    }\n                })}\n        </li>\n    }.into_any()\n}\n\nfn pluralize(n: usize) -> &'static str {\n    if n == 1 {\n        \" reply\"\n    } else {\n        \" replies\"\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_js_fetch/src/routes/users.rs",
    "content": "use crate::api::{self, User};\nuse leptos::{either::Either, prelude::*, server::Resource};\nuse leptos_router::hooks::use_params_map;\nuse send_wrapper::SendWrapper;\n\n#[component]\npub fn User() -> impl IntoView {\n    let params = use_params_map();\n    let user = Resource::new(\n        move || params.read().get(\"id\").unwrap_or_default(),\n        move |id| {\n            SendWrapper::new(async move {\n                if id.is_empty() {\n                    None\n                } else {\n                    api::fetch_api::<User>(&api::user(&id)).await\n                }\n            })\n        },\n    );\n    view! {\n        <div class=\"user-view\">\n            <Suspense fallback=|| {\n                view! { \"Loading...\" }\n            }>\n                {move || Suspend::new(async move {\n                    match user.await.clone() {\n                        None => Either::Left(view! { <h1>\"User not found.\"</h1> }),\n                        Some(user) => {\n                            Either::Right(\n                                view! {\n                                    <div>\n                                        <h1>\"User: \" {user.id.clone()}</h1>\n                                        <ul class=\"meta\">\n                                            <li>\n                                                <span class=\"label\">\"Created: \"</span>\n                                                {user.created}\n                                            </li>\n                                            <li>\n                                                <span class=\"label\">\"Karma: \"</span>\n                                                {user.karma}\n                                            </li>\n                                            <li inner_html=user.about class=\"about\"></li>\n                                        </ul>\n                                        <p class=\"links\">\n                                            <a href=format!(\n                                                \"https://news.ycombinator.com/submitted?id={}\",\n                                                user.id,\n                                            )>\"submissions\"</a>\n                                            \" | \"\n                                            <a href=format!(\n                                                \"https://news.ycombinator.com/threads?id={}\",\n                                                user.id,\n                                            )>\"comments\"</a>\n                                        </p>\n                                    </div>\n                                },\n                            )\n                        }\n                    }\n                })}\n            </Suspense>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/hackernews_js_fetch/src/routes.rs",
    "content": "pub mod nav;\npub mod stories;\npub mod story;\npub mod users;\n"
  },
  {
    "path": "examples/islands/Cargo.toml",
    "content": "[package]\nname = \"islands\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.30\"\nhttp = \"1.1\"\nleptos = { path = \"../../leptos\", features = [\"tracing\", \"islands\"] }\nserver_fn = { path = \"../../server_fn\", features = [\"serde-lite\"] }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nlog = \"0.4.22\"\nserde = { version = \"1.0\", features = [\"derive\"] }\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nwasm-bindgen = \"0.2.93\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"dep:leptos_axum\",\n]\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"islands\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "examples/islands/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/islands/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n"
  },
  {
    "path": "examples/islands/README.md",
    "content": "# Leptos Todo App Sqlite with Axum\n\nThis example creates a basic todo app with an Axum backend that uses Leptos' server functions to call sqlx from the client and seamlessly run it on the server.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## E2E Testing\n\nSee the [E2E README](./e2e/README.md) for more information about the testing strategy.\n\n## Rendering\n\nSee the [SSR Notes](../SSR_NOTES.md) for more information about Server Side Rendering.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/islands/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/islands/src/app.rs",
    "content": "use leptos::prelude::*;\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options=options islands=true/>\n                <link rel=\"stylesheet\" id=\"leptos\" href=\"/pkg/islands.css\"/>\n                <link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\"/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    view! {\n        <header>\n            <h1>\"My Application\"</h1>\n        </header>\n        <main>\n            <OuterIsland>\n                <InnerIsland/>\n                <InnerIsland/>\n                <InnerIsland/>\n            </OuterIsland>\n        </main>\n    }\n}\n\n#[island]\npub fn OuterIsland(children: Children) -> impl IntoView {\n    provide_context(42i32);\n    view! {\n        <div class=\"outer-island\">\n            <h2>\"Outer Island\"</h2>\n            <button on:click=|_| leptos::logging::log!(\"clicked button in island!\")>\n                \"Click me\"\n            </button>\n            {children()}\n        </div>\n    }\n}\n\n#[island]\npub fn InnerIsland() -> impl IntoView {\n    let val = use_context::<i32>();\n    view! {\n        <h2>\"Inner Island\"</h2>\n        <button on:click=move |_| leptos::logging::log!(\"value should be Some(42) -- it's {val:?}\")>\n            \"Click me (inner)\"\n        </button>\n    }\n}\n"
  },
  {
    "path": "examples/islands/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_islands();\n}\n"
  },
  {
    "path": "examples/islands/src/main.rs",
    "content": "use axum::Router;\nuse islands::app::{shell, App};\nuse leptos::prelude::*;\nuse leptos_axum::{generate_route_list, LeptosRoutes};\n\n#[tokio::main]\nasync fn main() {\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    println!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n"
  },
  {
    "path": "examples/islands/style.css",
    "content": ".pending {\n\tcolor: purple;\n}\n"
  },
  {
    "path": "examples/islands_router/Cargo.toml",
    "content": "[package]\nname = \"islands\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.30\"\nhttp = \"1.1\"\nleptos = { path = \"../../leptos\", features = [\n  \"tracing\",\n  \"islands\",\n  \"islands-router\",\n] }\nleptos_router = { path = \"../../router\" }\nserver_fn = { path = \"../../server_fn\", features = [\"serde-lite\"] }\nleptos_axum = { path = \"../../integrations/axum\", features = [\n  \"islands-router\",\n], optional = true }\nlog = \"0.4.22\"\nserde = { version = \"1.0\", features = [\"derive\"] }\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nwasm-bindgen = \"0.2.100\"\nserde_json = \"1.0.133\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"dep:leptos_axum\",\n]\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"islands\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3009\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "examples/islands_router/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/islands_router/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n"
  },
  {
    "path": "examples/islands_router/README.md",
    "content": "# Work in Progress\n\nThis example is something I wrote on a long layover in the Orlando airport in July. (It was really hot!)\n\nIt is the culmination of a couple years of thinking and working toward being able to do this, which you can see \ndescribed pretty well in the pinned roadmap issue (#1830) and its discussion of different modes of client-side\nrouting when you use islands.\n\nThis uses *only* server rendering, with no actual islands, but still maintains client-side state across page navigations.\nIt does this by building on the fact that we now have a statically-typed view tree to do pretty smart updates with \nnew HTML from the client, with extremely minimal diffing.\n"
  },
  {
    "path": "examples/islands_router/mock_data.json",
    "content": "[\n  {\n    \"id\": 2,\n    \"first_name\": \"Pippy\",\n    \"last_name\": \"Yule\",\n    \"email\": \"pyule1@mit.edu\"\n  },\n  {\n    \"id\": 3,\n    \"first_name\": \"Rodrick\",\n    \"last_name\": \"Swancock\",\n    \"email\": \"rswancock2@google.co.uk\"\n  },\n  {\n    \"id\": 8,\n    \"first_name\": \"Eugenie\",\n    \"last_name\": \"Lanyon\",\n    \"email\": \"elanyon7@nba.com\"\n  },\n  {\n    \"id\": 9,\n    \"first_name\": \"Derry\",\n    \"last_name\": \"Bovingdon\",\n    \"email\": \"dbovingdon8@furl.net\"\n  },\n  {\n    \"id\": 11,\n    \"first_name\": \"Burnard\",\n    \"last_name\": \"Kuhndel\",\n    \"email\": \"bkuhndela@cmu.edu\"\n  },\n  {\n    \"id\": 12,\n    \"first_name\": \"Greer\",\n    \"last_name\": \"Strachan\",\n    \"email\": \"gstrachanb@virginia.edu\"\n  },\n  {\n    \"id\": 13,\n    \"first_name\": \"Dorine\",\n    \"last_name\": \"Lougheed\",\n    \"email\": \"dlougheedc@redcross.org\"\n  },\n  {\n    \"id\": 14,\n    \"first_name\": \"Jayne\",\n    \"last_name\": \"Geggie\",\n    \"email\": \"jgeggied@youtu.be\"\n  },\n  {\n    \"id\": 15,\n    \"first_name\": \"Chelsey\",\n    \"last_name\": \"Botham\",\n    \"email\": \"cbothame@cargocollective.com\"\n  },\n  {\n    \"id\": 16,\n    \"first_name\": \"Emmy\",\n    \"last_name\": \"Whittier\",\n    \"email\": \"ewhittierf@delicious.com\"\n  },\n  {\n    \"id\": 18,\n    \"first_name\": \"Julio\",\n    \"last_name\": \"Vannini\",\n    \"email\": \"jvanninih@sourceforge.net\"\n  },\n  {\n    \"id\": 19,\n    \"first_name\": \"Zorah\",\n    \"last_name\": \"Turbefield\",\n    \"email\": \"zturbefieldi@epa.gov\"\n  },\n  {\n    \"id\": 20,\n    \"first_name\": \"Les\",\n    \"last_name\": \"Strutley\",\n    \"email\": \"lstrutleyj@networkadvertising.org\"\n  },\n  {\n    \"id\": 21,\n    \"first_name\": \"Dedie\",\n    \"last_name\": \"Roubeix\",\n    \"email\": \"droubeixk@linkedin.com\"\n  },\n  {\n    \"id\": 22,\n    \"first_name\": \"Milzie\",\n    \"last_name\": \"Doyley\",\n    \"email\": \"mdoyleyl@youtu.be\"\n  },\n  {\n    \"id\": 23,\n    \"first_name\": \"Cleopatra\",\n    \"last_name\": \"Croysdale\",\n    \"email\": \"ccroysdalem@cdc.gov\"\n  },\n  {\n    \"id\": 24,\n    \"first_name\": \"Nellie\",\n    \"last_name\": \"Records\",\n    \"email\": \"nrecordsn@rediff.com\"\n  },\n  {\n    \"id\": 25,\n    \"first_name\": \"Michelina\",\n    \"last_name\": \"Jentzsch\",\n    \"email\": \"mjentzscho@theguardian.com\"\n  },\n  {\n    \"id\": 26,\n    \"first_name\": \"Theodosia\",\n    \"last_name\": \"De Vries\",\n    \"email\": \"tdevriesp@unc.edu\"\n  },\n  {\n    \"id\": 27,\n    \"first_name\": \"Maryanna\",\n    \"last_name\": \"Jirieck\",\n    \"email\": \"mjirieckq@meetup.com\"\n  },\n  {\n    \"id\": 28,\n    \"first_name\": \"Dreddy\",\n    \"last_name\": \"Labden\",\n    \"email\": \"dlabdenr@feedburner.com\"\n  },\n  {\n    \"id\": 29,\n    \"first_name\": \"Glynda\",\n    \"last_name\": \"Geibel\",\n    \"email\": \"ggeibels@yelp.com\"\n  },\n  {\n    \"id\": 30,\n    \"first_name\": \"Yulma\",\n    \"last_name\": \"Giroldo\",\n    \"email\": \"ygiroldot@google.co.jp\"\n  },\n  {\n    \"id\": 31,\n    \"first_name\": \"Michele\",\n    \"last_name\": \"Jennions\",\n    \"email\": \"mjennionsu@meetup.com\"\n  },\n  {\n    \"id\": 32,\n    \"first_name\": \"Hyatt\",\n    \"last_name\": \"Picford\",\n    \"email\": \"hpicfordv@cornell.edu\"\n  },\n  {\n    \"id\": 33,\n    \"first_name\": \"Jehanna\",\n    \"last_name\": \"Frunks\",\n    \"email\": \"jfrunksw@slashdot.org\"\n  },\n  {\n    \"id\": 34,\n    \"first_name\": \"Gustavo\",\n    \"last_name\": \"Soda\",\n    \"email\": \"gsodax@scientificamerican.com\"\n  },\n  {\n    \"id\": 35,\n    \"first_name\": \"Rianon\",\n    \"last_name\": \"Lamey\",\n    \"email\": \"rlameyy@histats.com\"\n  },\n  {\n    \"id\": 36,\n    \"first_name\": \"Winston\",\n    \"last_name\": \"Pitcher\",\n    \"email\": \"wpitcherz@sphinn.com\"\n  },\n  {\n    \"id\": 37,\n    \"first_name\": \"Schuyler\",\n    \"last_name\": \"Rewcassell\",\n    \"email\": \"srewcassell10@phoca.cz\"\n  },\n  {\n    \"id\": 38,\n    \"first_name\": \"Garald\",\n    \"last_name\": \"Thoumas\",\n    \"email\": \"gthoumas11@upenn.edu\"\n  },\n  {\n    \"id\": 40,\n    \"first_name\": \"Trudy\",\n    \"last_name\": \"Scarratt\",\n    \"email\": \"tscarratt13@baidu.com\"\n  },\n  {\n    \"id\": 43,\n    \"first_name\": \"Ariel\",\n    \"last_name\": \"Brunroth\",\n    \"email\": \"abrunroth16@tinyurl.com\"\n  },\n  {\n    \"id\": 44,\n    \"first_name\": \"Fonz\",\n    \"last_name\": \"Duigan\",\n    \"email\": \"fduigan17@issuu.com\"\n  },\n  {\n    \"id\": 45,\n    \"first_name\": \"Adeline\",\n    \"last_name\": \"Cashell\",\n    \"email\": \"acashell18@house.gov\"\n  },\n  {\n    \"id\": 46,\n    \"first_name\": \"Kurt\",\n    \"last_name\": \"Brittle\",\n    \"email\": \"kbrittle19@mysql.com\"\n  },\n  {\n    \"id\": 47,\n    \"first_name\": \"Ginni\",\n    \"last_name\": \"Richardes\",\n    \"email\": \"grichardes1a@phpbb.com\"\n  },\n  {\n    \"id\": 48,\n    \"first_name\": \"Christina\",\n    \"last_name\": \"Wheway\",\n    \"email\": \"cwheway1b@wisc.edu\"\n  },\n  {\n    \"id\": 49,\n    \"first_name\": \"Erasmus\",\n    \"last_name\": \"Vickors\",\n    \"email\": \"evickors1c@dell.com\"\n  },\n  {\n    \"id\": 50,\n    \"first_name\": \"Lillian\",\n    \"last_name\": \"Valentin\",\n    \"email\": \"lvalentin1d@usa.gov\"\n  },\n  {\n    \"id\": 51,\n    \"first_name\": \"Rozalie\",\n    \"last_name\": \"Abel\",\n    \"email\": \"rabel1e@walmart.com\"\n  },\n  {\n    \"id\": 53,\n    \"first_name\": \"Mendel\",\n    \"last_name\": \"Meaddowcroft\",\n    \"email\": \"mmeaddowcroft1g@csmonitor.com\"\n  },\n  {\n    \"id\": 55,\n    \"first_name\": \"Danica\",\n    \"last_name\": \"Kenrack\",\n    \"email\": \"dkenrack1i@nhs.uk\"\n  },\n  {\n    \"id\": 56,\n    \"first_name\": \"Reuben\",\n    \"last_name\": \"De Benedictis\",\n    \"email\": \"rdebenedictis1j@cnbc.com\"\n  },\n  {\n    \"id\": 57,\n    \"first_name\": \"Larine\",\n    \"last_name\": \"Woffenden\",\n    \"email\": \"lwoffenden1k@goo.ne.jp\"\n  },\n  {\n    \"id\": 59,\n    \"first_name\": \"Frank\",\n    \"last_name\": \"Cominello\",\n    \"email\": \"fcominello1m@phpbb.com\"\n  },\n  {\n    \"id\": 61,\n    \"first_name\": \"Veda\",\n    \"last_name\": \"Pryn\",\n    \"email\": \"vpryn1o@squidoo.com\"\n  },\n  {\n    \"id\": 62,\n    \"first_name\": \"Heddie\",\n    \"last_name\": \"Tinston\",\n    \"email\": \"htinston1p@is.gd\"\n  },\n  {\n    \"id\": 63,\n    \"first_name\": \"Lorelle\",\n    \"last_name\": \"Radbone\",\n    \"email\": \"lradbone1q@usgs.gov\"\n  },\n  {\n    \"id\": 64,\n    \"first_name\": \"Gustavo\",\n    \"last_name\": \"Jans\",\n    \"email\": \"gjans1r@microsoft.com\"\n  },\n  {\n    \"id\": 65,\n    \"first_name\": \"Karita\",\n    \"last_name\": \"Beeching\",\n    \"email\": \"kbeeching1s@skype.com\"\n  },\n  {\n    \"id\": 66,\n    \"first_name\": \"Damian\",\n    \"last_name\": \"Bellhanger\",\n    \"email\": \"dbellhanger1t@bbb.org\"\n  },\n  {\n    \"id\": 67,\n    \"first_name\": \"Kinna\",\n    \"last_name\": \"Cotherill\",\n    \"email\": \"kcotherill1u@angelfire.com\"\n  },\n  {\n    \"id\": 68,\n    \"first_name\": \"Janeva\",\n    \"last_name\": \"Varndall\",\n    \"email\": \"jvarndall1v@vk.com\"\n  },\n  {\n    \"id\": 70,\n    \"first_name\": \"Bourke\",\n    \"last_name\": \"Cossum\",\n    \"email\": \"bcossum1x@chron.com\"\n  },\n  {\n    \"id\": 71,\n    \"first_name\": \"Berk\",\n    \"last_name\": \"Tomasino\",\n    \"email\": \"btomasino1y@fotki.com\"\n  },\n  {\n    \"id\": 72,\n    \"first_name\": \"Shepherd\",\n    \"last_name\": \"Lyness\",\n    \"email\": \"slyness1z@csmonitor.com\"\n  },\n  {\n    \"id\": 73,\n    \"first_name\": \"Christoph\",\n    \"last_name\": \"Warrener\",\n    \"email\": \"cwarrener20@gmpg.org\"\n  },\n  {\n    \"id\": 74,\n    \"first_name\": \"Artus\",\n    \"last_name\": \"Bantock\",\n    \"email\": \"abantock21@who.int\"\n  },\n  {\n    \"id\": 75,\n    \"first_name\": \"Bryana\",\n    \"last_name\": \"Mixer\",\n    \"email\": \"bmixer22@nih.gov\"\n  },\n  {\n    \"id\": 76,\n    \"first_name\": \"Blithe\",\n    \"last_name\": \"Brigstock\",\n    \"email\": \"bbrigstock23@goodreads.com\"\n  },\n  {\n    \"id\": 77,\n    \"first_name\": \"Krispin\",\n    \"last_name\": \"Gothrup\",\n    \"email\": \"kgothrup24@tuttocitta.it\"\n  },\n  {\n    \"id\": 78,\n    \"first_name\": \"Helen-elizabeth\",\n    \"last_name\": \"Hardinge\",\n    \"email\": \"hhardinge25@indiatimes.com\"\n  },\n  {\n    \"id\": 79,\n    \"first_name\": \"Zachariah\",\n    \"last_name\": \"Burberye\",\n    \"email\": \"zburberye26@va.gov\"\n  },\n  {\n    \"id\": 81,\n    \"first_name\": \"Mozes\",\n    \"last_name\": \"Mityushin\",\n    \"email\": \"mmityushin28@vk.com\"\n  },\n  {\n    \"id\": 82,\n    \"first_name\": \"Hyacinthie\",\n    \"last_name\": \"Stirrip\",\n    \"email\": \"hstirrip29@mozilla.com\"\n  },\n  {\n    \"id\": 83,\n    \"first_name\": \"Hestia\",\n    \"last_name\": \"Full\",\n    \"email\": \"hfull2a@cornell.edu\"\n  },\n  {\n    \"id\": 84,\n    \"first_name\": \"Betty\",\n    \"last_name\": \"Doogan\",\n    \"email\": \"bdoogan2b@paginegialle.it\"\n  },\n  {\n    \"id\": 86,\n    \"first_name\": \"Ulrick\",\n    \"last_name\": \"Nowakowska\",\n    \"email\": \"unowakowska2d@newyorker.com\"\n  },\n  {\n    \"id\": 87,\n    \"first_name\": \"Susanne\",\n    \"last_name\": \"Bannell\",\n    \"email\": \"sbannell2e@house.gov\"\n  },\n  {\n    \"id\": 88,\n    \"first_name\": \"Carlotta\",\n    \"last_name\": \"de Bullion\",\n    \"email\": \"cdebullion2f@wikispaces.com\"\n  },\n  {\n    \"id\": 89,\n    \"first_name\": \"Conny\",\n    \"last_name\": \"Rodgerson\",\n    \"email\": \"crodgerson2g@nsw.gov.au\"\n  },\n  {\n    \"id\": 90,\n    \"first_name\": \"Anthony\",\n    \"last_name\": \"Stovine\",\n    \"email\": \"astovine2h@trellian.com\"\n  },\n  {\n    \"id\": 91,\n    \"first_name\": \"Trula\",\n    \"last_name\": \"Mangenot\",\n    \"email\": \"tmangenot2i@example.com\"\n  },\n  {\n    \"id\": 92,\n    \"first_name\": \"Urbain\",\n    \"last_name\": \"Ogglebie\",\n    \"email\": \"uogglebie2j@wix.com\"\n  },\n  {\n    \"id\": 93,\n    \"first_name\": \"Robena\",\n    \"last_name\": \"Yve\",\n    \"email\": \"ryve2k@sciencedaily.com\"\n  },\n  {\n    \"id\": 94,\n    \"first_name\": \"Axel\",\n    \"last_name\": \"McTrustam\",\n    \"email\": \"amctrustam2l@ucoz.ru\"\n  },\n  {\n    \"id\": 95,\n    \"first_name\": \"Link\",\n    \"last_name\": \"Klagges\",\n    \"email\": \"lklagges2m@foxnews.com\"\n  },\n  {\n    \"id\": 96,\n    \"first_name\": \"Yoko\",\n    \"last_name\": \"Percifer\",\n    \"email\": \"ypercifer2n@indiegogo.com\"\n  },\n  {\n    \"id\": 97,\n    \"first_name\": \"Rheba\",\n    \"last_name\": \"Heaford\",\n    \"email\": \"rheaford2o@ed.gov\"\n  },\n  {\n    \"id\": 98,\n    \"first_name\": \"Dorolisa\",\n    \"last_name\": \"Seabert\",\n    \"email\": \"dseabert2p@pen.io\"\n  },\n  {\n    \"id\": 99,\n    \"first_name\": \"Asher\",\n    \"last_name\": \"Ffrench\",\n    \"email\": \"affrench2q@miitbeian.gov.cn\"\n  },\n  {\n    \"id\": 100,\n    \"first_name\": \"Inga\",\n    \"last_name\": \"Skeen\",\n    \"email\": \"iskeen2r@moonfruit.com\"\n  },\n  {\n    \"id\": 101,\n    \"first_name\": \"Dov\",\n    \"last_name\": \"Nevinson\",\n    \"email\": \"dnevinson2s@dedecms.com\"\n  },\n  {\n    \"id\": 103,\n    \"first_name\": \"Prudence\",\n    \"last_name\": \"Bysshe\",\n    \"email\": \"pbysshe2u@auda.org.au\"\n  },\n  {\n    \"id\": 104,\n    \"first_name\": \"Desdemona\",\n    \"last_name\": \"Belverstone\",\n    \"email\": \"dbelverstone2v@ihg.com\"\n  },\n  {\n    \"id\": 105,\n    \"first_name\": \"Adiana\",\n    \"last_name\": \"Arnott\",\n    \"email\": \"aarnott2w@icio.us\"\n  },\n  {\n    \"id\": 106,\n    \"first_name\": \"Christoper\",\n    \"last_name\": \"Sutter\",\n    \"email\": \"csutter2x@jimdo.com\"\n  },\n  {\n    \"id\": 107,\n    \"first_name\": \"Guinevere\",\n    \"last_name\": \"Morton\",\n    \"email\": \"gmorton2y@drupal.org\"\n  },\n  {\n    \"id\": 109,\n    \"first_name\": \"Reynold\",\n    \"last_name\": \"Frigout\",\n    \"email\": \"rfrigout30@digg.com\"\n  },\n  {\n    \"id\": 110,\n    \"first_name\": \"Beverlie\",\n    \"last_name\": \"Macek\",\n    \"email\": \"bmacek31@yahoo.com\"\n  },\n  {\n    \"id\": 112,\n    \"first_name\": \"Romola\",\n    \"last_name\": \"Penna\",\n    \"email\": \"rpenna33@devhub.com\"\n  },\n  {\n    \"id\": 114,\n    \"first_name\": \"Allen\",\n    \"last_name\": \"Lawey\",\n    \"email\": \"alawey35@marketwatch.com\"\n  },\n  {\n    \"id\": 115,\n    \"first_name\": \"Yard\",\n    \"last_name\": \"Solon\",\n    \"email\": \"ysolon36@fotki.com\"\n  },\n  {\n    \"id\": 116,\n    \"first_name\": \"Hubert\",\n    \"last_name\": \"Life\",\n    \"email\": \"hlife37@w3.org\"\n  },\n  {\n    \"id\": 117,\n    \"first_name\": \"Patin\",\n    \"last_name\": \"Prestner\",\n    \"email\": \"pprestner38@flavors.me\"\n  },\n  {\n    \"id\": 118,\n    \"first_name\": \"Hedda\",\n    \"last_name\": \"MacKniely\",\n    \"email\": \"hmackniely39@blog.com\"\n  },\n  {\n    \"id\": 119,\n    \"first_name\": \"Rube\",\n    \"last_name\": \"Ceyssen\",\n    \"email\": \"rceyssen3a@businesswire.com\"\n  },\n  {\n    \"id\": 120,\n    \"first_name\": \"Clementine\",\n    \"last_name\": \"ffrench Beytagh\",\n    \"email\": \"cffrenchbeytagh3b@surveymonkey.com\"\n  },\n  {\n    \"id\": 122,\n    \"first_name\": \"Standford\",\n    \"last_name\": \"McGurn\",\n    \"email\": \"smcgurn3d@1688.com\"\n  },\n  {\n    \"id\": 123,\n    \"first_name\": \"Nelie\",\n    \"last_name\": \"Grebert\",\n    \"email\": \"ngrebert3e@bluehost.com\"\n  },\n  {\n    \"id\": 124,\n    \"first_name\": \"Milly\",\n    \"last_name\": \"Danielsohn\",\n    \"email\": \"mdanielsohn3f@topsy.com\"\n  },\n  {\n    \"id\": 125,\n    \"first_name\": \"Debor\",\n    \"last_name\": \"Pighills\",\n    \"email\": \"dpighills3g@google.es\"\n  },\n  {\n    \"id\": 126,\n    \"first_name\": \"Gal\",\n    \"last_name\": \"Allebone\",\n    \"email\": \"gallebone3h@amazonaws.com\"\n  },\n  {\n    \"id\": 127,\n    \"first_name\": \"Kerwinn\",\n    \"last_name\": \"Gheorghescu\",\n    \"email\": \"kgheorghescu3i@aol.com\"\n  },\n  {\n    \"id\": 128,\n    \"first_name\": \"Essa\",\n    \"last_name\": \"Fifield\",\n    \"email\": \"efifield3j@bbb.org\"\n  },\n  {\n    \"id\": 129,\n    \"first_name\": \"Philippine\",\n    \"last_name\": \"Daens\",\n    \"email\": \"pdaens3k@boston.com\"\n  },\n  {\n    \"id\": 130,\n    \"first_name\": \"Timmy\",\n    \"last_name\": \"Colbeck\",\n    \"email\": \"tcolbeck3l@answers.com\"\n  },\n  {\n    \"id\": 131,\n    \"first_name\": \"Raffarty\",\n    \"last_name\": \"Liverock\",\n    \"email\": \"rliverock3m@bandcamp.com\"\n  },\n  {\n    \"id\": 132,\n    \"first_name\": \"Valeria\",\n    \"last_name\": \"Marqyes\",\n    \"email\": \"vmarqyes3n@1688.com\"\n  },\n  {\n    \"id\": 133,\n    \"first_name\": \"Neille\",\n    \"last_name\": \"Seiler\",\n    \"email\": \"nseiler3o@skyrock.com\"\n  },\n  {\n    \"id\": 134,\n    \"first_name\": \"Anet\",\n    \"last_name\": \"Kelle\",\n    \"email\": \"akelle3p@opensource.org\"\n  },\n  {\n    \"id\": 135,\n    \"first_name\": \"Barnebas\",\n    \"last_name\": \"Alleway\",\n    \"email\": \"balleway3q@goodreads.com\"\n  },\n  {\n    \"id\": 136,\n    \"first_name\": \"Valeria\",\n    \"last_name\": \"Chrippes\",\n    \"email\": \"vchrippes3r@wunderground.com\"\n  },\n  {\n    \"id\": 137,\n    \"first_name\": \"Duffy\",\n    \"last_name\": \"Hainge\",\n    \"email\": \"dhainge3s@posterous.com\"\n  },\n  {\n    \"id\": 138,\n    \"first_name\": \"Gweneth\",\n    \"last_name\": \"Silberschatz\",\n    \"email\": \"gsilberschatz3t@bing.com\"\n  },\n  {\n    \"id\": 139,\n    \"first_name\": \"Fredelia\",\n    \"last_name\": \"Stodd\",\n    \"email\": \"fstodd3u@mit.edu\"\n  },\n  {\n    \"id\": 140,\n    \"first_name\": \"Buckie\",\n    \"last_name\": \"MacGinley\",\n    \"email\": \"bmacginley3v@irs.gov\"\n  },\n  {\n    \"id\": 141,\n    \"first_name\": \"Ardyth\",\n    \"last_name\": \"Ewbanck\",\n    \"email\": \"aewbanck3w@cisco.com\"\n  },\n  {\n    \"id\": 142,\n    \"first_name\": \"Darice\",\n    \"last_name\": \"Martinolli\",\n    \"email\": \"dmartinolli3x@seesaa.net\"\n  },\n  {\n    \"id\": 143,\n    \"first_name\": \"Bucky\",\n    \"last_name\": \"Chivrall\",\n    \"email\": \"bchivrall3y@blogger.com\"\n  },\n  {\n    \"id\": 144,\n    \"first_name\": \"Freida\",\n    \"last_name\": \"Labrom\",\n    \"email\": \"flabrom3z@github.io\"\n  },\n  {\n    \"id\": 145,\n    \"first_name\": \"Huntlee\",\n    \"last_name\": \"Comelini\",\n    \"email\": \"hcomelini40@army.mil\"\n  },\n  {\n    \"id\": 146,\n    \"first_name\": \"Lester\",\n    \"last_name\": \"Farrah\",\n    \"email\": \"lfarrah41@princeton.edu\"\n  },\n  {\n    \"id\": 147,\n    \"first_name\": \"Chickie\",\n    \"last_name\": \"Lyddon\",\n    \"email\": \"clyddon42@smh.com.au\"\n  },\n  {\n    \"id\": 148,\n    \"first_name\": \"Lenette\",\n    \"last_name\": \"McGaffey\",\n    \"email\": \"lmcgaffey43@auda.org.au\"\n  },\n  {\n    \"id\": 149,\n    \"first_name\": \"Cleavland\",\n    \"last_name\": \"Balassa\",\n    \"email\": \"cbalassa44@squidoo.com\"\n  },\n  {\n    \"id\": 150,\n    \"first_name\": \"Towney\",\n    \"last_name\": \"Wessell\",\n    \"email\": \"twessell45@bloglovin.com\"\n  },\n  {\n    \"id\": 151,\n    \"first_name\": \"Marlee\",\n    \"last_name\": \"Sahlstrom\",\n    \"email\": \"msahlstrom46@51.la\"\n  },\n  {\n    \"id\": 152,\n    \"first_name\": \"Tadd\",\n    \"last_name\": \"Showalter\",\n    \"email\": \"tshowalter47@irs.gov\"\n  },\n  {\n    \"id\": 153,\n    \"first_name\": \"Isabeau\",\n    \"last_name\": \"Smalcombe\",\n    \"email\": \"ismalcombe48@goo.gl\"\n  },\n  {\n    \"id\": 154,\n    \"first_name\": \"Aprilette\",\n    \"last_name\": \"Pyett\",\n    \"email\": \"apyett49@taobao.com\"\n  },\n  {\n    \"id\": 155,\n    \"first_name\": \"Bendite\",\n    \"last_name\": \"Odney\",\n    \"email\": \"bodney4a@123-reg.co.uk\"\n  },\n  {\n    \"id\": 156,\n    \"first_name\": \"Temp\",\n    \"last_name\": \"Scherer\",\n    \"email\": \"tscherer4b@phoca.cz\"\n  },\n  {\n    \"id\": 157,\n    \"first_name\": \"Barris\",\n    \"last_name\": \"Ferrarotti\",\n    \"email\": \"bferrarotti4c@bloomberg.com\"\n  },\n  {\n    \"id\": 158,\n    \"first_name\": \"Floris\",\n    \"last_name\": \"Loudiane\",\n    \"email\": \"floudiane4d@bbb.org\"\n  },\n  {\n    \"id\": 159,\n    \"first_name\": \"Ives\",\n    \"last_name\": \"MacArdle\",\n    \"email\": \"imacardle4e@phoca.cz\"\n  },\n  {\n    \"id\": 160,\n    \"first_name\": \"Briano\",\n    \"last_name\": \"Antonsen\",\n    \"email\": \"bantonsen4f@jimdo.com\"\n  },\n  {\n    \"id\": 161,\n    \"first_name\": \"Rea\",\n    \"last_name\": \"McCumskay\",\n    \"email\": \"rmccumskay4g@netvibes.com\"\n  },\n  {\n    \"id\": 162,\n    \"first_name\": \"Madlen\",\n    \"last_name\": \"Karppi\",\n    \"email\": \"mkarppi4h@prweb.com\"\n  },\n  {\n    \"id\": 163,\n    \"first_name\": \"Hillie\",\n    \"last_name\": \"Ollerenshaw\",\n    \"email\": \"hollerenshaw4i@mapquest.com\"\n  },\n  {\n    \"id\": 164,\n    \"first_name\": \"Laure\",\n    \"last_name\": \"Giacomazzo\",\n    \"email\": \"lgiacomazzo4j@canalblog.com\"\n  },\n  {\n    \"id\": 165,\n    \"first_name\": \"Shanie\",\n    \"last_name\": \"Worsam\",\n    \"email\": \"sworsam4k@google.ca\"\n  },\n  {\n    \"id\": 166,\n    \"first_name\": \"Bibbie\",\n    \"last_name\": \"Trosdall\",\n    \"email\": \"btrosdall4l@list-manage.com\"\n  },\n  {\n    \"id\": 167,\n    \"first_name\": \"Marcelia\",\n    \"last_name\": \"Symes\",\n    \"email\": \"msymes4m@facebook.com\"\n  },\n  {\n    \"id\": 168,\n    \"first_name\": \"Jolene\",\n    \"last_name\": \"Roja\",\n    \"email\": \"jroja4n@mail.ru\"\n  },\n  {\n    \"id\": 169,\n    \"first_name\": \"Colas\",\n    \"last_name\": \"Leal\",\n    \"email\": \"cleal4o@ustream.tv\"\n  },\n  {\n    \"id\": 170,\n    \"first_name\": \"Oby\",\n    \"last_name\": \"Faichnie\",\n    \"email\": \"ofaichnie4p@goo.gl\"\n  },\n  {\n    \"id\": 171,\n    \"first_name\": \"Henry\",\n    \"last_name\": \"Willows\",\n    \"email\": \"hwillows4q@alibaba.com\"\n  },\n  {\n    \"id\": 172,\n    \"first_name\": \"Matilda\",\n    \"last_name\": \"Korf\",\n    \"email\": \"mkorf4r@bbb.org\"\n  },\n  {\n    \"id\": 173,\n    \"first_name\": \"Hiram\",\n    \"last_name\": \"Balls\",\n    \"email\": \"hballs4s@nba.com\"\n  },\n  {\n    \"id\": 174,\n    \"first_name\": \"Terri-jo\",\n    \"last_name\": \"Atrill\",\n    \"email\": \"tatrill4t@so-net.ne.jp\"\n  },\n  {\n    \"id\": 175,\n    \"first_name\": \"Tana\",\n    \"last_name\": \"Ciccarello\",\n    \"email\": \"tciccarello4u@cisco.com\"\n  },\n  {\n    \"id\": 176,\n    \"first_name\": \"Abbie\",\n    \"last_name\": \"Rohfsen\",\n    \"email\": \"arohfsen4v@japanpost.jp\"\n  },\n  {\n    \"id\": 177,\n    \"first_name\": \"Dominga\",\n    \"last_name\": \"Johanssen\",\n    \"email\": \"djohanssen4w@phoca.cz\"\n  },\n  {\n    \"id\": 178,\n    \"first_name\": \"Osmond\",\n    \"last_name\": \"Ryland\",\n    \"email\": \"oryland4x@51.la\"\n  },\n  {\n    \"id\": 179,\n    \"first_name\": \"Waverly\",\n    \"last_name\": \"Butting\",\n    \"email\": \"wbutting4y@4shared.com\"\n  },\n  {\n    \"id\": 180,\n    \"first_name\": \"Colin\",\n    \"last_name\": \"Antosch\",\n    \"email\": \"cantosch4z@unblog.fr\"\n  },\n  {\n    \"id\": 181,\n    \"first_name\": \"Filide\",\n    \"last_name\": \"Birks\",\n    \"email\": \"fbirks50@domainmarket.com\"\n  },\n  {\n    \"id\": 182,\n    \"first_name\": \"Avery\",\n    \"last_name\": \"Kruschov\",\n    \"email\": \"akruschov51@answers.com\"\n  },\n  {\n    \"id\": 183,\n    \"first_name\": \"Doralynne\",\n    \"last_name\": \"Mosten\",\n    \"email\": \"dmosten52@discovery.com\"\n  },\n  {\n    \"id\": 184,\n    \"first_name\": \"Dicky\",\n    \"last_name\": \"Muggeridge\",\n    \"email\": \"dmuggeridge53@163.com\"\n  },\n  {\n    \"id\": 185,\n    \"first_name\": \"Tucky\",\n    \"last_name\": \"Tennick\",\n    \"email\": \"ttennick54@gnu.org\"\n  },\n  {\n    \"id\": 186,\n    \"first_name\": \"Jareb\",\n    \"last_name\": \"Hews\",\n    \"email\": \"jhews55@twitpic.com\"\n  },\n  {\n    \"id\": 187,\n    \"first_name\": \"Guthrie\",\n    \"last_name\": \"Castro\",\n    \"email\": \"gcastro56@berkeley.edu\"\n  },\n  {\n    \"id\": 188,\n    \"first_name\": \"Rayna\",\n    \"last_name\": \"Howett\",\n    \"email\": \"rhowett57@theguardian.com\"\n  },\n  {\n    \"id\": 189,\n    \"first_name\": \"Linnet\",\n    \"last_name\": \"Painter\",\n    \"email\": \"lpainter58@apache.org\"\n  },\n  {\n    \"id\": 190,\n    \"first_name\": \"Gisele\",\n    \"last_name\": \"Varcoe\",\n    \"email\": \"gvarcoe59@timesonline.co.uk\"\n  },\n  {\n    \"id\": 191,\n    \"first_name\": \"Isabelita\",\n    \"last_name\": \"Klimpke\",\n    \"email\": \"iklimpke5a@technorati.com\"\n  },\n  {\n    \"id\": 192,\n    \"first_name\": \"Ofilia\",\n    \"last_name\": \"Kondrachenko\",\n    \"email\": \"okondrachenko5b@github.com\"\n  },\n  {\n    \"id\": 193,\n    \"first_name\": \"Margaretta\",\n    \"last_name\": \"Costello\",\n    \"email\": \"mcostello5c@nifty.com\"\n  },\n  {\n    \"id\": 194,\n    \"first_name\": \"Elli\",\n    \"last_name\": \"Sudell\",\n    \"email\": \"esudell5d@stumbleupon.com\"\n  },\n  {\n    \"id\": 195,\n    \"first_name\": \"Carie\",\n    \"last_name\": \"Preto\",\n    \"email\": \"cpreto5e@prnewswire.com\"\n  },\n  {\n    \"id\": 196,\n    \"first_name\": \"Kinny\",\n    \"last_name\": \"Gredden\",\n    \"email\": \"kgredden5f@alexa.com\"\n  },\n  {\n    \"id\": 197,\n    \"first_name\": \"Grethel\",\n    \"last_name\": \"Warwicker\",\n    \"email\": \"gwarwicker5g@fda.gov\"\n  },\n  {\n    \"id\": 198,\n    \"first_name\": \"Gerti\",\n    \"last_name\": \"Kippling\",\n    \"email\": \"gkippling5h@mayoclinic.com\"\n  },\n  {\n    \"id\": 199,\n    \"first_name\": \"Nanice\",\n    \"last_name\": \"Sirrell\",\n    \"email\": \"nsirrell5i@paginegialle.it\"\n  },\n  {\n    \"id\": 200,\n    \"first_name\": \"Coraline\",\n    \"last_name\": \"Readie\",\n    \"email\": \"creadie5j@joomla.org\"\n  },\n  {\n    \"id\": 202,\n    \"first_name\": \"Heath\",\n    \"last_name\": \"McNeilley\",\n    \"email\": \"hmcneilley5l@soundcloud.com\"\n  },\n  {\n    \"id\": 203,\n    \"first_name\": \"Torey\",\n    \"last_name\": \"Lortz\",\n    \"email\": \"tlortz5m@printfriendly.com\"\n  },\n  {\n    \"id\": 204,\n    \"first_name\": \"Melodee\",\n    \"last_name\": \"McWhan\",\n    \"email\": \"mmcwhan5n@tinyurl.com\"\n  },\n  {\n    \"id\": 205,\n    \"first_name\": \"Selie\",\n    \"last_name\": \"Llywarch\",\n    \"email\": \"sllywarch5o@usda.gov\"\n  },\n  {\n    \"id\": 206,\n    \"first_name\": \"Britt\",\n    \"last_name\": \"Shore\",\n    \"email\": \"bshore5p@unesco.org\"\n  },\n  {\n    \"id\": 207,\n    \"first_name\": \"Floyd\",\n    \"last_name\": \"Hairyes\",\n    \"email\": \"fhairyes5q@sphinn.com\"\n  },\n  {\n    \"id\": 208,\n    \"first_name\": \"Maris\",\n    \"last_name\": \"Fretter\",\n    \"email\": \"mfretter5r@umich.edu\"\n  },\n  {\n    \"id\": 209,\n    \"first_name\": \"Andrey\",\n    \"last_name\": \"MacCaughey\",\n    \"email\": \"amaccaughey5s@blinklist.com\"\n  },\n  {\n    \"id\": 210,\n    \"first_name\": \"Reena\",\n    \"last_name\": \"Kiledal\",\n    \"email\": \"rkiledal5t@blogs.com\"\n  },\n  {\n    \"id\": 211,\n    \"first_name\": \"Adey\",\n    \"last_name\": \"Molohan\",\n    \"email\": \"amolohan5u@yale.edu\"\n  },\n  {\n    \"id\": 212,\n    \"first_name\": \"Eddie\",\n    \"last_name\": \"Simner\",\n    \"email\": \"esimner5v@purevolume.com\"\n  },\n  {\n    \"id\": 213,\n    \"first_name\": \"Eldon\",\n    \"last_name\": \"Dregan\",\n    \"email\": \"edregan5w@nytimes.com\"\n  },\n  {\n    \"id\": 214,\n    \"first_name\": \"Terencio\",\n    \"last_name\": \"Cordell\",\n    \"email\": \"tcordell5x@answers.com\"\n  },\n  {\n    \"id\": 215,\n    \"first_name\": \"Barbra\",\n    \"last_name\": \"Matzkaitis\",\n    \"email\": \"bmatzkaitis5y@nyu.edu\"\n  },\n  {\n    \"id\": 216,\n    \"first_name\": \"Agathe\",\n    \"last_name\": \"Filler\",\n    \"email\": \"afiller5z@etsy.com\"\n  },\n  {\n    \"id\": 217,\n    \"first_name\": \"Jenine\",\n    \"last_name\": \"Adds\",\n    \"email\": \"jadds60@squarespace.com\"\n  },\n  {\n    \"id\": 218,\n    \"first_name\": \"Kathy\",\n    \"last_name\": \"Lampbrecht\",\n    \"email\": \"klampbrecht61@t-online.de\"\n  },\n  {\n    \"id\": 219,\n    \"first_name\": \"Demetri\",\n    \"last_name\": \"Godfroy\",\n    \"email\": \"dgodfroy62@ibm.com\"\n  },\n  {\n    \"id\": 220,\n    \"first_name\": \"Katuscha\",\n    \"last_name\": \"Renon\",\n    \"email\": \"krenon63@friendfeed.com\"\n  },\n  {\n    \"id\": 221,\n    \"first_name\": \"Claudian\",\n    \"last_name\": \"Devenport\",\n    \"email\": \"cdevenport64@godaddy.com\"\n  },\n  {\n    \"id\": 222,\n    \"first_name\": \"Jenica\",\n    \"last_name\": \"Kornilov\",\n    \"email\": \"jkornilov65@nifty.com\"\n  },\n  {\n    \"id\": 223,\n    \"first_name\": \"Grissel\",\n    \"last_name\": \"McMeeking\",\n    \"email\": \"gmcmeeking66@boston.com\"\n  },\n  {\n    \"id\": 224,\n    \"first_name\": \"Andy\",\n    \"last_name\": \"Rushforth\",\n    \"email\": \"arushforth67@pcworld.com\"\n  },\n  {\n    \"id\": 225,\n    \"first_name\": \"Rana\",\n    \"last_name\": \"Ferrino\",\n    \"email\": \"rferrino68@deviantart.com\"\n  },\n  {\n    \"id\": 226,\n    \"first_name\": \"Celie\",\n    \"last_name\": \"Schenkel\",\n    \"email\": \"cschenkel69@cargocollective.com\"\n  },\n  {\n    \"id\": 227,\n    \"first_name\": \"Doe\",\n    \"last_name\": \"Chadwyck\",\n    \"email\": \"dchadwyck6a@cargocollective.com\"\n  },\n  {\n    \"id\": 228,\n    \"first_name\": \"Amandy\",\n    \"last_name\": \"Marmon\",\n    \"email\": \"amarmon6b@de.vu\"\n  },\n  {\n    \"id\": 229,\n    \"first_name\": \"Aliza\",\n    \"last_name\": \"Haggeth\",\n    \"email\": \"ahaggeth6c@ibm.com\"\n  },\n  {\n    \"id\": 230,\n    \"first_name\": \"Velma\",\n    \"last_name\": \"Olner\",\n    \"email\": \"volner6d@house.gov\"\n  },\n  {\n    \"id\": 231,\n    \"first_name\": \"Bent\",\n    \"last_name\": \"Ayllett\",\n    \"email\": \"bayllett6e@google.com.hk\"\n  },\n  {\n    \"id\": 232,\n    \"first_name\": \"Parrnell\",\n    \"last_name\": \"Walker\",\n    \"email\": \"pwalker6f@irs.gov\"\n  },\n  {\n    \"id\": 233,\n    \"first_name\": \"Mickie\",\n    \"last_name\": \"Nossent\",\n    \"email\": \"mnossent6g@elegantthemes.com\"\n  },\n  {\n    \"id\": 234,\n    \"first_name\": \"Sawyer\",\n    \"last_name\": \"Tranter\",\n    \"email\": \"stranter6h@usa.gov\"\n  },\n  {\n    \"id\": 235,\n    \"first_name\": \"Bernetta\",\n    \"last_name\": \"Twine\",\n    \"email\": \"btwine6i@mapy.cz\"\n  },\n  {\n    \"id\": 236,\n    \"first_name\": \"Richard\",\n    \"last_name\": \"Kerss\",\n    \"email\": \"rkerss6j@harvard.edu\"\n  },\n  {\n    \"id\": 237,\n    \"first_name\": \"Margo\",\n    \"last_name\": \"Danilov\",\n    \"email\": \"mdanilov6k@clickbank.net\"\n  },\n  {\n    \"id\": 238,\n    \"first_name\": \"Willem\",\n    \"last_name\": \"Sheen\",\n    \"email\": \"wsheen6l@feedburner.com\"\n  },\n  {\n    \"id\": 239,\n    \"first_name\": \"Dukey\",\n    \"last_name\": \"Regnard\",\n    \"email\": \"dregnard6m@opensource.org\"\n  },\n  {\n    \"id\": 240,\n    \"first_name\": \"Wynnie\",\n    \"last_name\": \"Manilove\",\n    \"email\": \"wmanilove6n@themeforest.net\"\n  },\n  {\n    \"id\": 241,\n    \"first_name\": \"Hartley\",\n    \"last_name\": \"Tungay\",\n    \"email\": \"htungay6o@rambler.ru\"\n  },\n  {\n    \"id\": 242,\n    \"first_name\": \"Brena\",\n    \"last_name\": \"Yaneev\",\n    \"email\": \"byaneev6p@java.com\"\n  },\n  {\n    \"id\": 243,\n    \"first_name\": \"Fred\",\n    \"last_name\": \"Leaf\",\n    \"email\": \"fleaf6q@usatoday.com\"\n  },\n  {\n    \"id\": 244,\n    \"first_name\": \"Morna\",\n    \"last_name\": \"Beardsworth\",\n    \"email\": \"mbeardsworth6r@kickstarter.com\"\n  },\n  {\n    \"id\": 245,\n    \"first_name\": \"Griffin\",\n    \"last_name\": \"Kell\",\n    \"email\": \"gkell6s@github.io\"\n  },\n  {\n    \"id\": 246,\n    \"first_name\": \"Ozzie\",\n    \"last_name\": \"Picford\",\n    \"email\": \"opicford6t@instagram.com\"\n  },\n  {\n    \"id\": 247,\n    \"first_name\": \"Carson\",\n    \"last_name\": \"Andrivot\",\n    \"email\": \"candrivot6u@1und1.de\"\n  },\n  {\n    \"id\": 248,\n    \"first_name\": \"Caressa\",\n    \"last_name\": \"Kupis\",\n    \"email\": \"ckupis6v@sakura.ne.jp\"\n  },\n  {\n    \"id\": 249,\n    \"first_name\": \"Philly\",\n    \"last_name\": \"Knowlys\",\n    \"email\": \"pknowlys6w@army.mil\"\n  },\n  {\n    \"id\": 250,\n    \"first_name\": \"Nady\",\n    \"last_name\": \"Rolling\",\n    \"email\": \"nrolling6x@lulu.com\"\n  },\n  {\n    \"id\": 251,\n    \"first_name\": \"Albertina\",\n    \"last_name\": \"Spurdle\",\n    \"email\": \"aspurdle6y@pinterest.com\"\n  },\n  {\n    \"id\": 252,\n    \"first_name\": \"Brittne\",\n    \"last_name\": \"Tamlett\",\n    \"email\": \"btamlett6z@princeton.edu\"\n  },\n  {\n    \"id\": 253,\n    \"first_name\": \"Biddy\",\n    \"last_name\": \"Station\",\n    \"email\": \"bstation70@altervista.org\"\n  },\n  {\n    \"id\": 254,\n    \"first_name\": \"Estelle\",\n    \"last_name\": \"Swaden\",\n    \"email\": \"eswaden71@nih.gov\"\n  },\n  {\n    \"id\": 255,\n    \"first_name\": \"Dael\",\n    \"last_name\": \"Noyce\",\n    \"email\": \"dnoyce72@europa.eu\"\n  },\n  {\n    \"id\": 256,\n    \"first_name\": \"Verne\",\n    \"last_name\": \"Tomasi\",\n    \"email\": \"vtomasi73@pinterest.com\"\n  },\n  {\n    \"id\": 257,\n    \"first_name\": \"Toinette\",\n    \"last_name\": \"Adame\",\n    \"email\": \"tadame74@csmonitor.com\"\n  },\n  {\n    \"id\": 258,\n    \"first_name\": \"Teri\",\n    \"last_name\": \"Karolewski\",\n    \"email\": \"tkarolewski75@exblog.jp\"\n  },\n  {\n    \"id\": 259,\n    \"first_name\": \"Theo\",\n    \"last_name\": \"Weildish\",\n    \"email\": \"tweildish76@hexun.com\"\n  },\n  {\n    \"id\": 260,\n    \"first_name\": \"Eloise\",\n    \"last_name\": \"McLafferty\",\n    \"email\": \"emclafferty77@prnewswire.com\"\n  },\n  {\n    \"id\": 261,\n    \"first_name\": \"Pepita\",\n    \"last_name\": \"Fontel\",\n    \"email\": \"pfontel78@chronoengine.com\"\n  },\n  {\n    \"id\": 262,\n    \"first_name\": \"Valentine\",\n    \"last_name\": \"Gerry\",\n    \"email\": \"vgerry79@slashdot.org\"\n  },\n  {\n    \"id\": 263,\n    \"first_name\": \"Fanni\",\n    \"last_name\": \"Goodbairn\",\n    \"email\": \"fgoodbairn7a@elpais.com\"\n  },\n  {\n    \"id\": 264,\n    \"first_name\": \"Esra\",\n    \"last_name\": \"Troppmann\",\n    \"email\": \"etroppmann7b@phoca.cz\"\n  },\n  {\n    \"id\": 265,\n    \"first_name\": \"Carlynn\",\n    \"last_name\": \"Vorley\",\n    \"email\": \"cvorley7c@uiuc.edu\"\n  },\n  {\n    \"id\": 266,\n    \"first_name\": \"Mellie\",\n    \"last_name\": \"McLeoid\",\n    \"email\": \"mmcleoid7d@mit.edu\"\n  },\n  {\n    \"id\": 267,\n    \"first_name\": \"Gaile\",\n    \"last_name\": \"Bucke\",\n    \"email\": \"gbucke7e@alexa.com\"\n  },\n  {\n    \"id\": 268,\n    \"first_name\": \"Sherye\",\n    \"last_name\": \"Sheahan\",\n    \"email\": \"ssheahan7f@parallels.com\"\n  },\n  {\n    \"id\": 269,\n    \"first_name\": \"Norean\",\n    \"last_name\": \"Desport\",\n    \"email\": \"ndesport7g@taobao.com\"\n  },\n  {\n    \"id\": 270,\n    \"first_name\": \"Ugo\",\n    \"last_name\": \"Kernell\",\n    \"email\": \"ukernell7h@csmonitor.com\"\n  },\n  {\n    \"id\": 271,\n    \"first_name\": \"Base\",\n    \"last_name\": \"Cullinane\",\n    \"email\": \"bcullinane7i@indiegogo.com\"\n  },\n  {\n    \"id\": 272,\n    \"first_name\": \"Alan\",\n    \"last_name\": \"Godmar\",\n    \"email\": \"agodmar7j@fc2.com\"\n  },\n  {\n    \"id\": 273,\n    \"first_name\": \"Olly\",\n    \"last_name\": \"Wellstood\",\n    \"email\": \"owellstood7k@friendfeed.com\"\n  },\n  {\n    \"id\": 274,\n    \"first_name\": \"Reuben\",\n    \"last_name\": \"Aveyard\",\n    \"email\": \"raveyard7l@lycos.com\"\n  },\n  {\n    \"id\": 275,\n    \"first_name\": \"Richmond\",\n    \"last_name\": \"Broadberrie\",\n    \"email\": \"rbroadberrie7m@facebook.com\"\n  },\n  {\n    \"id\": 276,\n    \"first_name\": \"Maressa\",\n    \"last_name\": \"Carlett\",\n    \"email\": \"mcarlett7n@bizjournals.com\"\n  },\n  {\n    \"id\": 277,\n    \"first_name\": \"Marina\",\n    \"last_name\": \"Sprasen\",\n    \"email\": \"msprasen7o@dropbox.com\"\n  },\n  {\n    \"id\": 278,\n    \"first_name\": \"Winnie\",\n    \"last_name\": \"Ostridge\",\n    \"email\": \"wostridge7p@ted.com\"\n  },\n  {\n    \"id\": 279,\n    \"first_name\": \"Briney\",\n    \"last_name\": \"Rosenschein\",\n    \"email\": \"brosenschein7q@macromedia.com\"\n  },\n  {\n    \"id\": 280,\n    \"first_name\": \"Heidie\",\n    \"last_name\": \"Yeldon\",\n    \"email\": \"hyeldon7r@parallels.com\"\n  },\n  {\n    \"id\": 281,\n    \"first_name\": \"Addie\",\n    \"last_name\": \"Coldicott\",\n    \"email\": \"acoldicott7s@eventbrite.com\"\n  },\n  {\n    \"id\": 282,\n    \"first_name\": \"Aubrette\",\n    \"last_name\": \"Doswell\",\n    \"email\": \"adoswell7t@imgur.com\"\n  },\n  {\n    \"id\": 283,\n    \"first_name\": \"Rouvin\",\n    \"last_name\": \"Kassman\",\n    \"email\": \"rkassman7u@tinyurl.com\"\n  },\n  {\n    \"id\": 284,\n    \"first_name\": \"Mitchell\",\n    \"last_name\": \"Pietzke\",\n    \"email\": \"mpietzke7v@wunderground.com\"\n  },\n  {\n    \"id\": 285,\n    \"first_name\": \"Eadmund\",\n    \"last_name\": \"Rawstron\",\n    \"email\": \"erawstron7w@yelp.com\"\n  },\n  {\n    \"id\": 286,\n    \"first_name\": \"Corri\",\n    \"last_name\": \"Matyasik\",\n    \"email\": \"cmatyasik7x@weibo.com\"\n  },\n  {\n    \"id\": 287,\n    \"first_name\": \"Chuck\",\n    \"last_name\": \"Blandamere\",\n    \"email\": \"cblandamere7y@google.cn\"\n  },\n  {\n    \"id\": 288,\n    \"first_name\": \"Nari\",\n    \"last_name\": \"Edmondson\",\n    \"email\": \"nedmondson7z@cnbc.com\"\n  },\n  {\n    \"id\": 289,\n    \"first_name\": \"Valentine\",\n    \"last_name\": \"Sivyour\",\n    \"email\": \"vsivyour80@reverbnation.com\"\n  },\n  {\n    \"id\": 290,\n    \"first_name\": \"Darryl\",\n    \"last_name\": \"Hawket\",\n    \"email\": \"dhawket81@pcworld.com\"\n  },\n  {\n    \"id\": 291,\n    \"first_name\": \"Teddie\",\n    \"last_name\": \"Prosek\",\n    \"email\": \"tprosek82@odnoklassniki.ru\"\n  },\n  {\n    \"id\": 292,\n    \"first_name\": \"Chloris\",\n    \"last_name\": \"Linder\",\n    \"email\": \"clinder83@instagram.com\"\n  },\n  {\n    \"id\": 293,\n    \"first_name\": \"Lonnie\",\n    \"last_name\": \"Glede\",\n    \"email\": \"lglede84@taobao.com\"\n  },\n  {\n    \"id\": 294,\n    \"first_name\": \"Denise\",\n    \"last_name\": \"Deakin\",\n    \"email\": \"ddeakin85@si.edu\"\n  },\n  {\n    \"id\": 295,\n    \"first_name\": \"Ruprecht\",\n    \"last_name\": \"Sandcroft\",\n    \"email\": \"rsandcroft86@abc.net.au\"\n  },\n  {\n    \"id\": 296,\n    \"first_name\": \"Zorah\",\n    \"last_name\": \"Patridge\",\n    \"email\": \"zpatridge87@freewebs.com\"\n  },\n  {\n    \"id\": 297,\n    \"first_name\": \"Abigail\",\n    \"last_name\": \"Chatain\",\n    \"email\": \"achatain88@google.cn\"\n  },\n  {\n    \"id\": 298,\n    \"first_name\": \"Neala\",\n    \"last_name\": \"Osichev\",\n    \"email\": \"nosichev89@hostgator.com\"\n  },\n  {\n    \"id\": 299,\n    \"first_name\": \"Debora\",\n    \"last_name\": \"Crocetto\",\n    \"email\": \"dcrocetto8a@1und1.de\"\n  },\n  {\n    \"id\": 300,\n    \"first_name\": \"Dall\",\n    \"last_name\": \"Labeuil\",\n    \"email\": \"dlabeuil8b@ucsd.edu\"\n  },\n  {\n    \"id\": 301,\n    \"first_name\": \"Bunny\",\n    \"last_name\": \"McSperrin\",\n    \"email\": \"bmcsperrin8c@hubpages.com\"\n  },\n  {\n    \"id\": 302,\n    \"first_name\": \"Marianne\",\n    \"last_name\": \"Sabbin\",\n    \"email\": \"msabbin8d@hc360.com\"\n  },\n  {\n    \"id\": 303,\n    \"first_name\": \"Anette\",\n    \"last_name\": \"Wickersley\",\n    \"email\": \"awickersley8e@businesswire.com\"\n  },\n  {\n    \"id\": 304,\n    \"first_name\": \"Millisent\",\n    \"last_name\": \"Heinemann\",\n    \"email\": \"mheinemann8f@ameblo.jp\"\n  },\n  {\n    \"id\": 305,\n    \"first_name\": \"Kala\",\n    \"last_name\": \"Attock\",\n    \"email\": \"kattock8g@theatlantic.com\"\n  },\n  {\n    \"id\": 306,\n    \"first_name\": \"Meriel\",\n    \"last_name\": \"Vasyukhin\",\n    \"email\": \"mvasyukhin8h@linkedin.com\"\n  },\n  {\n    \"id\": 307,\n    \"first_name\": \"Julianne\",\n    \"last_name\": \"Baudou\",\n    \"email\": \"jbaudou8i@cbc.ca\"\n  },\n  {\n    \"id\": 308,\n    \"first_name\": \"Pryce\",\n    \"last_name\": \"Landal\",\n    \"email\": \"plandal8j@myspace.com\"\n  },\n  {\n    \"id\": 309,\n    \"first_name\": \"Nerissa\",\n    \"last_name\": \"Dreghorn\",\n    \"email\": \"ndreghorn8k@usda.gov\"\n  },\n  {\n    \"id\": 310,\n    \"first_name\": \"Clyve\",\n    \"last_name\": \"Soldner\",\n    \"email\": \"csoldner8l@bluehost.com\"\n  },\n  {\n    \"id\": 311,\n    \"first_name\": \"Hadlee\",\n    \"last_name\": \"Syplus\",\n    \"email\": \"hsyplus8m@auda.org.au\"\n  },\n  {\n    \"id\": 312,\n    \"first_name\": \"Prudi\",\n    \"last_name\": \"Merkel\",\n    \"email\": \"pmerkel8n@hao123.com\"\n  },\n  {\n    \"id\": 313,\n    \"first_name\": \"Zebulon\",\n    \"last_name\": \"Denisyev\",\n    \"email\": \"zdenisyev8o@parallels.com\"\n  },\n  {\n    \"id\": 314,\n    \"first_name\": \"Leopold\",\n    \"last_name\": \"Laddle\",\n    \"email\": \"lladdle8p@si.edu\"\n  },\n  {\n    \"id\": 315,\n    \"first_name\": \"Danella\",\n    \"last_name\": \"Aymes\",\n    \"email\": \"daymes8q@earthlink.net\"\n  },\n  {\n    \"id\": 316,\n    \"first_name\": \"Murdock\",\n    \"last_name\": \"De Ath\",\n    \"email\": \"mdeath8r@naver.com\"\n  },\n  {\n    \"id\": 317,\n    \"first_name\": \"Umeko\",\n    \"last_name\": \"Feavearyear\",\n    \"email\": \"ufeavearyear8s@youtube.com\"\n  },\n  {\n    \"id\": 318,\n    \"first_name\": \"Valli\",\n    \"last_name\": \"Neary\",\n    \"email\": \"vneary8t@vimeo.com\"\n  },\n  {\n    \"id\": 319,\n    \"first_name\": \"Kendell\",\n    \"last_name\": \"Blaby\",\n    \"email\": \"kblaby8u@tuttocitta.it\"\n  },\n  {\n    \"id\": 320,\n    \"first_name\": \"Ahmad\",\n    \"last_name\": \"Tate\",\n    \"email\": \"atate8v@friendfeed.com\"\n  },\n  {\n    \"id\": 321,\n    \"first_name\": \"Cullan\",\n    \"last_name\": \"Christofol\",\n    \"email\": \"cchristofol8w@hao123.com\"\n  },\n  {\n    \"id\": 322,\n    \"first_name\": \"Nolan\",\n    \"last_name\": \"Betser\",\n    \"email\": \"nbetser8x@github.io\"\n  },\n  {\n    \"id\": 323,\n    \"first_name\": \"Vachel\",\n    \"last_name\": \"Burrage\",\n    \"email\": \"vburrage8y@cargocollective.com\"\n  },\n  {\n    \"id\": 324,\n    \"first_name\": \"Gigi\",\n    \"last_name\": \"McCaughran\",\n    \"email\": \"gmccaughran8z@fda.gov\"\n  },\n  {\n    \"id\": 325,\n    \"first_name\": \"Leanora\",\n    \"last_name\": \"Epple\",\n    \"email\": \"lepple90@stanford.edu\"\n  },\n  {\n    \"id\": 326,\n    \"first_name\": \"Kathi\",\n    \"last_name\": \"Yearnes\",\n    \"email\": \"kyearnes91@sfgate.com\"\n  },\n  {\n    \"id\": 327,\n    \"first_name\": \"Tadio\",\n    \"last_name\": \"Salleir\",\n    \"email\": \"tsalleir92@a8.net\"\n  },\n  {\n    \"id\": 328,\n    \"first_name\": \"Kev\",\n    \"last_name\": \"Mayell\",\n    \"email\": \"kmayell93@weibo.com\"\n  },\n  {\n    \"id\": 329,\n    \"first_name\": \"Clarissa\",\n    \"last_name\": \"Hartley\",\n    \"email\": \"chartley94@networksolutions.com\"\n  },\n  {\n    \"id\": 330,\n    \"first_name\": \"Cindie\",\n    \"last_name\": \"Skyme\",\n    \"email\": \"cskyme95@etsy.com\"\n  },\n  {\n    \"id\": 331,\n    \"first_name\": \"Timoteo\",\n    \"last_name\": \"Wieprecht\",\n    \"email\": \"twieprecht96@dion.ne.jp\"\n  },\n  {\n    \"id\": 332,\n    \"first_name\": \"Aloise\",\n    \"last_name\": \"Parres\",\n    \"email\": \"aparres97@infoseek.co.jp\"\n  },\n  {\n    \"id\": 333,\n    \"first_name\": \"Ezri\",\n    \"last_name\": \"Jacobsen\",\n    \"email\": \"ejacobsen98@google.de\"\n  },\n  {\n    \"id\": 334,\n    \"first_name\": \"Lewie\",\n    \"last_name\": \"Ambroz\",\n    \"email\": \"lambroz99@youku.com\"\n  },\n  {\n    \"id\": 335,\n    \"first_name\": \"Kerwin\",\n    \"last_name\": \"Ceney\",\n    \"email\": \"kceney9a@comsenz.com\"\n  },\n  {\n    \"id\": 336,\n    \"first_name\": \"Frederich\",\n    \"last_name\": \"Crolly\",\n    \"email\": \"fcrolly9b@shareasale.com\"\n  },\n  {\n    \"id\": 337,\n    \"first_name\": \"Sayer\",\n    \"last_name\": \"Matanin\",\n    \"email\": \"smatanin9c@newsvine.com\"\n  },\n  {\n    \"id\": 338,\n    \"first_name\": \"Jennifer\",\n    \"last_name\": \"Vasyatkin\",\n    \"email\": \"jvasyatkin9d@chronoengine.com\"\n  },\n  {\n    \"id\": 339,\n    \"first_name\": \"Nicky\",\n    \"last_name\": \"Heinsh\",\n    \"email\": \"nheinsh9e@technorati.com\"\n  },\n  {\n    \"id\": 340,\n    \"first_name\": \"Neda\",\n    \"last_name\": \"Lanon\",\n    \"email\": \"nlanon9f@toplist.cz\"\n  },\n  {\n    \"id\": 341,\n    \"first_name\": \"Elbertine\",\n    \"last_name\": \"Larkcum\",\n    \"email\": \"elarkcum9g@a8.net\"\n  },\n  {\n    \"id\": 342,\n    \"first_name\": \"Whitby\",\n    \"last_name\": \"Farrell\",\n    \"email\": \"wfarrell9h@dailymail.co.uk\"\n  },\n  {\n    \"id\": 343,\n    \"first_name\": \"Dun\",\n    \"last_name\": \"Mackieson\",\n    \"email\": \"dmackieson9i@weebly.com\"\n  },\n  {\n    \"id\": 344,\n    \"first_name\": \"Krishna\",\n    \"last_name\": \"Tacon\",\n    \"email\": \"ktacon9j@w3.org\"\n  },\n  {\n    \"id\": 345,\n    \"first_name\": \"Dyna\",\n    \"last_name\": \"Sneezum\",\n    \"email\": \"dsneezum9k@sfgate.com\"\n  },\n  {\n    \"id\": 346,\n    \"first_name\": \"Gardner\",\n    \"last_name\": \"Habercham\",\n    \"email\": \"ghabercham9l@goodreads.com\"\n  },\n  {\n    \"id\": 347,\n    \"first_name\": \"Kalil\",\n    \"last_name\": \"Reinmar\",\n    \"email\": \"kreinmar9m@google.ru\"\n  },\n  {\n    \"id\": 348,\n    \"first_name\": \"Karly\",\n    \"last_name\": \"Cribbins\",\n    \"email\": \"kcribbins9n@ustream.tv\"\n  },\n  {\n    \"id\": 349,\n    \"first_name\": \"Jeanne\",\n    \"last_name\": \"Easen\",\n    \"email\": \"jeasen9o@time.com\"\n  },\n  {\n    \"id\": 350,\n    \"first_name\": \"Yorgo\",\n    \"last_name\": \"de Courcy\",\n    \"email\": \"ydecourcy9p@reference.com\"\n  },\n  {\n    \"id\": 351,\n    \"first_name\": \"Dyanna\",\n    \"last_name\": \"Wordesworth\",\n    \"email\": \"dwordesworth9q@clickbank.net\"\n  },\n  {\n    \"id\": 352,\n    \"first_name\": \"Ashien\",\n    \"last_name\": \"Whittles\",\n    \"email\": \"awhittles9r@dell.com\"\n  },\n  {\n    \"id\": 353,\n    \"first_name\": \"Alia\",\n    \"last_name\": \"Paradin\",\n    \"email\": \"aparadin9s@ucsd.edu\"\n  },\n  {\n    \"id\": 354,\n    \"first_name\": \"Babbie\",\n    \"last_name\": \"Palethorpe\",\n    \"email\": \"bpalethorpe9t@sciencedirect.com\"\n  },\n  {\n    \"id\": 355,\n    \"first_name\": \"Mort\",\n    \"last_name\": \"Hargie\",\n    \"email\": \"mhargie9u@nyu.edu\"\n  },\n  {\n    \"id\": 356,\n    \"first_name\": \"Lucais\",\n    \"last_name\": \"Writer\",\n    \"email\": \"lwriter9v@domainmarket.com\"\n  },\n  {\n    \"id\": 357,\n    \"first_name\": \"Lucho\",\n    \"last_name\": \"Robley\",\n    \"email\": \"lrobley9w@cargocollective.com\"\n  },\n  {\n    \"id\": 358,\n    \"first_name\": \"Drucie\",\n    \"last_name\": \"Hapgood\",\n    \"email\": \"dhapgood9x@ft.com\"\n  },\n  {\n    \"id\": 359,\n    \"first_name\": \"Arin\",\n    \"last_name\": \"Boddy\",\n    \"email\": \"aboddy9y@cdbaby.com\"\n  },\n  {\n    \"id\": 360,\n    \"first_name\": \"Biddy\",\n    \"last_name\": \"Ewles\",\n    \"email\": \"bewles9z@globo.com\"\n  },\n  {\n    \"id\": 361,\n    \"first_name\": \"Marlon\",\n    \"last_name\": \"Allder\",\n    \"email\": \"malldera0@t.co\"\n  },\n  {\n    \"id\": 362,\n    \"first_name\": \"Jock\",\n    \"last_name\": \"Ing\",\n    \"email\": \"jinga1@ocn.ne.jp\"\n  },\n  {\n    \"id\": 363,\n    \"first_name\": \"Franny\",\n    \"last_name\": \"Taverner\",\n    \"email\": \"ftavernera2@ezinearticles.com\"\n  },\n  {\n    \"id\": 364,\n    \"first_name\": \"Vanda\",\n    \"last_name\": \"Whiterod\",\n    \"email\": \"vwhiteroda3@usda.gov\"\n  },\n  {\n    \"id\": 365,\n    \"first_name\": \"Lezlie\",\n    \"last_name\": \"Godbehere\",\n    \"email\": \"lgodbeherea4@youtube.com\"\n  },\n  {\n    \"id\": 366,\n    \"first_name\": \"Rebecka\",\n    \"last_name\": \"Scarsbrook\",\n    \"email\": \"rscarsbrooka5@myspace.com\"\n  },\n  {\n    \"id\": 367,\n    \"first_name\": \"Abba\",\n    \"last_name\": \"Mingotti\",\n    \"email\": \"amingottia6@tuttocitta.it\"\n  },\n  {\n    \"id\": 368,\n    \"first_name\": \"Miguela\",\n    \"last_name\": \"McNysche\",\n    \"email\": \"mmcnyschea7@t-online.de\"\n  },\n  {\n    \"id\": 369,\n    \"first_name\": \"Weider\",\n    \"last_name\": \"Rosenau\",\n    \"email\": \"wrosenaua8@mysql.com\"\n  },\n  {\n    \"id\": 370,\n    \"first_name\": \"Antonietta\",\n    \"last_name\": \"Littefair\",\n    \"email\": \"alittefaira9@xing.com\"\n  },\n  {\n    \"id\": 371,\n    \"first_name\": \"Heda\",\n    \"last_name\": \"Wheowall\",\n    \"email\": \"hwheowallaa@360.cn\"\n  },\n  {\n    \"id\": 372,\n    \"first_name\": \"Nettle\",\n    \"last_name\": \"Semonin\",\n    \"email\": \"nsemoninab@patch.com\"\n  },\n  {\n    \"id\": 373,\n    \"first_name\": \"Sheri\",\n    \"last_name\": \"Baudry\",\n    \"email\": \"sbaudryac@google.ru\"\n  },\n  {\n    \"id\": 374,\n    \"first_name\": \"Janna\",\n    \"last_name\": \"Bogue\",\n    \"email\": \"jboguead@illinois.edu\"\n  },\n  {\n    \"id\": 375,\n    \"first_name\": \"Saundra\",\n    \"last_name\": \"Skaid\",\n    \"email\": \"sskaidae@ycombinator.com\"\n  },\n  {\n    \"id\": 376,\n    \"first_name\": \"Xenia\",\n    \"last_name\": \"Cadden\",\n    \"email\": \"xcaddenaf@booking.com\"\n  },\n  {\n    \"id\": 377,\n    \"first_name\": \"Claudia\",\n    \"last_name\": \"Spirit\",\n    \"email\": \"cspiritag@linkedin.com\"\n  },\n  {\n    \"id\": 378,\n    \"first_name\": \"Willard\",\n    \"last_name\": \"Grimwood\",\n    \"email\": \"wgrimwoodah@oracle.com\"\n  },\n  {\n    \"id\": 379,\n    \"first_name\": \"Smith\",\n    \"last_name\": \"Allenby\",\n    \"email\": \"sallenbyai@godaddy.com\"\n  },\n  {\n    \"id\": 380,\n    \"first_name\": \"Morlee\",\n    \"last_name\": \"Bernardin\",\n    \"email\": \"mbernardinaj@list-manage.com\"\n  },\n  {\n    \"id\": 381,\n    \"first_name\": \"Murvyn\",\n    \"last_name\": \"Becom\",\n    \"email\": \"mbecomak@sourceforge.net\"\n  },\n  {\n    \"id\": 382,\n    \"first_name\": \"Teriann\",\n    \"last_name\": \"Flori\",\n    \"email\": \"tflorial@xinhuanet.com\"\n  },\n  {\n    \"id\": 383,\n    \"first_name\": \"Liva\",\n    \"last_name\": \"Cabera\",\n    \"email\": \"lcaberaam@t.co\"\n  },\n  {\n    \"id\": 384,\n    \"first_name\": \"Andrej\",\n    \"last_name\": \"Hearnden\",\n    \"email\": \"ahearndenan@mysql.com\"\n  },\n  {\n    \"id\": 385,\n    \"first_name\": \"Reg\",\n    \"last_name\": \"Vollam\",\n    \"email\": \"rvollamao@networkadvertising.org\"\n  },\n  {\n    \"id\": 386,\n    \"first_name\": \"Kimberlyn\",\n    \"last_name\": \"Bedells\",\n    \"email\": \"kbedellsap@bbc.co.uk\"\n  },\n  {\n    \"id\": 387,\n    \"first_name\": \"Cam\",\n    \"last_name\": \"Quantrill\",\n    \"email\": \"cquantrillaq@alibaba.com\"\n  },\n  {\n    \"id\": 388,\n    \"first_name\": \"Giuditta\",\n    \"last_name\": \"Force\",\n    \"email\": \"gforcear@delicious.com\"\n  },\n  {\n    \"id\": 389,\n    \"first_name\": \"Lanie\",\n    \"last_name\": \"MacQueen\",\n    \"email\": \"lmacqueenas@flickr.com\"\n  },\n  {\n    \"id\": 390,\n    \"first_name\": \"Marja\",\n    \"last_name\": \"O'Dempsey\",\n    \"email\": \"modempseyat@auda.org.au\"\n  },\n  {\n    \"id\": 391,\n    \"first_name\": \"Howey\",\n    \"last_name\": \"St Clair\",\n    \"email\": \"hstclairau@soundcloud.com\"\n  },\n  {\n    \"id\": 392,\n    \"first_name\": \"Carolynn\",\n    \"last_name\": \"Khotler\",\n    \"email\": \"ckhotlerav@utexas.edu\"\n  },\n  {\n    \"id\": 393,\n    \"first_name\": \"Theodosia\",\n    \"last_name\": \"Ort\",\n    \"email\": \"tortaw@shop-pro.jp\"\n  },\n  {\n    \"id\": 394,\n    \"first_name\": \"West\",\n    \"last_name\": \"Matchett\",\n    \"email\": \"wmatchettax@cbc.ca\"\n  },\n  {\n    \"id\": 395,\n    \"first_name\": \"Vivianne\",\n    \"last_name\": \"Wheelwright\",\n    \"email\": \"vwheelwrightay@washingtonpost.com\"\n  },\n  {\n    \"id\": 396,\n    \"first_name\": \"Pennie\",\n    \"last_name\": \"Beames\",\n    \"email\": \"pbeamesaz@drupal.org\"\n  },\n  {\n    \"id\": 397,\n    \"first_name\": \"Nady\",\n    \"last_name\": \"Letch\",\n    \"email\": \"nletchb0@skype.com\"\n  },\n  {\n    \"id\": 398,\n    \"first_name\": \"Moselle\",\n    \"last_name\": \"Maytum\",\n    \"email\": \"mmaytumb1@usgs.gov\"\n  },\n  {\n    \"id\": 399,\n    \"first_name\": \"Jennilee\",\n    \"last_name\": \"Kid\",\n    \"email\": \"jkidb2@wisc.edu\"\n  },\n  {\n    \"id\": 400,\n    \"first_name\": \"Parnell\",\n    \"last_name\": \"Gong\",\n    \"email\": \"pgongb3@oaic.gov.au\"\n  },\n  {\n    \"id\": 401,\n    \"first_name\": \"Noll\",\n    \"last_name\": \"Kohtler\",\n    \"email\": \"nkohtlerb4@opensource.org\"\n  },\n  {\n    \"id\": 402,\n    \"first_name\": \"Sonya\",\n    \"last_name\": \"Orris\",\n    \"email\": \"sorrisb5@bandcamp.com\"\n  },\n  {\n    \"id\": 403,\n    \"first_name\": \"Bronnie\",\n    \"last_name\": \"Guillotin\",\n    \"email\": \"bguillotinb6@geocities.jp\"\n  },\n  {\n    \"id\": 404,\n    \"first_name\": \"Inger\",\n    \"last_name\": \"Clipsham\",\n    \"email\": \"iclipshamb7@psu.edu\"\n  },\n  {\n    \"id\": 405,\n    \"first_name\": \"Melina\",\n    \"last_name\": \"Grigorio\",\n    \"email\": \"mgrigoriob8@eventbrite.com\"\n  },\n  {\n    \"id\": 406,\n    \"first_name\": \"Jermain\",\n    \"last_name\": \"Thraves\",\n    \"email\": \"jthravesb9@biblegateway.com\"\n  },\n  {\n    \"id\": 407,\n    \"first_name\": \"Jock\",\n    \"last_name\": \"Payn\",\n    \"email\": \"jpaynba@google.cn\"\n  },\n  {\n    \"id\": 408,\n    \"first_name\": \"Nikolia\",\n    \"last_name\": \"Sterre\",\n    \"email\": \"nsterrebb@google.ru\"\n  },\n  {\n    \"id\": 409,\n    \"first_name\": \"Rosemarie\",\n    \"last_name\": \"Caurah\",\n    \"email\": \"rcaurahbc@sohu.com\"\n  },\n  {\n    \"id\": 410,\n    \"first_name\": \"Conney\",\n    \"last_name\": \"Spawell\",\n    \"email\": \"cspawellbd@ycombinator.com\"\n  },\n  {\n    \"id\": 411,\n    \"first_name\": \"Hernando\",\n    \"last_name\": \"Percival\",\n    \"email\": \"hpercivalbe@cisco.com\"\n  },\n  {\n    \"id\": 412,\n    \"first_name\": \"Michale\",\n    \"last_name\": \"Stadding\",\n    \"email\": \"mstaddingbf@bandcamp.com\"\n  },\n  {\n    \"id\": 413,\n    \"first_name\": \"Zulema\",\n    \"last_name\": \"Danks\",\n    \"email\": \"zdanksbg@ovh.net\"\n  },\n  {\n    \"id\": 414,\n    \"first_name\": \"Olia\",\n    \"last_name\": \"Joost\",\n    \"email\": \"ojoostbh@soup.io\"\n  },\n  {\n    \"id\": 416,\n    \"first_name\": \"Corbet\",\n    \"last_name\": \"Cliff\",\n    \"email\": \"ccliffbj@oaic.gov.au\"\n  },\n  {\n    \"id\": 417,\n    \"first_name\": \"Georgetta\",\n    \"last_name\": \"Tinto\",\n    \"email\": \"gtintobk@craigslist.org\"\n  },\n  {\n    \"id\": 418,\n    \"first_name\": \"Priscilla\",\n    \"last_name\": \"Philips\",\n    \"email\": \"pphilipsbl@ox.ac.uk\"\n  },\n  {\n    \"id\": 419,\n    \"first_name\": \"David\",\n    \"last_name\": \"Mulcock\",\n    \"email\": \"dmulcockbm@nytimes.com\"\n  },\n  {\n    \"id\": 420,\n    \"first_name\": \"Agatha\",\n    \"last_name\": \"Hek\",\n    \"email\": \"ahekbn@homestead.com\"\n  },\n  {\n    \"id\": 421,\n    \"first_name\": \"Burty\",\n    \"last_name\": \"Ceschini\",\n    \"email\": \"bceschinibo@jimdo.com\"\n  },\n  {\n    \"id\": 422,\n    \"first_name\": \"Ange\",\n    \"last_name\": \"Maeer\",\n    \"email\": \"amaeerbp@feedburner.com\"\n  },\n  {\n    \"id\": 423,\n    \"first_name\": \"Dannel\",\n    \"last_name\": \"Sackes\",\n    \"email\": \"dsackesbq@pbs.org\"\n  },\n  {\n    \"id\": 424,\n    \"first_name\": \"Lorrie\",\n    \"last_name\": \"Entres\",\n    \"email\": \"lentresbr@ebay.co.uk\"\n  },\n  {\n    \"id\": 425,\n    \"first_name\": \"Oswell\",\n    \"last_name\": \"Patrick\",\n    \"email\": \"opatrickbs@answers.com\"\n  },\n  {\n    \"id\": 426,\n    \"first_name\": \"Nefen\",\n    \"last_name\": \"Sedgefield\",\n    \"email\": \"nsedgefieldbt@google.com.au\"\n  },\n  {\n    \"id\": 427,\n    \"first_name\": \"Crichton\",\n    \"last_name\": \"Giorgione\",\n    \"email\": \"cgiorgionebu@va.gov\"\n  },\n  {\n    \"id\": 428,\n    \"first_name\": \"Shaylynn\",\n    \"last_name\": \"Bulstrode\",\n    \"email\": \"sbulstrodebv@ehow.com\"\n  },\n  {\n    \"id\": 429,\n    \"first_name\": \"Randolf\",\n    \"last_name\": \"Pickvance\",\n    \"email\": \"rpickvancebw@istockphoto.com\"\n  },\n  {\n    \"id\": 430,\n    \"first_name\": \"Diarmid\",\n    \"last_name\": \"Lias\",\n    \"email\": \"dliasbx@alexa.com\"\n  },\n  {\n    \"id\": 431,\n    \"first_name\": \"Francis\",\n    \"last_name\": \"Clipson\",\n    \"email\": \"fclipsonby@google.com.au\"\n  },\n  {\n    \"id\": 432,\n    \"first_name\": \"Lorene\",\n    \"last_name\": \"Maciejewski\",\n    \"email\": \"lmaciejewskibz@nytimes.com\"\n  },\n  {\n    \"id\": 433,\n    \"first_name\": \"Kizzee\",\n    \"last_name\": \"Klammt\",\n    \"email\": \"kklammtc0@vkontakte.ru\"\n  },\n  {\n    \"id\": 434,\n    \"first_name\": \"Zolly\",\n    \"last_name\": \"Cattle\",\n    \"email\": \"zcattlec1@va.gov\"\n  },\n  {\n    \"id\": 435,\n    \"first_name\": \"Kattie\",\n    \"last_name\": \"Chidwick\",\n    \"email\": \"kchidwickc2@hatena.ne.jp\"\n  },\n  {\n    \"id\": 436,\n    \"first_name\": \"Izabel\",\n    \"last_name\": \"Weight\",\n    \"email\": \"iweightc3@ycombinator.com\"\n  },\n  {\n    \"id\": 437,\n    \"first_name\": \"Kerby\",\n    \"last_name\": \"Redler\",\n    \"email\": \"kredlerc4@google.es\"\n  },\n  {\n    \"id\": 438,\n    \"first_name\": \"Wynn\",\n    \"last_name\": \"Glass\",\n    \"email\": \"wglassc5@yelp.com\"\n  },\n  {\n    \"id\": 439,\n    \"first_name\": \"Jemmie\",\n    \"last_name\": \"Scorey\",\n    \"email\": \"jscoreyc6@blogspot.com\"\n  },\n  {\n    \"id\": 440,\n    \"first_name\": \"Krispin\",\n    \"last_name\": \"Kirstein\",\n    \"email\": \"kkirsteinc7@elegantthemes.com\"\n  },\n  {\n    \"id\": 441,\n    \"first_name\": \"Mil\",\n    \"last_name\": \"Ogdahl\",\n    \"email\": \"mogdahlc8@ft.com\"\n  },\n  {\n    \"id\": 442,\n    \"first_name\": \"Van\",\n    \"last_name\": \"Bernholt\",\n    \"email\": \"vbernholtc9@indiegogo.com\"\n  },\n  {\n    \"id\": 443,\n    \"first_name\": \"Ilyse\",\n    \"last_name\": \"Boecke\",\n    \"email\": \"iboeckeca@a8.net\"\n  },\n  {\n    \"id\": 444,\n    \"first_name\": \"Gabby\",\n    \"last_name\": \"Silcock\",\n    \"email\": \"gsilcockcb@ow.ly\"\n  },\n  {\n    \"id\": 445,\n    \"first_name\": \"Ulberto\",\n    \"last_name\": \"Edgeley\",\n    \"email\": \"uedgeleycc@blinklist.com\"\n  },\n  {\n    \"id\": 446,\n    \"first_name\": \"Bary\",\n    \"last_name\": \"McGuinley\",\n    \"email\": \"bmcguinleycd@icq.com\"\n  },\n  {\n    \"id\": 447,\n    \"first_name\": \"Willie\",\n    \"last_name\": \"Whyard\",\n    \"email\": \"wwhyardce@msn.com\"\n  },\n  {\n    \"id\": 448,\n    \"first_name\": \"Borden\",\n    \"last_name\": \"Thrussell\",\n    \"email\": \"bthrussellcf@rediff.com\"\n  },\n  {\n    \"id\": 449,\n    \"first_name\": \"Pincus\",\n    \"last_name\": \"McMahon\",\n    \"email\": \"pmcmahoncg@rakuten.co.jp\"\n  },\n  {\n    \"id\": 450,\n    \"first_name\": \"Rriocard\",\n    \"last_name\": \"Franke\",\n    \"email\": \"rfrankech@addtoany.com\"\n  },\n  {\n    \"id\": 451,\n    \"first_name\": \"Clayborne\",\n    \"last_name\": \"Greensitt\",\n    \"email\": \"cgreensittci@yellowbook.com\"\n  },\n  {\n    \"id\": 452,\n    \"first_name\": \"Shayla\",\n    \"last_name\": \"Comber\",\n    \"email\": \"scombercj@sphinn.com\"\n  },\n  {\n    \"id\": 453,\n    \"first_name\": \"Ronni\",\n    \"last_name\": \"Errigo\",\n    \"email\": \"rerrigock@tmall.com\"\n  },\n  {\n    \"id\": 454,\n    \"first_name\": \"Bunnie\",\n    \"last_name\": \"Fishly\",\n    \"email\": \"bfishlycl@a8.net\"\n  },\n  {\n    \"id\": 455,\n    \"first_name\": \"Jilly\",\n    \"last_name\": \"Skelly\",\n    \"email\": \"jskellycm@bbc.co.uk\"\n  },\n  {\n    \"id\": 456,\n    \"first_name\": \"Melinde\",\n    \"last_name\": \"Prene\",\n    \"email\": \"mprenecn@smh.com.au\"\n  },\n  {\n    \"id\": 457,\n    \"first_name\": \"Alanah\",\n    \"last_name\": \"De Atta\",\n    \"email\": \"adeattaco@gmpg.org\"\n  },\n  {\n    \"id\": 458,\n    \"first_name\": \"Tamiko\",\n    \"last_name\": \"Gerrish\",\n    \"email\": \"tgerrishcp@baidu.com\"\n  },\n  {\n    \"id\": 459,\n    \"first_name\": \"Winslow\",\n    \"last_name\": \"Waszczyk\",\n    \"email\": \"wwaszczykcq@bbc.co.uk\"\n  },\n  {\n    \"id\": 460,\n    \"first_name\": \"Lydon\",\n    \"last_name\": \"Habershaw\",\n    \"email\": \"lhabershawcr@imgur.com\"\n  },\n  {\n    \"id\": 461,\n    \"first_name\": \"Dill\",\n    \"last_name\": \"Playle\",\n    \"email\": \"dplaylecs@livejournal.com\"\n  },\n  {\n    \"id\": 462,\n    \"first_name\": \"Natassia\",\n    \"last_name\": \"Kendle\",\n    \"email\": \"nkendlect@usa.gov\"\n  },\n  {\n    \"id\": 463,\n    \"first_name\": \"Carree\",\n    \"last_name\": \"Bohills\",\n    \"email\": \"cbohillscu@pinterest.com\"\n  },\n  {\n    \"id\": 464,\n    \"first_name\": \"Terrel\",\n    \"last_name\": \"Knell\",\n    \"email\": \"tknellcv@webnode.com\"\n  },\n  {\n    \"id\": 465,\n    \"first_name\": \"Wilhelmina\",\n    \"last_name\": \"Lumbley\",\n    \"email\": \"wlumbleycw@stanford.edu\"\n  },\n  {\n    \"id\": 466,\n    \"first_name\": \"Dori\",\n    \"last_name\": \"Astridge\",\n    \"email\": \"dastridgecx@salon.com\"\n  },\n  {\n    \"id\": 467,\n    \"first_name\": \"Cherie\",\n    \"last_name\": \"Houlridge\",\n    \"email\": \"choulridgecy@squarespace.com\"\n  },\n  {\n    \"id\": 468,\n    \"first_name\": \"Cord\",\n    \"last_name\": \"Caunt\",\n    \"email\": \"ccauntcz@hatena.ne.jp\"\n  },\n  {\n    \"id\": 469,\n    \"first_name\": \"Josie\",\n    \"last_name\": \"MacMeeking\",\n    \"email\": \"jmacmeekingd0@people.com.cn\"\n  },\n  {\n    \"id\": 470,\n    \"first_name\": \"Glenine\",\n    \"last_name\": \"Feron\",\n    \"email\": \"gferond1@a8.net\"\n  },\n  {\n    \"id\": 471,\n    \"first_name\": \"Eliot\",\n    \"last_name\": \"Doidge\",\n    \"email\": \"edoidged2@intel.com\"\n  },\n  {\n    \"id\": 472,\n    \"first_name\": \"Dudley\",\n    \"last_name\": \"Lehrahan\",\n    \"email\": \"dlehrahand3@technorati.com\"\n  },\n  {\n    \"id\": 473,\n    \"first_name\": \"Tyler\",\n    \"last_name\": \"Puddan\",\n    \"email\": \"tpuddand4@ft.com\"\n  },\n  {\n    \"id\": 474,\n    \"first_name\": \"Egor\",\n    \"last_name\": \"Lindgren\",\n    \"email\": \"elindgrend5@nba.com\"\n  },\n  {\n    \"id\": 475,\n    \"first_name\": \"Barnabe\",\n    \"last_name\": \"Rival\",\n    \"email\": \"brivald6@yahoo.co.jp\"\n  },\n  {\n    \"id\": 476,\n    \"first_name\": \"Domenico\",\n    \"last_name\": \"De Courtney\",\n    \"email\": \"ddecourtneyd7@themeforest.net\"\n  },\n  {\n    \"id\": 477,\n    \"first_name\": \"Matthus\",\n    \"last_name\": \"Bodycote\",\n    \"email\": \"mbodycoted8@usa.gov\"\n  },\n  {\n    \"id\": 478,\n    \"first_name\": \"Jamie\",\n    \"last_name\": \"Blyden\",\n    \"email\": \"jblydend9@comcast.net\"\n  },\n  {\n    \"id\": 479,\n    \"first_name\": \"Mignon\",\n    \"last_name\": \"Woolnough\",\n    \"email\": \"mwoolnoughda@sciencedaily.com\"\n  },\n  {\n    \"id\": 480,\n    \"first_name\": \"Mala\",\n    \"last_name\": \"Devlin\",\n    \"email\": \"mdevlindb@epa.gov\"\n  },\n  {\n    \"id\": 481,\n    \"first_name\": \"Tiertza\",\n    \"last_name\": \"Letterick\",\n    \"email\": \"tletterickdc@state.gov\"\n  },\n  {\n    \"id\": 482,\n    \"first_name\": \"Rebecka\",\n    \"last_name\": \"Alday\",\n    \"email\": \"raldaydd@flavors.me\"\n  },\n  {\n    \"id\": 483,\n    \"first_name\": \"Vinita\",\n    \"last_name\": \"Etter\",\n    \"email\": \"vetterde@wikispaces.com\"\n  },\n  {\n    \"id\": 484,\n    \"first_name\": \"Noreen\",\n    \"last_name\": \"Sirmond\",\n    \"email\": \"nsirmonddf@jigsy.com\"\n  },\n  {\n    \"id\": 485,\n    \"first_name\": \"Ashley\",\n    \"last_name\": \"McClaughlin\",\n    \"email\": \"amcclaughlindg@bloomberg.com\"\n  },\n  {\n    \"id\": 486,\n    \"first_name\": \"Vale\",\n    \"last_name\": \"Le Houx\",\n    \"email\": \"vlehouxdh@nhs.uk\"\n  },\n  {\n    \"id\": 487,\n    \"first_name\": \"Donnell\",\n    \"last_name\": \"Treadway\",\n    \"email\": \"dtreadwaydi@mit.edu\"\n  },\n  {\n    \"id\": 488,\n    \"first_name\": \"Gwennie\",\n    \"last_name\": \"Gundrey\",\n    \"email\": \"ggundreydj@over-blog.com\"\n  },\n  {\n    \"id\": 489,\n    \"first_name\": \"Karel\",\n    \"last_name\": \"Dani\",\n    \"email\": \"kdanidk@163.com\"\n  },\n  {\n    \"id\": 490,\n    \"first_name\": \"Merle\",\n    \"last_name\": \"Bonnaire\",\n    \"email\": \"mbonnairedl@uol.com.br\"\n  },\n  {\n    \"id\": 491,\n    \"first_name\": \"Annabel\",\n    \"last_name\": \"Nockles\",\n    \"email\": \"anocklesdm@walmart.com\"\n  },\n  {\n    \"id\": 492,\n    \"first_name\": \"Urban\",\n    \"last_name\": \"Ivashov\",\n    \"email\": \"uivashovdn@amazon.co.uk\"\n  },\n  {\n    \"id\": 493,\n    \"first_name\": \"Hector\",\n    \"last_name\": \"Rothwell\",\n    \"email\": \"hrothwelldo@howstuffworks.com\"\n  },\n  {\n    \"id\": 494,\n    \"first_name\": \"Ford\",\n    \"last_name\": \"Brozek\",\n    \"email\": \"fbrozekdp@hibu.com\"\n  },\n  {\n    \"id\": 495,\n    \"first_name\": \"Rhody\",\n    \"last_name\": \"Phythean\",\n    \"email\": \"rphytheandq@bbc.co.uk\"\n  },\n  {\n    \"id\": 496,\n    \"first_name\": \"Angie\",\n    \"last_name\": \"Durno\",\n    \"email\": \"adurnodr@china.com.cn\"\n  },\n  {\n    \"id\": 497,\n    \"first_name\": \"Elisha\",\n    \"last_name\": \"Jerrome\",\n    \"email\": \"ejerromeds@163.com\"\n  },\n  {\n    \"id\": 498,\n    \"first_name\": \"Davita\",\n    \"last_name\": \"Dakers\",\n    \"email\": \"ddakersdt@bravesites.com\"\n  },\n  {\n    \"id\": 499,\n    \"first_name\": \"Kiersten\",\n    \"last_name\": \"Josey\",\n    \"email\": \"kjoseydu@networksolutions.com\"\n  }\n]\n"
  },
  {
    "path": "examples/islands_router/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/islands_router/src/app.rs",
    "content": "use leptos::{\n    either::{Either, EitherOf3},\n    prelude::*,\n};\nuse leptos_router::{\n    components::{Route, Router, Routes},\n    hooks::{use_params_map, use_query_map},\n    path,\n};\nuse serde::{Deserialize, Serialize};\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options=options islands=true islands_router=true/>\n                <link rel=\"stylesheet\" id=\"leptos\" href=\"/pkg/islands.css\"/>\n                <link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\"/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    view! {\n        <Router>\n            <header>\n                <h1>\"My Contacts\"</h1>\n            </header>\n            <nav>\n                <a href=\"/\">\"Home\"</a>\n                <a href=\"/about\">\"About\"</a>\n            </nav>\n            <main>\n                <Routes fallback=|| \"Not found.\">\n                    <Route path=path!(\"\") view=Home/>\n                    <Route path=path!(\"user/:id\") view=Details/>\n                    <Route path=path!(\"about\") view=About/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n#[server]\npub async fn search(query: String) -> Result<Vec<User>, ServerFnError> {\n    let users = tokio::fs::read_to_string(\"./mock_data.json\").await?;\n    let data: Vec<User> = serde_json::from_str(&users)?;\n    let query = query.to_ascii_lowercase();\n    Ok(data\n        .into_iter()\n        .filter(|user| {\n            user.first_name.to_ascii_lowercase().contains(&query)\n                || user.last_name.to_ascii_lowercase().contains(&query)\n                || user.email.to_ascii_lowercase().contains(&query)\n        })\n        .collect())\n}\n\n#[server]\npub async fn delete_user(id: u32) -> Result<(), ServerFnError> {\n    let users = tokio::fs::read_to_string(\"./mock_data.json\").await?;\n    let mut data: Vec<User> = serde_json::from_str(&users)?;\n    data.retain(|user| user.id != id);\n    let new_json = serde_json::to_string(&data)?;\n    tokio::fs::write(\"./mock_data.json\", &new_json).await?;\n    Ok(())\n}\n\n#[derive(Deserialize, Serialize, Debug, Clone)]\npub struct User {\n    id: u32,\n    first_name: String,\n    last_name: String,\n    email: String,\n}\n\n#[component]\npub fn Home() -> impl IntoView {\n    let q = use_query_map();\n    let q = move || q.read().get(\"q\");\n    let data = Resource::new(q, |q| async move {\n        if let Some(q) = q {\n            search(q).await\n        } else {\n            Ok(vec![])\n        }\n    });\n    let delete_user_action = ServerAction::<DeleteUser>::new();\n\n    let view = move || {\n        Suspend::new(async move {\n            let users = data.await.unwrap();\n            if q().is_none() {\n                EitherOf3::A(view! {\n                    <p class=\"note\">\"Enter a search to begin viewing contacts.\"</p>\n                })\n            } else if users.is_empty() {\n                EitherOf3::B(view! {\n                    <p class=\"note\">\"No users found matching that search.\"</p>\n                })\n            } else {\n                EitherOf3::C(view! {\n                    <table>\n                        <tbody>\n                            <For\n                                each=move || users.clone()\n                                key=|user| user.id\n                                let:user\n                            >\n                                <tr>\n                                    <td>{user.first_name}</td>\n                                    <td>{user.last_name}</td>\n                                    <td>{user.email}</td>\n                                    <td>\n                                        <a href=format!(\"/user/{}\", user.id)>\"Details\"</a>\n                                        <input type=\"checkbox\"/>\n                                        <ActionForm action=delete_user_action>\n                                            <input type=\"hidden\" name=\"id\" value=user.id/>\n                                            <input type=\"submit\" value=\"Delete\"/>\n                                        </ActionForm>\n                                    </td>\n                                </tr>\n                            </For>\n                        </tbody>\n                    </table>\n                })\n            }\n        })\n    };\n    view! {\n        <section class=\"page\">\n            <form method=\"GET\" class=\"search\">\n                <input type=\"search\" name=\"q\" value=q autofocus oninput=\"this.form.requestSubmit()\"/>\n                <input type=\"submit\"/>\n            </form>\n            <Suspense fallback=|| view! { <p>\"Loading...\"</p> }>{view}</Suspense>\n        </section>\n    }\n}\n\n#[component]\npub fn Details() -> impl IntoView {\n    #[server]\n    pub async fn get_user(id: u32) -> Result<Option<User>, ServerFnError> {\n        let users = tokio::fs::read_to_string(\"./mock_data.json\").await?;\n        let data: Vec<User> = serde_json::from_str(&users)?;\n        Ok(data.iter().find(|user| user.id == id).cloned())\n    }\n    let params = use_params_map();\n    let id = move || {\n        params\n            .read()\n            .get(\"id\")\n            .and_then(|id| id.parse::<u32>().ok())\n    };\n    let user = Resource::new(id, |id| async move {\n        match id {\n            None => Ok(None),\n            Some(id) => get_user(id).await,\n        }\n    });\n\n    move || {\n        Suspend::new(async move {\n            user.await.map(|user| match user {\n                None => Either::Left(view! {\n                    <section class=\"page\">\n                        <h2>\"Not found.\"</h2>\n                        <p>\"Sorry — we couldn’t find that user.\"</p>\n                    </section>\n                }),\n                Some(user) => Either::Right(view! {\n                    <section class=\"page\">\n                        <h2>{user.first_name} \" \" { user.last_name}</h2>\n                        <p class=\"email\">{user.email}</p>\n                    </section>\n                }),\n            })\n        })\n    }\n}\n\n#[component]\npub fn About() -> impl IntoView {\n    view! {\n        <section class=\"page\">\n            <h2>\"About\"</h2>\n            <p>\"This demo is intended to show off an experimental “islands router” feature, which mimics the smooth transitions and user experience of client-side routing while minimizing the amount of code that actually runs in the browser.\"</p>\n            <p>\"By default, all the content in this application is only rendered on the server. But you can add client-side interactivity via islands like this one:\"</p>\n            <Counter/>\n        </section>\n    }\n}\n\n#[island]\npub fn Counter() -> impl IntoView {\n    let count = RwSignal::new(0);\n    view! {\n        <button class=\"counter\" on:click=move |_| *count.write() += 1>{count}</button>\n    }\n}\n"
  },
  {
    "path": "examples/islands_router/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_islands();\n}\n"
  },
  {
    "path": "examples/islands_router/src/main.rs",
    "content": "use axum::Router;\nuse islands::app::{shell, App};\nuse leptos::prelude::*;\nuse leptos_axum::{generate_route_list, LeptosRoutes};\n\n#[tokio::main]\nasync fn main() {\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    println!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n"
  },
  {
    "path": "examples/islands_router/style.css",
    "content": "body {\n\tfont-family: system-ui, sans-serif;\n\tbackground-color: #f6f6fa;\n}\n\nh1, h2, h3, h4, h5, h6 {\n\tfont-family: ui-rounded, 'Hiragino Maru Gothic ProN', Quicksand, Comfortaa, Manjari, 'Arial Rounded MT', 'Arial Rounded MT Bold', Calibri, source-sans-pro, sans-serif;\n\ttext-align: center;\n}\n\nnav {\n\tpadding: 1rem;\n\ttext-align: center;\n}\n\nnav a {\n\tmargin: 1rem;\n}\n\nform.search {\n\tdisplay: flex;\n\tmargin: 2rem auto;\n\tjustify-content: center;\n}\n\ntd {\n\tmin-width: 10rem;\n\twidth: 10rem;\n}\n\ntable {\n\tmin-width: 100%;\n}\n\n.page {\n\twidth: 80%;\n\tmargin: auto;\n}\n\ntd:last-child > * {\n\tdisplay: inline-block;\n}\n\n.note, .note {\n\ttext-align: center;\n}\n\nbutton.counter {\n\tdisplay: block;\n\tfont-size: 2rem;\n\tmargin: auto;\n}\n"
  },
  {
    "path": "examples/js-framework-benchmark/Cargo.toml",
    "content": "[package]\nname = \"js-framework-benchmark-leptos\"\nversion = \"1.0.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] } # for actual benchmarking, add `nightly` and `delegation` features\n# used in rand, but we need to enable js feature\ngetrandom = { version = \"0.2.15\", features = [\"js\"] }\nrand = { version = \"0.8.5\", features = [\"small_rng\"] }\nconsole_error_panic_hook = \"0.1.7\"\n\n[dev-dependencies]\nwasm-bindgen = \"0.2\"\nwasm-bindgen-test = \"0.3.42\"\nweb-sys = \"0.3\"\n"
  },
  {
    "path": "examples/js-framework-benchmark/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/wasm-test.toml\" },\n  { path = \"../cargo-make/trunk_server.toml\" },\n]\n\n[tasks.clippy-each-feature]\ndependencies = [\"cargo-all-features\"]\ncommand = \"cargo\"\nargs = [\n  \"all-features\",\n  \"clippy\",\n  \"--target\",\n  \"wasm32-unknown-unknown\",\n  \"--no-deps\",\n  \"--\",\n  \"-D\",\n  \"warnings\",\n]\n"
  },
  {
    "path": "examples/js-framework-benchmark/README.md",
    "content": "# Leptos benchmark example\n\nThis example is adoptation of code from [js-framework-benchmark](https://github.com/krausest/js-framework-benchmark/tree/master/frameworks/keyed/leptos).\nThis example creates a large table with randomized entries, it also shows usage of `template` macro and `For` component.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/js-framework-benchmark/index.html",
    "content": "<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\">\n    <title>Leptos</title>\n    <base href=\"bundled-dist/\"></base>\n    <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css\" integrity=\"sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65\" crossorigin=\"anonymous\">\n\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n  <style>.danger { background: gold }</style>\n  </head>\n  <body>\n    <span class=\"preloadicon glyphicon glyphicon-remove\" aria-hidden=\"true\"></span>\n    <div id='main'></div>\n  </body>\n</html>"
  },
  {
    "path": "examples/js-framework-benchmark/src/lib.rs",
    "content": "use leptos::prelude::*;\nuse rand::prelude::*;\nuse std::sync::atomic::{AtomicUsize, Ordering};\nstatic ADJECTIVES: &[&str] = &[\n    \"pretty\",\n    \"large\",\n    \"big\",\n    \"small\",\n    \"tall\",\n    \"short\",\n    \"long\",\n    \"handsome\",\n    \"plain\",\n    \"quaint\",\n    \"clean\",\n    \"elegant\",\n    \"easy\",\n    \"angry\",\n    \"crazy\",\n    \"helpful\",\n    \"mushy\",\n    \"odd\",\n    \"unsightly\",\n    \"adorable\",\n    \"important\",\n    \"inexpensive\",\n    \"cheap\",\n    \"expensive\",\n    \"fancy\",\n];\n\nstatic COLOURS: &[&str] = &[\n    \"red\", \"yellow\", \"blue\", \"green\", \"pink\", \"brown\", \"purple\", \"brown\",\n    \"white\", \"black\", \"orange\",\n];\n\nstatic NOUNS: &[&str] = &[\n    \"table\", \"chair\", \"house\", \"bbq\", \"desk\", \"car\", \"pony\", \"cookie\",\n    \"sandwich\", \"burger\", \"pizza\", \"mouse\", \"keyboard\",\n];\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\nstruct RowData {\n    id: usize,\n    label: ArcRwSignal<String>,\n}\n\nstatic ID_COUNTER: AtomicUsize = AtomicUsize::new(1);\n\nfn build_data(count: usize) -> Vec<RowData> {\n    let mut thread_rng = thread_rng();\n\n    let mut data = Vec::new();\n    data.reserve_exact(count);\n\n    for _i in 0..count {\n        let adjective = ADJECTIVES.choose(&mut thread_rng).unwrap();\n        let colour = COLOURS.choose(&mut thread_rng).unwrap();\n        let noun = NOUNS.choose(&mut thread_rng).unwrap();\n        let capacity = adjective.len() + colour.len() + noun.len() + 2;\n        let mut label = String::with_capacity(capacity);\n        label.push_str(adjective);\n        label.push(' ');\n        label.push_str(colour);\n        label.push(' ');\n        label.push_str(noun);\n\n        data.push(RowData {\n            id: ID_COUNTER.load(Ordering::Relaxed),\n            label: ArcRwSignal::new(label),\n        });\n\n        ID_COUNTER\n            .store(ID_COUNTER.load(Ordering::Relaxed) + 1, Ordering::Relaxed);\n    }\n\n    data\n}\n\n/// Button component.\n#[component]\nfn Button(\n    /// ID for the button element\n    id: &'static str,\n    /// Text that should be included\n    text: &'static str,\n) -> impl IntoView {\n    view! {\n        <div class=\"col-sm-6 smallpad\">\n            <button id=id class=\"btn btn-primary btn-block\" type=\"button\">\n                {text}\n            </button>\n        </div>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    let (data, set_data) = signal(Vec::<RowData>::new());\n    let (selected, set_selected) = signal(None::<usize>);\n\n    let remove = move |id: usize| {\n        set_data.update(move |data| data.retain(|row| row.id != id));\n    };\n\n    let run = move |_| {\n        set_data.set(build_data(1000));\n        set_selected.set(None);\n    };\n\n    let run_lots = move |_| {\n        set_data.set(build_data(10000));\n        set_selected.set(None);\n    };\n\n    let add = move |_| {\n        set_data.update(move |data| data.append(&mut build_data(1000)));\n    };\n\n    let update = move |_| {\n        data.with(|data| {\n            for row in data.iter().step_by(10) {\n                row.label.update(|n| n.push_str(\" !!!\"));\n            }\n        });\n    };\n\n    let clear = move |_| {\n        set_data.set(Vec::new());\n        set_selected.set(None);\n    };\n\n    let swap_rows = move |_| {\n        set_data.update(|data| {\n            if data.len() > 998 {\n                data.swap(1, 998);\n            }\n        });\n    };\n\n    let is_selected = Selector::new(move || selected.get());\n\n    view! {\n        <div class=\"container\">\n            <div class=\"jumbotron\">\n                <div class=\"row\">\n                    <div class=\"col-md-6\">\n                        <h1>\"Leptos\"</h1>\n                    </div>\n                    <div class=\"col-md-6\">\n                        <div class=\"row\">\n                            <Button id=\"run\" text=\"Create 1,000 rows\" on:click=run />\n                            <Button id=\"runlots\" text=\"Create 10,000 rows\" on:click=run_lots />\n                            <Button id=\"add\" text=\"Append 1,000 rows\" on:click=add />\n                            <Button id=\"update\" text=\"Update every 10th row\" on:click=update />\n                            <Button id=\"clear\" text=\"Clear\" on:click=clear />\n                            <Button id=\"swaprows\" text=\"Swap Rows\" on:click=swap_rows />\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <table class=\"table table-hover table-striped test-data\">\n                <tbody>\n                    <For\n                        each=move || data.get()\n                        key=|row| row.id\n                        children=move |row: RowData| {\n                            let row_id = row.id;\n                            let label = row.label;\n                            let is_selected = is_selected.clone();\n                            template! {\n                                < tr class : danger = { move || is_selected.selected(&Some(row_id)) }\n                                > < td class = \"col-md-1\" > { row_id.to_string() } </ td > < td\n                                class = \"col-md-4\" >< a on : click = move | _ | set_selected\n                                .set(Some(row_id)) > { move || label.get() } </ a ></ td > < td\n                                class = \"col-md-1\" >< a on : click = move | _ | remove(row_id) ><\n                                span class = \"glyphicon glyphicon-remove\" aria - hidden = \"true\" ></\n                                span ></ a ></ td > < td class = \"col-md-6\" /> </ tr >\n                            }\n                        }\n                    />\n\n                </tbody>\n            </table>\n            <span class=\"preloadicon glyphicon glyphicon-remove\" aria-hidden=\"true\"></span>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/js-framework-benchmark/src/main.rs",
    "content": "use js_framework_benchmark_leptos::App;\nuse leptos::{\n    leptos_dom::helpers::document, mount::mount_to, wasm_bindgen::JsCast,\n};\n\npub fn main() {\n    console_error_panic_hook::set_once();\n    let root = document().query_selector(\"#main\").unwrap().unwrap();\n    let handle = mount_to(root.unchecked_into(), App);\n    handle.forget();\n}\n"
  },
  {
    "path": "examples/js-framework-benchmark/tests/web.rs",
    "content": "use js_framework_benchmark_leptos::*;\nuse leptos::{prelude::*, task::tick};\nuse wasm_bindgen::JsCast;\nuse wasm_bindgen_test::*;\n\nwasm_bindgen_test_configure!(run_in_browser);\n\n#[wasm_bindgen_test]\nasync fn add_item() {\n    let document = document();\n    let test_wrapper = document.create_element(\"section\").unwrap();\n    let _ = document.body().unwrap().append_child(&test_wrapper);\n\n    // start by rendering our counter and mounting it to the DOM\n    let _handle = mount_to(test_wrapper.clone().unchecked_into(), App);\n\n    let table = test_wrapper\n        .query_selector(\"table\")\n        .unwrap()\n        .unwrap()\n        .unchecked_into::<web_sys::HtmlTableElement>();\n\n    let create = test_wrapper\n        .query_selector(\"button#runlots\")\n        .unwrap()\n        .unwrap()\n        .unchecked_into::<web_sys::HtmlButtonElement>();\n\n    let add = test_wrapper\n        .query_selector(\"button#add\")\n        .unwrap()\n        .unwrap()\n        .unchecked_into::<web_sys::HtmlButtonElement>();\n\n    let clear = test_wrapper\n        .query_selector(\"button#clear\")\n        .unwrap()\n        .unwrap()\n        .unchecked_into::<web_sys::HtmlButtonElement>();\n    _handle.forget();\n\n    // now let's click the `clear` button\n    clear.click();\n    tick().await;\n\n    // now check that table is empty\n    assert_eq!(table.rows().length(), 0);\n\n    create.click();\n    tick().await;\n\n    assert_eq!(table.rows().length(), 10000);\n    add.click();\n    tick().await;\n\n    assert_eq!(table.rows().length(), 11000);\n\n    clear.click();\n    tick().await;\n\n    assert_eq!(table.rows().length(), 0)\n}\n"
  },
  {
    "path": "examples/lazy_routes/Cargo.toml",
    "content": "[package]\nname = \"lazy_routes\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.8.1\", optional = true }\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\nleptos = { path = \"../../leptos\", features = [\"tracing\"] }\nleptos_meta = { path = \"../../meta\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../router\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\nthiserror = \"1.0\"\ntokio = { version = \"1.39\", features = [\n  \"rt-multi-thread\",\n  \"macros\",\n  \"time\",\n], optional = true }\nwasm-bindgen = \"0.2.92\"\nfutures = \"0.3.31\"\nserde_json = \"1.0.140\"\ngloo-timers = { version = \"0.3\", features = [\"futures\"] }\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"dep:leptos_axum\",\n  \"leptos_router/ssr\",\n]\n\n[profile.release]\npanic = \"abort\"\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"]]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"regression\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"cargo make test-ui\"\nend2end-dir = \"e2e\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/lazy_routes/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 Leptos\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/lazy_routes/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos-split-webdriver-test.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"regression\"\n"
  },
  {
    "path": "examples/lazy_routes/README.md",
    "content": "# Regression Tests\n\nThis example functions as a catch-all for all current and future regression\ntest cases that typically happens at integration.\n\n## Quick Start\n\nRun `cargo leptos watch --split` to run this example.\n"
  },
  {
    "path": "examples/lazy_routes/e2e/Cargo.toml",
    "content": "[package]\nname = \"lazy_routes_e2e\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\nanyhow = \"1.0\"\nasync-trait = \"0.1.81\"\ncucumber = \"0.21.1\"\nfantoccini = \"0.21.1\"\npretty_assertions = \"1.4\"\nserde_json = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\", \"time\"] }\nurl = \"2.5\"\n\n[[test]]\nname = \"app_suite\"\nharness = false    # Allow Cucumber to print output instead of libtest\n"
  },
  {
    "path": "examples/lazy_routes/e2e/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.test]\nenv = { RUN_AUTOMATICALLY = false }\ncondition = { env_true = [\"RUN_AUTOMATICALLY\"] }\n\n[tasks.ci]\n\n[tasks.test-ui]\ncommand = \"cargo\"\nargs = [\n  \"test\",\n  \"--test\",\n  \"app_suite\",\n  \"--\",\n  \"--retry\",\n  \"2\",\n  \"--retry-after\",\n  \"5sec\",\n  \"--fail-fast\",\n  \"${@}\",\n]\n"
  },
  {
    "path": "examples/lazy_routes/e2e/README.md",
    "content": "# Lazy Routes\n\nThis example demonstrates how to split the WASM bundle that is sent to the client into multiple binaries, which can be lazy-loaded, either independently or in a way that's integrated into the router.\n\nWithout code splitting, the entire application is compiled to a monolithic WASM binary, the size of which grows in proportion to the complexity of the application. This means that the time to interactive (TTI) for any page is proportional to the size of the entire application, not only that page.\n\nCode splitting allows you to lazy-load some functions, by splitting off the WASM binary code for certain functions into separate files, which can be downloaded as needed. This minimizes initial TTI for any page, and then amortizes the cost of loading the binary over the lifetime of the application session.\n\nIn many cases, this can be done with minimal or no cost.\n\nLazy loading can be used in two ways, each of which is shown in the example.\n\n## `#[lazy]` macro\n\n`#[lazy]` is an attribute macro that can be used to annotate an `async fn` in order to split its code out into a separate file that will be loaded on demand, when compiled with `cargo leptos --split`.\n\nThis has some limitations (for example, it must return concrete types) but can be used for most functions.\n\n## `LazyRoute`\n\n`LazyRoute` is a specialized application of `#[lazy]` that allows you to define an entire route/page of your application as being lazy-loaded.\n\nCreating a lazy route requires you to split the route into two parts:\n\n1. `data()`: A synchronous method that should be used to start loading any async data used by the page, for example by creating a `Resource`\n2. `view()`: An async (because lazy-loaded) method that renders the view.\n\nThe purpose of splitting these into two parts is to avoid a “waterfall,” in which the browser first waits for a lazy-loaded WASM chunk that defines the page, _then_ makes a second request to the server to load the relevant data. Instead, a `LazyRoute` will begin loading resources created in the `data` method while lazy-loading the component body in the `view`, then render the route.\n\nThis means that in many cases, the data loading “hides” the cost of the lazy-loading; i.e., the page needs to wait for the data to load, so the fact that it is waiting concurrently for the lazy-loaded view means that the lazy loading does not cost anything additional in terms of page load time.\n"
  },
  {
    "path": "examples/lazy_routes/e2e/features/basic.feature",
    "content": "@basic\nFeature: Check that each page hydrates correctly\n\n\tScenario: Page A is rendered correctly.\n\t\tGiven I see the app\n\t\tThen I see the page is View A\n\n\tScenario: Page A hydrates and allows navigating to page B.\n\t\tGiven I see the app\n\t\tWhen I select the link B\n\t\tThen I see the navigating indicator\n\t\tWhen I wait for a second\n\t\tThen I see the page is View B\n\n\tScenario: Page B is rendered correctly.\n\t\tWhen I open the app at /b\n\t\tThen I see the page is View B\n\n\tScenario: Page B hydrates and allows navigating to page C.\n\t\tWhen I open the app at /b\n\t\tWhen I select the link C\n\t\tThen I see the navigating indicator\n\t\tWhen I wait for a second\n\t\tThen I see the page is View C\n\n\tScenario: Page C is rendered correctly.\n\t\tWhen I open the app at /c\n\t\tThen I see the page is View C\n\n\tScenario: Page C hydrates and allows navigating to page A.\n\t\tWhen I open the app at /c\n\t\tWhen I select the link A\n\t\tThen I see the page is View A"
  },
  {
    "path": "examples/lazy_routes/e2e/features/duplicate_name.feature",
    "content": "@duplicate_names\nFeature: Lazy functions can share the same name\n\n\tScenario: Two functions with the same name both work.\n\t\tGiven I see the app\n\t\tThen I see the page is View A\n\t\tWhen I click the button First\n\t\tWhen I wait for a second\n\t\tThen I see the result is {\"a\":\"First Value\",\"b\":1}\n\t\tWhen I click the button Second\n\t\tWhen I wait for a second\n\t\tThen I see the result is {\"a\":\"Second Value\",\"b\":2}\n\t\tWhen I click the button Third\n\t\tWhen I wait for a second\n\t\tThen I see the result is Third value."
  },
  {
    "path": "examples/lazy_routes/e2e/features/shared_chunks.feature",
    "content": "@shared_chunks\nFeature: Shared code splitting works correctly\n\n\tScenario: Two functions using same serde code both work.\n\t\tGiven I see the app\n\t\tThen I see the page is View A\n\t\tWhen I click the button First\n\t\tWhen I wait for a second\n\t\tThen I see the result is {\"a\":\"First Value\",\"b\":1}"
  },
  {
    "path": "examples/lazy_routes/e2e/tests/app_suite.rs",
    "content": "mod fixtures;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fixtures::world::AppWorld;\nuse std::{ffi::OsStr, fs::read_dir};\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    // Normally the below is done, but it's now gotten to the point of\n    // having a sufficient number of tests where the resource contention\n    // of the concurrently running browsers will cause failures on CI.\n    // AppWorld::cucumber()\n    //     .fail_on_skipped()\n    //     .run_and_exit(\"./features\")\n    //     .await;\n\n    // Mitigate the issue by manually stepping through each feature,\n    // rather than letting cucumber glob them and dispatch all at once.\n    for entry in read_dir(\"./features\")? {\n        let path = entry?.path();\n        if path.extension() == Some(OsStr::new(\"feature\")) {\n            AppWorld::cucumber()\n                .fail_on_skipped()\n                .run_and_exit(path)\n                .await;\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "examples/lazy_routes/e2e/tests/fixtures/action.rs",
    "content": "use super::{find, world::HOST};\nuse anyhow::Result;\nuse fantoccini::Client;\nuse std::result::Result::Ok;\n\npub async fn goto_path(client: &Client, path: &str) -> Result<()> {\n    let url = format!(\"{}{}\", HOST, path);\n    client.goto(&url).await?;\n\n    Ok(())\n}\n\npub async fn click_link(client: &Client, text: &str) -> Result<()> {\n    let link = find::link_with_text(&client, &text).await?;\n    link.click().await?;\n    Ok(())\n}\n\npub async fn click_button(client: &Client, id: &str) -> Result<()> {\n    let btn = find::element_by_id(&client, &id).await?;\n    btn.click().await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/lazy_routes/e2e/tests/fixtures/check.rs",
    "content": "use crate::fixtures::find;\nuse anyhow::{Ok, Result};\nuse fantoccini::Client;\nuse pretty_assertions::assert_eq;\n\npub async fn page_name_is(client: &Client, expected_text: &str) -> Result<()> {\n    let actual = find::text_at_id(client, \"page\").await?;\n    assert_eq!(&actual, expected_text);\n    Ok(())\n}\n\npub async fn result_is(client: &Client, expected_text: &str) -> Result<()> {\n    let actual = find::text_at_id(client, \"result\").await?;\n    assert_eq!(&actual, expected_text);\n    Ok(())\n}\n\npub async fn navigating_appears(client: &Client) -> Result<()> {\n    let actual = find::text_at_id(client, \"navigating\").await?;\n    assert_eq!(&actual, \"Navigating...\");\n    Ok(())\n}\n\npub async fn element_exists(client: &Client, id: &str) -> Result<()> {\n    find::element_by_id(client, id)\n        .await\n        .expect(&format!(\"could not find element with id `{id}`\"));\n    Ok(())\n}\n"
  },
  {
    "path": "examples/lazy_routes/e2e/tests/fixtures/find.rs",
    "content": "use anyhow::{Ok, Result};\nuse fantoccini::{elements::Element, Client, Locator};\n\npub async fn text_at_id(client: &Client, id: &str) -> Result<String> {\n    let element = element_by_id(client, id)\n        .await\n        .expect(format!(\"no such element with id `{}`\", id).as_str());\n    let text = element.text().await?;\n    Ok(text)\n}\n\npub async fn link_with_text(client: &Client, text: &str) -> Result<Element> {\n    let link = client\n        .wait()\n        .for_element(Locator::LinkText(text))\n        .await\n        .expect(format!(\"Link not found by `{}`\", text).as_str());\n    Ok(link)\n}\n\npub async fn element_by_id(client: &Client, id: &str) -> Result<Element> {\n    Ok(client.wait().for_element(Locator::Id(id)).await?)\n}\n"
  },
  {
    "path": "examples/lazy_routes/e2e/tests/fixtures/mod.rs",
    "content": "pub mod action;\npub mod check;\npub mod find;\npub mod world;\n"
  },
  {
    "path": "examples/lazy_routes/e2e/tests/fixtures/world/action_steps.rs",
    "content": "use crate::fixtures::{action, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::{gherkin::Step, given, when};\n\n#[given(\"I see the app\")]\n#[when(\"I open the app\")]\nasync fn i_open_the_app(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::goto_path(client, \"\").await?;\n\n    Ok(())\n}\n\n#[when(regex = \"^I open the app at (.*)$\")]\nasync fn i_open_the_app_at(world: &mut AppWorld, url: String) -> Result<()> {\n    let client = &world.client;\n    action::goto_path(client, &url).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"^I select the link (.*)$\")]\nasync fn i_select_the_link(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::click_link(client, &text).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"^I click the button (.*)$\")]\nasync fn i_click_the_button(world: &mut AppWorld, id: String) -> Result<()> {\n    let client = &world.client;\n    action::click_button(client, &id).await?;\n\n    Ok(())\n}\n\n#[when(expr = \"I select the following links\")]\nasync fn i_select_the_following_links(\n    world: &mut AppWorld,\n    step: &Step,\n) -> Result<()> {\n    let client = &world.client;\n\n    if let Some(table) = step.table.as_ref() {\n        for row in table.rows.iter() {\n            action::click_link(client, &row[0]).await?;\n        }\n    }\n\n    Ok(())\n}\n\n#[when(\"I wait for a second\")]\nasync fn i_wait_for_a_second(world: &mut AppWorld) -> Result<()> {\n    tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n    Ok(())\n}\n\n#[given(regex = \"^I (refresh|reload) the (browser|page)$\")]\n#[when(regex = \"^I (refresh|reload) the (browser|page)$\")]\nasync fn i_refresh_the_browser(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    client.refresh().await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/lazy_routes/e2e/tests/fixtures/world/check_steps.rs",
    "content": "use crate::fixtures::{check, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::then;\n\n#[then(regex = r\"^I see the navigating indicator\")]\nasync fn i_see_the_nav(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    check::navigating_appears(client).await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see the page is (.*)$\")]\nasync fn i_see_the_page_is(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    check::page_name_is(client, &text).await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see the result is (.*)$\")]\nasync fn i_see_the_result_is(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    check::result_is(client, &text).await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see the navbar$\")]\nasync fn i_see_the_navbar(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    check::element_exists(client, \"nav\").await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/lazy_routes/e2e/tests/fixtures/world/mod.rs",
    "content": "pub mod action_steps;\npub mod check_steps;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fantoccini::{\n    error::NewSessionError, wd::Capabilities, Client, ClientBuilder,\n};\n\npub const HOST: &str = \"http://127.0.0.1:3000\";\n\n#[derive(Debug, World)]\n#[world(init = Self::new)]\npub struct AppWorld {\n    pub client: Client,\n}\n\nimpl AppWorld {\n    async fn new() -> Result<Self, anyhow::Error> {\n        let webdriver_client = build_client().await?;\n\n        Ok(Self {\n            client: webdriver_client,\n        })\n    }\n}\n\nasync fn build_client() -> Result<Client, NewSessionError> {\n    let mut cap = Capabilities::new();\n    let arg = serde_json::from_str(\"{\\\"args\\\": [\\\"-headless\\\"]}\").unwrap();\n    cap.insert(\"goog:chromeOptions\".to_string(), arg);\n\n    let client = ClientBuilder::native()\n        .capabilities(cap)\n        .connect(\"http://localhost:4444\")\n        .await?;\n\n    Ok(client)\n}\n"
  },
  {
    "path": "examples/lazy_routes/src/app.rs",
    "content": "use leptos::{prelude::*, task::spawn_local};\nuse leptos_router::{\n    components::{Outlet, ParentRoute, Route, Router, Routes},\n    lazy_route, Lazy, LazyRoute, StaticSegment,\n};\nuse serde::{Deserialize, Serialize};\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    let count = RwSignal::new(0);\n    provide_context(count);\n    let (is_routing, set_is_routing) = signal(false);\n\n    view! {\n        <nav id=\"nav\" style=\"width: 100%\">\n            <a href=\"/\">\"A\"</a> \" | \"\n            <a href=\"/b\">\"B\"</a> \" | \"\n            <a href=\"/c\">\"C\"</a> \" | \"\n            <a href=\"/d\">\"D\"</a>\n            <span style=\"float: right\" id=\"navigating\">\n                {move || is_routing.get().then_some(\"Navigating...\")}\n            </span>\n        </nav>\n        <Router set_is_routing>\n            <Routes fallback=|| \"Not found.\">\n                <Route path=StaticSegment(\"\") view=ViewA/>\n                <Route path=StaticSegment(\"b\") view=ViewB/>\n                <Route path=StaticSegment(\"c\") view={Lazy::<ViewC>::new()}/>\n                // you can nest lazy routes, and there data and views will all load concurrently\n                <ParentRoute path=StaticSegment(\"d\") view={Lazy::<ViewD>::new()}>\n                    <Route path=StaticSegment(\"\") view={Lazy::<ViewE>::new()}/>\n                </ParentRoute>\n            </Routes>\n        </Router>\n    }\n}\n\n// View A: A plain old synchronous route, just like they all currently work. The WASM binary code\n// for this is shipped as part of the main bundle.  Any data-loading code (like resources that run\n// in the body of the component) will be shipped as part of the main bundle.\n\n#[component]\npub fn ViewA() -> impl IntoView {\n    leptos::logging::log!(\"View A\");\n    let result = RwSignal::new(\"Click a button to see the result\".to_string());\n\n    view! {\n        <p id=\"page\">\"View A\"</p>\n        <pre id=\"result\">{result}</pre>\n        <button id=\"First\" on:click=move |_| spawn_local(async move { result.set(first_value().await); })>\"First\"</button>\n        <button id=\"Second\" on:click=move |_| spawn_local(async move { result.set(second_value().await); })>\"Second\"</button>\n        // test to make sure duplicate names in different scopes can be used\n        <button id=\"Third\" on:click=move |_| {\n            #[lazy]\n            pub fn second_value() -> String {\n                \"Third value.\".to_string()\n            }\n\n            spawn_local(async move {\n                result.set(second_value().await);\n            });\n        }>\"Third\"</button>\n    }\n}\n\n// View B: lazy-loaded route with lazy-loaded data\n#[derive(Debug, Clone, Deserialize)]\npub struct Comment {\n    #[serde(rename = \"postId\")]\n    post_id: usize,\n    id: usize,\n    name: String,\n    email: String,\n    body: String,\n}\n\n#[lazy]\nfn deserialize_comments(data: &str) -> Vec<Comment> {\n    serde_json::from_str(data).unwrap()\n}\n\n#[component]\npub fn ViewB() -> impl IntoView {\n    let data = LocalResource::new(|| async move {\n        let preload = deserialize_comments(\"[]\");\n        let (_, data) = futures::future::join(preload, async {\n            gloo_timers::future::TimeoutFuture::new(500).await;\n\n            r#\"\n                [\n                    {\n                        \"postId\": 1,\n                        \"id\": 1,\n                        \"name\": \"id labore ex et quam laborum\",\n                        \"email\": \"Eliseo@gardner.biz\",\n                        \"body\": \"laudantium enim quasi est quidem magnam voluptate ipsam eos\\ntempora quo necessitatibus\\ndolor quam autem quasi\\nreiciendis et nam sapiente accusantium\"\n                    },\n                    {\n                        \"postId\": 1,\n                        \"id\": 2,\n                        \"name\": \"quo vero reiciendis velit similique earum\",\n                        \"email\": \"Jayne_Kuhic@sydney.com\",\n                        \"body\": \"est natus enim nihil est dolore omnis voluptatem numquam\\net omnis occaecati quod ullam at\\nvoluptatem error expedita pariatur\\nnihil sint nostrum voluptatem reiciendis et\"\n                    },\n                    {\n                        \"postId\": 1,\n                        \"id\": 3,\n                        \"name\": \"odio adipisci rerum aut animi\",\n                        \"email\": \"Nikita@garfield.biz\",\n                        \"body\": \"quia molestiae reprehenderit quasi aspernatur\\naut expedita occaecati aliquam eveniet laudantium\\nomnis quibusdam delectus saepe quia accusamus maiores nam est\\ncum et ducimus et vero voluptates excepturi deleniti ratione\"\n                    }\n                ]\n            \"#\n        })\n        .await;\n        deserialize_comments(data).await\n    });\n    view! {\n        <p id=\"page\">\"View B\"</p>\n        <Suspense fallback=|| view! { <p id=\"loading\">\"Loading...\"</p> }>\n            <ul>\n            {move || Suspend::new(async move {\n                let items = data.await;\n                items.into_iter()\n                    .map(|comment| view! {\n                        <li id=format!(\"{}-{}\", comment.post_id, comment.id)>\n                            <strong>{comment.name}</strong>  \" (by \" {comment.email} \")\"<br/>\n                            {comment.body}\n                        </li>\n                    })\n                    .collect_view()\n            })}\n            </ul>\n        </Suspense>\n    }\n    .into_any()\n}\n\n#[derive(Debug, Clone, Deserialize)]\npub struct Album {\n    #[serde(rename = \"userId\")]\n    user_id: usize,\n    id: usize,\n    title: String,\n}\n\n// View C: a lazy view, and some data, loaded in parallel when we navigate to /c.\n#[derive(Clone)]\npub struct ViewC {\n    data: LocalResource<Vec<Album>>,\n}\n\n// Lazy-loaded routes need to implement the LazyRoute trait. They define a \"route data\" struct,\n// which is created with `::data()`, and then a separate view function which is lazily loaded.\n//\n// This is important because it allows us to concurrently 1) load the route data, and 2) lazily\n// load the component, rather than creating a \"waterfall\" where we can't start loading the route\n// data until we've received the view.\n//\n// The `#[lazy_route]` macro makes `view` into a lazy-loaded inner function, replacing `self` with\n// `this`.\n#[lazy_route]\nimpl LazyRoute for ViewC {\n    fn data() -> Self {\n        // the data method itself is synchronous: it typically creates things like Resources,\n        // which are created synchronously but spawn an async data-loading task\n        // if you want further code-splitting, however, you can create a lazy function to load the data!\n        #[lazy]\n        async fn lazy_data() -> Vec<Album> {\n            gloo_timers::future::TimeoutFuture::new(250).await;\n            vec![\n                Album {\n                    user_id: 1,\n                    id: 1,\n                    title: \"quidem molestiae enim\".into(),\n                },\n                Album {\n                    user_id: 1,\n                    id: 2,\n                    title: \"sunt qui excepturi placeat culpa\".into(),\n                },\n                Album {\n                    user_id: 1,\n                    id: 3,\n                    title: \"omnis laborum odio\".into(),\n                },\n            ]\n        }\n\n        Self {\n            data: LocalResource::new(lazy_data),\n        }\n    }\n\n    fn view(this: Self) -> AnyView {\n        let albums = move || {\n            Suspend::new(async move {\n                this.data\n                    .await\n                    .into_iter()\n                    .map(|album| {\n                        view! {\n                            <li id=format!(\"{}-{}\", album.user_id, album.id)>\n                                {album.title}\n                            </li>\n                        }\n                    })\n                    .collect::<Vec<_>>()\n            })\n        };\n        view! {\n            <p id=\"page\">\"View C\"</p>\n            <hr/>\n            <Suspense fallback=|| view! { <p id=\"loading\">\"Loading...\"</p> }>\n                <ul>{albums}</ul>\n            </Suspense>\n        }\n        .into_any()\n    }\n}\n\n// When two functions have shared code, that shared code will be split out automatically\n// into an additional file. For example, the shared serde code here will be split into a single file,\n// and then loaded lazily once when the first of the two functions is called\n\n#[lazy]\npub fn first_value() -> String {\n    #[derive(Serialize)]\n    struct FirstValue {\n        a: String,\n        b: i32,\n    }\n\n    serde_json::to_string(&FirstValue {\n        a: \"First Value\".into(),\n        b: 1,\n    })\n    .unwrap()\n}\n\n#[lazy]\npub fn second_value() -> String {\n    #[derive(Serialize)]\n    struct SecondValue {\n        a: String,\n        b: i32,\n    }\n\n    serde_json::to_string(&SecondValue {\n        a: \"Second Value\".into(),\n        b: 2,\n    })\n    .unwrap()\n}\n\nstruct ViewD {\n    data: Resource<Result<Vec<i32>, ServerFnError>>,\n}\n\n#[lazy_route]\nimpl LazyRoute for ViewD {\n    fn data() -> Self {\n        Self {\n            data: Resource::new(|| (), |_| d_data()),\n        }\n    }\n\n    fn view(this: Self) -> AnyView {\n        let items = move || {\n            Suspend::new(async move {\n                this.data\n                    .await\n                    .unwrap_or_default()\n                    .into_iter()\n                    .map(|item| view! { <li>{item}</li> })\n                    .collect::<Vec<_>>()\n            })\n        };\n        view! {\n            <p id=\"page\">\"View D\"</p>\n            <hr/>\n            <Suspense fallback=|| view! { <p id=\"loading\">\"Loading...\"</p> }>\n                <ul>{items}</ul>\n            </Suspense>\n            <Outlet/>\n        }\n        .into_any()\n    }\n}\n\n// Server functions can be made lazy by combining the two macros,\n// with `#[server]` coming first, then `#[lazy]`\n#[server]\n#[lazy]\nasync fn d_data() -> Result<Vec<i32>, ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n    Ok(vec![1, 1, 2, 3, 5, 8, 13])\n}\n\nstruct ViewE {\n    data: Resource<Result<Vec<String>, ServerFnError>>,\n}\n\n#[lazy_route]\nimpl LazyRoute for ViewE {\n    fn data() -> Self {\n        Self {\n            data: Resource::new(|| (), |_| e_data()),\n        }\n    }\n\n    fn view(this: Self) -> AnyView {\n        let items = move || {\n            Suspend::new(async move {\n                this.data\n                    .await\n                    .unwrap_or_default()\n                    .into_iter()\n                    .map(|item| view! { <li>{item}</li> })\n                    .collect::<Vec<_>>()\n            })\n        };\n        view! {\n            <p id=\"page\">\"View E\"</p>\n            <hr/>\n            <Suspense fallback=|| view! { <p id=\"loading\">\"Loading...\"</p> }>\n                <ul>{items}</ul>\n            </Suspense>\n        }\n        .into_any()\n    }\n}\n\n#[server]\nasync fn e_data() -> Result<Vec<String>, ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n    Ok(vec![\"foo\".into(), \"bar\".into(), \"baz\".into()])\n}\n"
  },
  {
    "path": "examples/lazy_routes/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_lazy(App);\n}\n"
  },
  {
    "path": "examples/lazy_routes/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use lazy_routes::app::{shell, App};\n    use leptos::prelude::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let leptos_options = conf.leptos_options;\n    // Generate the list of routes in your Leptos App\n    let routes = generate_route_list(App);\n\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    println!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "examples/lazy_routes/style/main.scss",
    "content": "body {\n    font-family: sans-serif;\n}\n"
  },
  {
    "path": "examples/parent_child/Cargo.toml",
    "content": "[package]\nname = \"parent-child\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_log = \"1.0\"\nlog = \"0.4.22\"\nconsole_error_panic_hook = \"0.1.7\"\nweb-sys = \"0.3.70\"\n"
  },
  {
    "path": "examples/parent_child/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/parent_child/README.md",
    "content": "# Parent Child Example\n\nThis example highlights four different ways that child components can communicate with their parent:\n\n1. `<ButtonA/>`: passing a WriteSignal as one of the child component props,\n   for the child component to write into and the parent to read\n2. `<ButtonB/>`: passing a closure as one of the child component props, for\n   the child component to call\n3. `<ButtonC/>`: adding a simple event listener on the child component itself\n4. `<ButtonD/>`: providing a context that is used in the component (rather than prop drilling)\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/parent_child/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t\t<style>\n\t\t\t.red {\n\t\t\t\tcolor: red;\n\t\t\t}\n\n\t\t\t.right {\n\t\t\t\ttext-align: right;\n\t\t\t}\n\n\t\t\t.italics {\n\t\t\t\tfont-style: italic;\n\t\t\t}\n\n\t\t\t.smallcaps {\n\t\t\t\tfont-variant: small-caps;\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/parent_child/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/parent_child/src/lib.rs",
    "content": "use leptos::prelude::*;\nuse web_sys::MouseEvent;\n\n// This highlights four different ways that child components can communicate\n// with their parent:\n// 1) <ButtonA/>: passing a WriteSignal as one of the child component props,\n//    for the child component to write into and the parent to read\n// 2) <ButtonB/>: passing a closure as one of the child component props, for\n//    the child component to call\n// 3) <ButtonC/>: adding an `on:` event listener to a component\n// 4) <ButtonD/>: providing a context that is used in the component (rather than prop drilling)\n\n#[derive(Copy, Clone)]\nstruct SmallcapsContext(WriteSignal<bool>);\n\n#[component]\npub fn App() -> impl IntoView {\n    // just some signals to toggle three classes on our <p>\n    let (red, set_red) = signal(false);\n    let (right, set_right) = signal(false);\n    let (italics, set_italics) = signal(false);\n    let (smallcaps, set_smallcaps) = signal(false);\n\n    // the newtype pattern isn't *necessary* here but is a good practice\n    // it avoids confusion with other possible future `WriteSignal<bool>` contexts\n    // and makes it easier to refer to it in ButtonC\n    provide_context(SmallcapsContext(set_smallcaps));\n\n    view! {\n        <main>\n            <p\n                // class: attributes take F: Fn() => bool, and these signals all implement Fn()\n                class:red=red\n                class:right=right\n                class:italics=italics\n                class:smallcaps=smallcaps\n            >\n                \"Lorem ipsum sit dolor amet.\"\n            </p>\n\n            // Button A: pass the signal setter\n            <ButtonA setter=set_red/>\n\n            // Button B: pass a closure\n            <ButtonB on_click=move |_| set_right.update(|value| *value = !*value)/>\n\n            // Button C: use a regular event listener\n            // setting an event listener on a component like this applies it\n            // to each of the top-level elements the component returns\n            <ButtonC on:click=move |_| set_italics.update(|value| *value = !*value)/>\n\n            // Button D gets its setter from context rather than props\n            <ButtonD/>\n        </main>\n    }\n}\n\n/// Button A receives a signal setter and updates the signal itself\n#[component]\npub fn ButtonA(\n    /// Signal that will be toggled when the button is clicked.\n    setter: WriteSignal<bool>,\n) -> impl IntoView {\n    view! { <button on:click=move |_| setter.update(|value| *value = !*value)>\"Toggle Red\"</button> }\n}\n\n/// Button B receives a closure\n#[component]\npub fn ButtonB(\n    /// Callback that will be invoked when the button is clicked.\n    on_click: impl FnMut(MouseEvent) + 'static,\n) -> impl IntoView {\n    view! { <button on:click=on_click>\"Toggle Right\"</button> }\n}\n\n/// Button C is a dummy: it renders a button but doesn't handle\n/// its click. Instead, the parent component adds an event listener.\n#[component]\npub fn ButtonC() -> impl IntoView {\n    view! { <button>\"Toggle Italics\"</button> }\n}\n\n/// Button D is very similar to Button A, but instead of passing the setter as a prop\n/// we get it from the context\n#[component]\npub fn ButtonD() -> impl IntoView {\n    let setter = use_context::<SmallcapsContext>().unwrap().0;\n\n    view! {\n        <button on:click=move |_| {\n            setter.update(|value| *value = !*value)\n        }>\"Toggle Small Caps\"</button>\n    }\n}\n"
  },
  {
    "path": "examples/parent_child/src/main.rs",
    "content": "use leptos::prelude::*;\nuse parent_child::*;\n\npub fn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(App)\n}\n"
  },
  {
    "path": "examples/portal/Cargo.toml",
    "content": "[package]\nname = \"portal\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nlog = \"0.4.27\"\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nwasm-bindgen = \"0.2.100\"\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.50\"\nwasm-bindgen = \"0.2.100\"\nweb-sys = \"0.3.77\"\n"
  },
  {
    "path": "examples/portal/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/webdriver.toml\" },\n  { path = \"../cargo-make/wasm-test.toml\" },\n  { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/portal/README.md",
    "content": "# Leptos Portal Example\n\nThis example showcases a basic leptos app with a portal.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/portal/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\" data-weak-refs/>\n\t</head>\n\t<body>\n\t\t<div id=\"app\"></div>\n\t</body>\n</html>"
  },
  {
    "path": "examples/portal/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/portal/src/lib.rs",
    "content": "use leptos::{control_flow::Show, portal::Portal, prelude::*};\n\n#[component]\npub fn App() -> impl IntoView {\n    let (show_overlay, set_show_overlay) = signal(false);\n    let (show_inside_overlay, set_show_inside_overlay) = signal(false);\n\n    view! {\n        <div>\n            <button id=\"btn-show\" on:click=move |_| set_show_overlay.set(true)>\n                \"Show Overlay\"\n            </button>\n\n            <Show when=move || show_overlay.get() fallback=|| ()>\n                <div>Show</div>\n                <Portal mount=document().get_element_by_id(\"app\").unwrap()>\n                    <div style=\"position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;\">\n                        <p>This is in the body element</p>\n                        <button id=\"btn-hide\" on:click=move |_| set_show_overlay.set(false)>\n                            \"Close Overlay\"\n                        </button>\n                        <button\n                            id=\"btn-toggle\"\n                            on:click=move |_| {\n                                set_show_inside_overlay.set(!show_inside_overlay.get())\n                            }\n                        >\n                            \"Toggle inner\"\n                        </button>\n\n                        <Show when=move || show_inside_overlay.get() fallback=|| view! { \"Hidden\" }>\n                            \"Visible\"\n                        </Show>\n                    </div>\n                </Portal>\n            </Show>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/portal/src/main.rs",
    "content": "use leptos::prelude::*;\nuse portal::App;\nuse wasm_bindgen::JsCast;\n\nfn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    let handle = mount_to(\n        document()\n            .get_element_by_id(\"app\")\n            .unwrap()\n            .unchecked_into(),\n        App,\n    );\n    handle.forget();\n}\n"
  },
  {
    "path": "examples/portal/tests/web.rs",
    "content": "#![allow(dead_code)]\n\nuse leptos::{leptos_dom::helpers::document, mount::mount_to, task::tick};\nuse portal::App;\nuse wasm_bindgen::JsCast;\nuse wasm_bindgen_test::*;\nuse web_sys::HtmlButtonElement;\nwasm_bindgen_test_configure!(run_in_browser);\n\nfn minify(html: &str) -> String {\n    let mut result = String::with_capacity(html.len());\n    let mut in_tag = false;\n    let mut last_char_was_tag_end = false;\n    let mut whitespace_buffer = String::new();\n\n    for c in html.chars() {\n        match c {\n            '<' => {\n                // Starting a new tag\n                in_tag = true;\n                last_char_was_tag_end = false;\n\n                // Discard any buffered whitespace\n                whitespace_buffer.clear();\n\n                result.push(c);\n            }\n            '>' => {\n                // Ending a tag\n                in_tag = false;\n                last_char_was_tag_end = true;\n                result.push(c);\n            }\n            c if c.is_whitespace() => {\n                if in_tag {\n                    // Preserve whitespace inside tags\n                    result.push(c);\n                } else if !last_char_was_tag_end {\n                    // Buffer whitespace between content\n                    whitespace_buffer.push(c);\n                }\n                // Whitespace immediately after a tag end is ignored\n            }\n            _ => {\n                // Regular character\n                last_char_was_tag_end = false;\n\n                // If we have buffered whitespace and are outputting content,\n                // preserve a single space\n                if !whitespace_buffer.is_empty() {\n                    result.push(' ');\n                    whitespace_buffer.clear();\n                }\n\n                result.push(c);\n            }\n        }\n    }\n\n    result\n}\n\n#[wasm_bindgen_test]\nasync fn portal() {\n    let document = document();\n    let body = document.body().unwrap();\n\n    let div = document.create_element(\"div\").unwrap();\n    div.set_id(\"app\");\n    let _ = body.append_child(&div);\n\n    let _handle = mount_to(div.clone().unchecked_into(), App);\n\n    let show_button = document\n        .get_element_by_id(\"btn-show\")\n        .unwrap()\n        .unchecked_into::<HtmlButtonElement>();\n\n    show_button.click();\n\n    tick().await;\n\n    // check HTML\n    assert_eq!(\n        minify(div.inner_html().as_str()),\n        minify(\n            \"<div><button id=\\\"btn-show\\\">Show \\\n             Overlay</button><div>Show</div><!----></div><div><div \\\n             style=\\\"position: fixed; z-index: 10; width: 100vw; height: \\\n             100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: \\\n             white;\\\"><p>This is in the body element</p><button \\\n             id=\\\"btn-hide\\\">Close Overlay</button><button \\\n             id=\\\"btn-toggle\\\">Toggle inner</button>Hidden</div></div>\"\n        )\n    );\n\n    let toggle_button = document\n        .get_element_by_id(\"btn-toggle\")\n        .unwrap()\n        .unchecked_into::<HtmlButtonElement>();\n\n    toggle_button.click();\n\n    assert_eq!(\n        minify(div.inner_html().as_str()),\n        minify(\n            \"<div><button id=\\\"btn-show\\\">Show \\\n             Overlay</button><div>Show</div><!----></div><div><div \\\n             style=\\\"position: fixed; z-index: 10; width: 100vw; height: \\\n             100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: \\\n             white;\\\"><p>This is in the body element</p><button \\\n             id=\\\"btn-hide\\\">Close Overlay</button><button \\\n             id=\\\"btn-toggle\\\">Toggle inner</button>Hidden</div></div>\"\n        )\n    );\n\n    let hide_button = document\n        .get_element_by_id(\"btn-hide\")\n        .unwrap()\n        .unchecked_into::<HtmlButtonElement>();\n\n    hide_button.click();\n\n    assert_eq!(\n        minify(div.inner_html().as_str()),\n        minify(\n            \"<div><button id=\\\"btn-show\\\">Show \\\n             Overlay</button><div>Show</div><!----></div><div><div \\\n             style=\\\"position: fixed; z-index: 10; width: 100vw; height: \\\n             100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: \\\n             white;\\\"><p>This is in the body element</p><button \\\n             id=\\\"btn-hide\\\">Close Overlay</button><button \\\n             id=\\\"btn-toggle\\\">Toggle inner</button>Hidden</div></div>\"\n        )\n    );\n}\n\n#[test]\nfn test_minify() {\n    let input = r#\"<div>\n            <p>   Hello   world!   </p>\n\n            <ul>\n                <li>Item 1</li>\n                <li>Item 2</li>\n            </ul>\n        </div>\"#;\n\n    let expected = r#\"<div><p>Hello world!</p><ul><li>Item 1</li><li>Item 2</li></ul></div>\"#;\n\n    assert_eq!(minify(input), expected);\n}\n\n#[test]\nfn test_preserve_whitespace_in_tags() {\n    let input = r#\"<div class = \"container\">\"#;\n    let expected = r#\"<div class = \"container\">\"#;\n\n    assert_eq!(minify(input), expected);\n}\n"
  },
  {
    "path": "examples/regression/Cargo.toml",
    "content": "[package]\nname = \"regression\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.8.1\", optional = true }\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\nleptos = { path = \"../../leptos\", features = [\"tracing\"] }\nleptos_meta = { path = \"../../meta\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../router\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\nthiserror = \"2.0.12\"\ntokio = { version = \"1.39\", features = [ \"rt-multi-thread\", \"macros\", \"time\" ], optional = true }\nwasm-bindgen = \"0.2.106\"\n\n[features]\nhydrate = [\n  \"leptos/hydrate\",\n]\nssr = [\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"dep:leptos_axum\",\n  \"leptos_router/ssr\",\n]\n\n[profile.release]\npanic = \"abort\"\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"]]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"regression\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"cargo make test-ui\"\nend2end-dir = \"e2e\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/regression/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 Leptos\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/regression/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos-webdriver-test.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"regression\"\n"
  },
  {
    "path": "examples/regression/README.md",
    "content": "# Regression Tests\n\nThis example functions as a catch-all for all current and future regression\ntest cases that typically happens at integration.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/regression/e2e/Cargo.toml",
    "content": "[package]\nname = \"regression_e2e\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\nanyhow = \"1.0\"\nasync-trait = \"0.1.81\"\ncucumber = \"0.21.1\"\nfantoccini = \"0.21.1\"\npretty_assertions = \"1.4\"\nserde_json = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\", \"time\"] }\nurl = \"2.5\"\n\n[[test]]\nname = \"app_suite\"\nharness = false    # Allow Cucumber to print output instead of libtest\n"
  },
  {
    "path": "examples/regression/e2e/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.test]\nenv = { RUN_AUTOMATICALLY = false }\ncondition = { env_true = [\"RUN_AUTOMATICALLY\"] }\n\n[tasks.ci]\n\n[tasks.test-ui]\ncommand = \"cargo\"\nargs = [\n  \"test\",\n  \"--test\",\n  \"app_suite\",\n  \"--\",\n  \"--retry\",\n  \"2\",\n  \"--retry-after\",\n  \"5sec\",\n  \"--fail-fast\",\n  \"${@}\",\n]\n"
  },
  {
    "path": "examples/regression/e2e/README.md",
    "content": "# E2E Testing\n\nThis example demonstrates e2e testing with Rust using executable requirements.\n\n## Testing Stack\n\n|    |      Role      |  Description |\n|---|---|---|\n| [Cucumber](https://github.com/cucumber-rs/cucumber/tree/main) | Test Runner | Run [Gherkin](https://cucumber.io/docs/gherkin/reference/) specifications as Rust tests |\n| [Fantoccini](https://github.com/jonhoo/fantoccini/tree/main) | Browser Client | Interact with web pages through WebDriver |\n| [Cargo Leptos](https://github.com/leptos-rs/cargo-leptos) | Build Tool |  Compile example and start the server and end-2-end tests |\n| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome |\n\n## Testing Organization\n\nTesting is organized around what a user can do and see/not see. Test scenarios are grouped by the **user action** and the **object** of that action. This makes it easier to locate and reason about requirements.\n\nHere is a brief overview of how things fit together.\n\n```bash\nfeatures\n└── {action}_{object}.feature   # Specify test scenarios\ntests\n├── fixtures\n│   ├── action.rs               # Perform a user action (click, type, etc.)\n│   ├── check.rs                # Assert what a user can see/not see\n│   ├── find.rs                 # Query page elements\n│   ├── mod.rs\n│   └── world\n│       ├── action_steps.rs     # Map Gherkin steps to user actions\n│       ├── check_steps.rs      # Map Gherkin steps to user expectations\n│       └── mod.rs\n└── app_suite.rs                # Test main\n```\n"
  },
  {
    "path": "examples/regression/e2e/features/issue_4005.feature",
    "content": "@check_issue_4005\nFeature: Check that issue 4005 does not reappear\n\n\tScenario: The second item is selected.\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4005\n\t\tThen I see the value of select is 2"
  },
  {
    "path": "examples/regression/e2e/features/issue_4088.feature",
    "content": "@check_issue_4088\nFeature: Check that issue 4088 does not reappear\n\n\tScenario: I can see the navbar\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4088\n\t\tThen I see the navbar\n\n\tScenario: The user info is shared via context\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4088\n\t\tWhen I select the link Class 1\n\t\tThen I see the result is the string Assignments for team of user with id 42\n\n\tScenario: The user info is shared via context\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4088\n\t\tWhen I select the link Class 1\n\t\tWhen I refresh the browser\n\t\tThen I see the result is the string Assignments for team of user with id 42\n"
  },
  {
    "path": "examples/regression/e2e/features/issue_4217.feature",
    "content": "@check_issue_4217\nFeature: Check that issue 4217 does not reappear\n\n\tScenario: All items are selected.\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4217\n\t\tThen I see option1 is selected\n\t\tAnd I see option2 is selected\n\t\tAnd I see option3 is selected"
  },
  {
    "path": "examples/regression/e2e/features/issue_4251.feature",
    "content": "@check_issue_4251\nFeature: Check that issue 4251 does not reappear\n\n\tScenario: Clicking a link to the same page you’re currently on should not add the page to the history stack.\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4324\n\t\tWhen I select the link This page\n\t\tAnd I select the link This page\n\t\tAnd I select the link This page\n\t\tThen I see the result is the string Issue4324\n\t\tWhen I press the back button\n\t\tAnd I select the link 4324\n\t\tThen I see the result is the string Issue4324"
  },
  {
    "path": "examples/regression/e2e/features/issue_4285.feature",
    "content": "@check_issue_4285\nFeature: Check that issue 4285 does not reappear\n\n\tScenario: Navigating several times to same lazy route does not cause issues.\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4285\n\t\tAnd I can access regression test 4285\n\t\tAnd I can access regression test 4285\n\t\tThen I see the result is the string 42\n"
  },
  {
    "path": "examples/regression/e2e/features/issue_4296.feature",
    "content": "@check_issue_4296\nFeature: Check that issue 4296 does not reappear\n\n\tScenario: Query param signals created in LazyRoute::data() are reactive in ::view().\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4296\n\t\tThen I see the result is the string None\n\t\tWhen I select the link abc\n\t\tThen I see the result is the string Some(\"abc\")\n\t\tWhen I select the link def\n\t\tThen I see the result is the string Some(\"def\")\n\n\tScenario: Loading page with query signal works as well.\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4296\n\t\tWhen I select the link abc\n\t\tWhen I reload the page\n\t\tThen I see the result is the string Some(\"abc\")\n"
  },
  {
    "path": "examples/regression/e2e/features/issue_4324.feature",
    "content": "@check_issue_4324\nFeature: Check that issue 4324 does not reappear\n\n\tScenario: Navigating to the same page after clicking \"Back\" should set the URL correctly\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4324\n\t\tThen I see the path is /4324/\n\t\tWhen I press the back button\n\t\tThen I see the path is /\n\t\tWhen I select the link 4324\n\t\tThen I see the path is /4324/"
  },
  {
    "path": "examples/regression/e2e/features/issue_4492.feature",
    "content": "@check_issue_4492\nFeature: Regression test for issue #4492\n\n\tScenario: Scenario A should show Loading once on first load.\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4492\n\t\tWhen I click the button a-toggle\n\t\tThen I see a-result has the text Loading...\n\t\tWhen I wait 100ms\n\t\tThen I see a-result has the text 0\n\t\tWhen I click the button a-button\n\t\tThen I see a-result has the text 0\n\t\tWhen I wait 100ms\n\t\tThen I see a-result has the text 1\n\n\tScenario: Scenario B should never show Loading\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4492\n\t\tWhen I click the button b-toggle\n\t\tThen I see b-result has the text 0\n\t\tWhen I click the button b-button\n\t\tThen I see b-result has the text 0\n\t\tWhen I wait 100ms\n\t\tThen I see b-result has the text 1\n\t\tWhen I click the button b-button\n\t\tThen I see b-result has the text 1\n\t\tWhen I wait 100ms\n\t\tThen I see b-result has the text 2\n\n\tScenario: Scenario C should never show Loading\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4492\n\t\tWhen I click the button c-toggle\n\t\tThen I see c-result has the text 0\n\t\tWhen I click the button c-button\n\t\tThen I see c-result has the text 42\n\t\tWhen I wait 100ms\n\t\tThen I see c-result has the text 1\n"
  },
  {
    "path": "examples/regression/e2e/features/pr_4015.feature",
    "content": "@check_pr_4015\nFeature: Check that PR 4015 does not regress\n\n\tScenario: The correct text appears\n\t\tGiven I see the app\n\t\tAnd I can access regression test 4015\n\t\tThen I see the result is the string Some(42)\n\n"
  },
  {
    "path": "examples/regression/e2e/features/pr_4091.feature",
    "content": "@check_pr_4091\nFeature: Regression from pull request 4091\n\n    Scenario: Signal for testing should work\n        Given I see the app\n        And I can access regression test 4091\n        When I select the link test1\n        Then I see the result is the string Test1\n\n    Scenario: The result returns to empty due to on_cleanup\n        Given I see the app\n        And I can access regression test 4091\n        When I select the following links\n            | test1     |\n            | 4091 Home |\n        Then I see the result is empty\n\n    Scenario: The result does not accumulate due to on_cleanup\n        Given I see the app\n        And I can access regression test 4091\n        When I select the following links\n            | test1     |\n            | 4091 Home |\n            | test1     |\n            | 4091 Home |\n        Then I see the result is empty\n\n    Scenario: I can see the navbar\n        Given I see the app\n        And I can access regression test 4091\n        Then I see the navbar\n\n    Scenario: If I navigate to home and back, I can still see the navbar\n        Given I see the app\n        And I can access regression test 4091\n        When I select the following links\n            | Home |\n            | 4091 |\n        Then I see the navbar\n\n    Scenario: The signal is not disposed too early\n        Given I see the app\n        And I can access regression test 4091\n        When I select the following links\n            | test1 |\n            | Home  |\n            | 4091  |\n        Then I see the navbar"
  },
  {
    "path": "examples/regression/e2e/tests/app_suite.rs",
    "content": "mod fixtures;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fixtures::world::AppWorld;\nuse std::{ffi::OsStr, fs::read_dir};\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    // Normally the below is done, but it's now gotten to the point of\n    // having a sufficient number of tests where the resource contention\n    // of the concurrently running browsers will cause failures on CI.\n    // AppWorld::cucumber()\n    //     .fail_on_skipped()\n    //     .run_and_exit(\"./features\")\n    //     .await;\n\n    // Mitigate the issue by manually stepping through each feature,\n    // rather than letting cucumber glob them and dispatch all at once.\n    for entry in read_dir(\"./features\")? {\n        let path = entry?.path();\n        if path.extension() == Some(OsStr::new(\"feature\")) {\n            AppWorld::cucumber()\n                .fail_on_skipped()\n                .run_and_exit(path)\n                .await;\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "examples/regression/e2e/tests/fixtures/action.rs",
    "content": "use super::{find, world::HOST};\nuse anyhow::Result;\nuse fantoccini::Client;\nuse std::result::Result::Ok;\n\npub async fn goto_path(client: &Client, path: &str) -> Result<()> {\n    let url = format!(\"{}{}\", HOST, path);\n    client.goto(&url).await?;\n\n    Ok(())\n}\n\npub async fn click_link(client: &Client, text: &str) -> Result<()> {\n    let link = find::link_with_text(&client, &text).await?;\n    link.click().await?;\n    Ok(())\n}\n\npub async fn click_button(client: &Client, id: &str) -> Result<()> {\n    let btn = find::element_by_id(&client, &id).await?;\n    btn.click().await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/regression/e2e/tests/fixtures/check.rs",
    "content": "use crate::fixtures::find;\nuse anyhow::{Ok, Result};\nuse fantoccini::Client;\nuse pretty_assertions::assert_eq;\n\npub async fn result_text_is(\n    client: &Client,\n    expected_text: &str,\n) -> Result<()> {\n    element_text_is(client, \"result\", expected_text).await\n}\n\npub async fn element_text_is(\n    client: &Client,\n    id: &str,\n    expected_text: &str,\n) -> Result<()> {\n    let actual = find::text_at_id(client, id).await?;\n    assert_eq!(&actual, expected_text);\n    Ok(())\n}\n\npub async fn element_exists(client: &Client, id: &str) -> Result<()> {\n    find::element_by_id(client, id)\n        .await\n        .expect(&format!(\"could not find element with id `{id}`\"));\n    Ok(())\n}\n\npub async fn select_option_is_selected(\n    client: &Client,\n    id: &str,\n) -> Result<()> {\n    let el = find::element_by_id(client, id)\n        .await\n        .expect(&format!(\"could not find element with id `{id}`\"));\n    let selected = el.prop(\"selected\").await?;\n    assert_eq!(selected.as_deref(), Some(\"true\"));\n    Ok(())\n}\n\npub async fn element_value_is(\n    client: &Client,\n    id: &str,\n    expected: &str,\n) -> Result<()> {\n    let el = find::element_by_id(client, id)\n        .await\n        .expect(&format!(\"could not find element with id `{id}`\"));\n    let value = el.prop(\"value\").await?;\n    assert_eq!(value.as_deref(), Some(expected));\n    Ok(())\n}\n\npub async fn path_is(client: &Client, expected_path: &str) -> Result<()> {\n    let url = client\n        .current_url()\n        .await\n        .expect(\"could not access current URL\");\n    let path = url.path();\n    assert_eq!(expected_path, path);\n    Ok(())\n}\n"
  },
  {
    "path": "examples/regression/e2e/tests/fixtures/find.rs",
    "content": "use anyhow::{Ok, Result};\nuse fantoccini::{elements::Element, Client, Locator};\n\npub async fn text_at_id(client: &Client, id: &str) -> Result<String> {\n    let element = element_by_id(client, id)\n        .await\n        .expect(format!(\"no such element with id `{}`\", id).as_str());\n    let text = element.text().await?;\n    Ok(text)\n}\n\npub async fn link_with_text(client: &Client, text: &str) -> Result<Element> {\n    let link = client\n        .wait()\n        .for_element(Locator::LinkText(text))\n        .await\n        .expect(format!(\"Link not found by `{}`\", text).as_str());\n    Ok(link)\n}\n\npub async fn element_by_id(client: &Client, id: &str) -> Result<Element> {\n    Ok(client.wait().for_element(Locator::Id(id)).await?)\n}\n"
  },
  {
    "path": "examples/regression/e2e/tests/fixtures/mod.rs",
    "content": "pub mod action;\npub mod check;\npub mod find;\npub mod world;\n"
  },
  {
    "path": "examples/regression/e2e/tests/fixtures/world/action_steps.rs",
    "content": "use crate::fixtures::{action, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::{gherkin::Step, given, when};\n\n#[given(\"I see the app\")]\n#[when(\"I open the app\")]\nasync fn i_open_the_app(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::goto_path(client, \"\").await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I can access regression test (.*)$\")]\n#[when(regex = \"^I select the link (.*)$\")]\nasync fn i_select_the_link(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::click_link(client, &text).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"^I click the button (.*)$\")]\nasync fn i_click_the_button(world: &mut AppWorld, id: String) -> Result<()> {\n    let client = &world.client;\n    action::click_button(client, &id).await?;\n\n    Ok(())\n}\n\n#[given(expr = \"I select the following links\")]\n#[when(expr = \"I select the following links\")]\nasync fn i_select_the_following_links(\n    world: &mut AppWorld,\n    step: &Step,\n) -> Result<()> {\n    let client = &world.client;\n\n    if let Some(table) = step.table.as_ref() {\n        for row in table.rows.iter() {\n            action::click_link(client, &row[0]).await?;\n        }\n    }\n\n    Ok(())\n}\n\n#[given(regex = \"^I (refresh|reload) the (browser|page)$\")]\n#[when(regex = \"^I (refresh|reload) the (browser|page)$\")]\nasync fn i_refresh_the_browser(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    client.refresh().await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I press the back button$\")]\n#[when(regex = \"^I press the back button$\")]\nasync fn i_go_back(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    client.back().await?;\n\n    Ok(())\n}\n\n#[when(regex = r\"^I wait (\\d+)ms$\")]\nasync fn i_wait_ms(_world: &mut AppWorld, ms: u64) -> Result<()> {\n    tokio::time::sleep(std::time::Duration::from_millis(ms)).await;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/regression/e2e/tests/fixtures/world/check_steps.rs",
    "content": "use crate::fixtures::{check, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::then;\n\n#[then(regex = r\"^I see the result is empty$\")]\nasync fn i_see_the_result_is_empty(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    check::result_text_is(client, \"\").await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see the result is the string (.*)$\")]\nasync fn i_see_the_result_is_the_string(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::result_text_is(client, &text).await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see ([\\w-]+) has the text (.*)$\")]\nasync fn i_see_element_has_text(\n    world: &mut AppWorld,\n    id: String,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::element_text_is(client, &id, &text).await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see the navbar$\")]\nasync fn i_see_the_navbar(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    check::element_exists(client, \"nav\").await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see ([\\d\\w]+) is selected$\")]\nasync fn i_see_the_select(world: &mut AppWorld, id: String) -> Result<()> {\n    let client = &world.client;\n    check::select_option_is_selected(client, &id).await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see the value of (\\w+) is (.*)$\")]\nasync fn i_see_the_value(\n    world: &mut AppWorld,\n    id: String,\n    value: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::element_value_is(client, &id, &value).await?;\n    Ok(())\n}\n\n#[then(regex = r\"^I see the path is (.*)$\")]\nasync fn i_see_the_path(world: &mut AppWorld, path: String) -> Result<()> {\n    let client = &world.client;\n    check::path_is(client, &path).await?;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/regression/e2e/tests/fixtures/world/mod.rs",
    "content": "pub mod action_steps;\npub mod check_steps;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fantoccini::{\n    error::NewSessionError, wd::Capabilities, Client, ClientBuilder,\n};\n\npub const HOST: &str = \"http://127.0.0.1:3000\";\n\n#[derive(Debug, World)]\n#[world(init = Self::new)]\npub struct AppWorld {\n    pub client: Client,\n}\n\nimpl AppWorld {\n    async fn new() -> Result<Self, anyhow::Error> {\n        let webdriver_client = build_client().await?;\n\n        Ok(Self {\n            client: webdriver_client,\n        })\n    }\n}\n\nasync fn build_client() -> Result<Client, NewSessionError> {\n    let mut cap = Capabilities::new();\n    let arg = serde_json::from_str(\"{\\\"args\\\": [\\\"-headless\\\"]}\").unwrap();\n    cap.insert(\"goog:chromeOptions\".to_string(), arg);\n\n    let client = ClientBuilder::native()\n        .capabilities(cap)\n        .connect(\"http://localhost:4444\")\n        .await?;\n\n    Ok(client)\n}\n"
  },
  {
    "path": "examples/regression/src/app.rs",
    "content": "use crate::{\n    issue_4005::Routes4005, issue_4088::Routes4088, issue_4217::Routes4217,\n    issue_4285::Routes4285, issue_4296::Routes4296, issue_4324::Routes4324,\n    issue_4492::Routes4492, pr_4015::Routes4015, pr_4091::Routes4091,\n};\nuse leptos::prelude::*;\nuse leptos_meta::{MetaTags, *};\nuse leptos_router::{\n    components::{Route, Router, Routes},\n    path,\n};\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n    let fallback = || view! { \"Page not found.\" }.into_view();\n    let (_, set_is_routing) = signal(false);\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/regression.css\"/>\n        <Router set_is_routing>\n            <main>\n                <Routes fallback>\n                    <Route path=path!(\"\") view=HomePage/>\n                    <Routes4091/>\n                    <Routes4015/>\n                    <Routes4088/>\n                    <Routes4217/>\n                    <Routes4005/>\n                    <Routes4285/>\n                    <Routes4296/>\n                    <Routes4324/>\n                    <Routes4492/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n#[server]\nasync fn server_call() -> Result<(), ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_millis(1)).await;\n    Ok(())\n}\n\n#[component]\nfn HomePage() -> impl IntoView {\n    view! {\n        <Title text=\"Regression Tests\"/>\n        <h1>\"Listing of regression tests\"</h1>\n        <nav>\n            <ul>\n                <li><a href=\"/4091/\">\"4091\"</a></li>\n                <li><a href=\"/4015/\">\"4015\"</a></li>\n                <li><a href=\"/4088/\">\"4088\"</a></li>\n                <li><a href=\"/4217/\">\"4217\"</a></li>\n                <li><a href=\"/4005/\">\"4005\"</a></li>\n                <li><a href=\"/4285/\">\"4285\"</a></li>\n                <li><a href=\"/4296/\">\"4296\"</a></li>\n                <li><a href=\"/4324/\">\"4324\"</a></li>\n                <li><a href=\"/4492/\">\"4492\"</a></li>\n            </ul>\n        </nav>\n    }\n}\n"
  },
  {
    "path": "examples/regression/src/issue_4005.rs",
    "content": "use leptos::prelude::*;\n#[allow(unused_imports)]\nuse leptos_router::{\n    components::Route, path, MatchNestedRoutes, NavigateOptions,\n};\n\n#[component]\npub fn Routes4005() -> impl MatchNestedRoutes + Clone {\n    view! {\n        <Route path=path!(\"4005\") view=Issue4005/>\n    }\n    .into_inner()\n}\n\n#[component]\nfn Issue4005() -> impl IntoView {\n    view! {\n        <select id=\"select\" prop:value=\"2\">\n            <option value=\"1\">\"Option 1\"</option>\n            <option value=\"2\">\"Option 2\"</option>\n            <option value=\"3\">\"Option 3\"</option>\n        </select>\n    }\n}\n"
  },
  {
    "path": "examples/regression/src/issue_4088.rs",
    "content": "use leptos::{either::Either, prelude::*};\n#[allow(unused_imports)]\nuse leptos_router::{\n    components::{Outlet, ParentRoute, Redirect, Route},\n    path, MatchNestedRoutes, NavigateOptions,\n};\nuse serde::{Deserialize, Serialize};\n\n#[component]\npub fn Routes4088() -> impl MatchNestedRoutes + Clone {\n    view! {\n        <ParentRoute path=path!(\"4088\") view=|| view!{ <LoggedIn/> }>\n\t\t\t<ParentRoute path=path!(\"\") view=||view!{<AssignmentsSelector/>}>\n\t\t\t\t<Route path=path!(\"/:team_id\") view=||view!{<AssignmentsForTeam/>} />\n\t\t\t\t<Route path=path!(\"\") view=||view!{ <p>No class selected</p> }/>\n\t\t\t</ParentRoute>\n        </ParentRoute>\n    }\n    .into_inner()\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct UserInfo {\n    pub id: usize,\n}\n\n#[server]\npub async fn get_user_info() -> Result<Option<UserInfo>, ServerFnError> {\n    Ok(Some(UserInfo { id: 42 }))\n}\n\n#[component]\npub fn LoggedIn() -> impl IntoView {\n    let user_info_resource =\n        Resource::new(|| (), move |_| async { get_user_info().await });\n\n    view! {\n\n      <Transition fallback=move || view!{\n            \"loading\"\n            }\n        >\n        {move || {\n            user_info_resource.get()\n                .map(|a|\n                    match a {\n                        Ok(Some(a)) => Either::Left(view! {\n                            <LoggedInContent user_info={a} />\n                        }),\n                        _ => Either::Right(view!{\n                            <Redirect path=\"/not_logged_in\"/>\n                        })\n                    })\n        }}\n        </Transition>\n    }\n}\n\n#[component]\n/// Component which provides UserInfo and renders it's child\n/// Can also contain some code to check for specific situations (e.g. privacy policies accepted or not? redirect if needed...)\npub fn LoggedInContent(user_info: UserInfo) -> impl IntoView {\n    provide_context(user_info.clone());\n\n    if user_info.id == 42 {\n        Either::Left(Outlet())\n    } else {\n        Either::Right(\n            view! { <Redirect path=\"/somewhere\" options={NavigateOptions::default()}/> },\n        )\n    }\n}\n\n#[component]\n/// This component also uses Outlet (so nested Outlet)\nfn AssignmentsSelector() -> impl IntoView {\n    let user_info = use_context::<UserInfo>().expect(\"user info not provided\");\n\n    view! {\n        <p>\"Assignments for user with ID: \"{user_info.id}</p>\n        <ul id=\"nav\">\n            <li><a href=\"/4088/1\">\"Class 1\"</a></li>\n            <li><a href=\"/4088/2\">\"Class 2\"</a></li>\n            <li><a href=\"/4088/3\">\"Class 3\"</a></li>\n        </ul>\n\n        <Outlet />\n    }\n}\n\n#[component]\nfn AssignmentsForTeam() -> impl IntoView {\n    // THIS FAILS -> Because of the nested outlet in LoggedInContent > AssignmentsSelector?\n    // It did not fail when LoggedIn did not use a resource and transition (but a hardcoded UserInfo in the component)\n    let user_info = use_context::<UserInfo>().expect(\"user info not provided\");\n\n    let items = vec![\"Assignment 1\", \"Assignment 2\", \"Assignment 3\"];\n    view! {\n        <p id=\"result\">\"Assignments for team of user with id \" {user_info.id}</p>\n        <ul>\n            {\n            items.into_iter().map(|item| {\n                view! {\n                    <Assignment name=item.to_string() />\n                }\n            }).collect_view()\n            }\n        </ul>\n    }\n}\n\n#[component]\nfn Assignment(name: String) -> impl IntoView {\n    let user_info = use_context::<UserInfo>().expect(\"user info not provided\");\n\n    view! {\n        <li>{name}\" \"{user_info.id}</li>\n    }\n}\n"
  },
  {
    "path": "examples/regression/src/issue_4217.rs",
    "content": "use leptos::prelude::*;\n#[allow(unused_imports)]\nuse leptos_router::{\n    components::Route, path, MatchNestedRoutes, NavigateOptions,\n};\n\n#[component]\npub fn Routes4217() -> impl MatchNestedRoutes + Clone {\n    view! {\n        <Route path=path!(\"4217\") view=Issue4217/>\n    }\n    .into_inner()\n}\n\n#[component]\nfn Issue4217() -> impl IntoView {\n    view! {\n        <select multiple=true>\n            <option id=\"option1\" value=\"1\" selected>\"Option 1\"</option>\n            <option id=\"option2\" value=\"2\" selected>\"Option 2\"</option>\n            <option id=\"option3\" value=\"3\" selected>\"Option 3\"</option>\n        </select>\n    }\n}\n"
  },
  {
    "path": "examples/regression/src/issue_4285.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::LazyRoute;\n#[allow(unused_imports)]\nuse leptos_router::{\n    components::Route, path, Lazy, MatchNestedRoutes, NavigateOptions,\n};\n\n#[component]\npub fn Routes4285() -> impl MatchNestedRoutes + Clone {\n    view! {\n        <Route path=path!(\"4285\") view={Lazy::<Issue4285>::new()}/>\n    }\n    .into_inner()\n}\n\nstruct Issue4285 {\n    data: Resource<Result<i32, ServerFnError>>,\n}\n\nimpl LazyRoute for Issue4285 {\n    fn data() -> Self {\n        Self {\n            data: Resource::new(|| (), |_| slow_call()),\n        }\n    }\n\n    async fn view(this: Self) -> AnyView {\n        let Issue4285 { data } = this;\n        view! {\n            <Suspense>\n                {move || {\n                    Suspend::new(async move {\n                        let data = data.await;\n                        view! {\n                            <p id=\"result\">{data}</p>\n                        }\n                    })\n                }}\n            </Suspense>\n        }\n        .into_any()\n    }\n}\n\n#[server]\nasync fn slow_call() -> Result<i32, ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n    Ok(42)\n}\n"
  },
  {
    "path": "examples/regression/src/issue_4296.rs",
    "content": "use leptos::prelude::*;\n#[allow(unused_imports)]\nuse leptos_router::{\n    components::Route, path, Lazy, MatchNestedRoutes, NavigateOptions,\n};\nuse leptos_router::{hooks::use_query_map, LazyRoute};\n\n#[component]\npub fn Routes4296() -> impl MatchNestedRoutes + Clone {\n    view! {\n        <Route path=path!(\"4296\") view={Lazy::<Issue4296>::new()}/>\n    }\n    .into_inner()\n}\n\nstruct Issue4296 {\n    query: Signal<Option<String>>,\n}\n\nimpl LazyRoute for Issue4296 {\n    fn data() -> Self {\n        let query = use_query_map();\n        let query = Signal::derive(move || query.read().get(\"q\"));\n        Self { query }\n    }\n\n    async fn view(this: Self) -> AnyView {\n        let Issue4296 { query } = this;\n        view! {\n            <a href=\"?q=abc\">\"abc\"</a>\n            <a href=\"?q=def\">\"def\"</a>\n            <p id=\"result\">{move || format!(\"{:?}\", query.get())}</p>\n        }\n        .into_any()\n    }\n}\n"
  },
  {
    "path": "examples/regression/src/issue_4324.rs",
    "content": "use leptos::prelude::*;\n#[allow(unused_imports)]\nuse leptos_router::{\n    components::Route, path, Lazy, MatchNestedRoutes, NavigateOptions,\n};\n\n#[component]\npub fn Routes4324() -> impl MatchNestedRoutes + Clone {\n    view! {\n        <Route path=path!(\"4324\") view=Issue4324/>\n    }\n    .into_inner()\n}\n\n#[component]\npub fn Issue4324() -> impl IntoView {\n    view! {\n        <a href=\"/4324/\">\"This page\"</a>\n        <p id=\"result\">\"Issue4324\"</p>\n    }\n}\n"
  },
  {
    "path": "examples/regression/src/issue_4492.rs",
    "content": "use leptos::prelude::*;\n#[allow(unused_imports)]\nuse leptos_router::{\n    components::Route, path, MatchNestedRoutes, NavigateOptions,\n};\n\n#[component]\npub fn Routes4492() -> impl MatchNestedRoutes + Clone {\n    view! {\n        <Route path=path!(\"4492\") view=Issue4492/>\n    }\n    .into_inner()\n}\n\n#[component]\nfn Issue4492() -> impl IntoView {\n    let show_a = RwSignal::new(false);\n    let show_b = RwSignal::new(false);\n    let show_c = RwSignal::new(false);\n\n    view! {\n        <button id=\"a-toggle\" on:click=move |_| show_a.set(!show_a.get())>\"Toggle A\"</button>\n        <button id=\"b-toggle\" on:click=move |_| show_b.set(!show_b.get())>\"Toggle B\"</button>\n        <button id=\"c-toggle\" on:click=move |_| show_c.set(!show_c.get())>\"Toggle C\"</button>\n\n        <Show when=move || show_a.get()>\n            <ScenarioA/>\n        </Show>\n        <Show when=move || show_b.get()>\n            <ScenarioB/>\n        </Show>\n        <Show when=move || show_c.get()>\n            <ScenarioC/>\n        </Show>\n    }\n}\n\n#[component]\nfn ScenarioA() -> impl IntoView {\n    // scenario A: one truly-async resource is read on click\n    let counter = RwSignal::new(0);\n    let resource = Resource::new(\n        move || counter.get(),\n        |count| async move {\n            sleep(50).await.unwrap();\n            count\n        },\n    );\n    view! {\n        <Transition fallback=|| view! { <p id=\"a-result\">\"Loading...\"</p> }>\n            <p id=\"a-result\">{resource}</p>\n        </Transition>\n        <button id=\"a-button\" on:click=move |_| *counter.write() += 1>\"+1\"</button>\n    }\n}\n\n#[component]\nfn ScenarioB() -> impl IntoView {\n    // scenario B: resource immediately available first time, then after 250ms\n    let counter = RwSignal::new(0);\n    let resource = Resource::new(\n        move || counter.get(),\n        |count| async move {\n            if count == 0 {\n                count\n            } else {\n                sleep(50).await.unwrap();\n                count\n            }\n        },\n    );\n    view! {\n        <Transition fallback=|| view! { <p id=\"b-result\">\"Loading...\"</p> }>\n            <p id=\"b-result\">{resource}</p>\n        </Transition>\n        <button id=\"b-button\" on:click=move |_| *counter.write() += 1>\"+1\"</button>\n    }\n}\n\n#[component]\nfn ScenarioC() -> impl IntoView {\n    // scenario C: not even a resource on the first run, just a value\n    // see https://github.com/leptos-rs/leptos/issues/3868\n    let counter = RwSignal::new(0);\n    let s_res = StoredValue::new(None::<ArcLocalResource<i32>>);\n    let resource = move || {\n        let count = counter.get();\n        if count == 0 {\n            count\n        } else {\n            let r = s_res.get_value().unwrap_or_else(|| {\n                let res = ArcLocalResource::new(move || async move {\n                    sleep(50).await.unwrap();\n                    count\n                });\n                s_res.set_value(Some(res.clone()));\n                res\n            });\n            r.get().unwrap_or(42)\n        }\n    };\n    view! {\n        <Transition fallback=|| view! { <p id=\"c-result\">\"Loading...\"</p> }>\n            <p id=\"c-result\">{resource}</p>\n        </Transition>\n        <button id=\"c-button\" on:click=move |_| *counter.write() += 1>\"+1\"</button>\n    }\n}\n\n#[server]\nasync fn sleep(ms: u64) -> Result<(), ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_millis(ms)).await;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/regression/src/lib.rs",
    "content": "pub mod app;\nmod issue_4005;\nmod issue_4088;\nmod issue_4217;\nmod issue_4285;\nmod issue_4296;\nmod issue_4324;\nmod issue_4492;\nmod pr_4015;\nmod pr_4091;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/regression/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use leptos::prelude::*;\n    use leptos_axum::{\n        generate_route_list, site_pkg_dir_service, ErrorHandler, LeptosRoutes,\n    };\n    use regression::app::{shell, App};\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let leptos_options = conf.leptos_options;\n    // Generate the list of routes in your Leptos App\n    let routes = generate_route_list(App);\n\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback_service(\n            site_pkg_dir_service(&leptos_options)\n                .fallback(ErrorHandler::new(shell, leptos_options.clone())),\n        )\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    println!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "examples/regression/src/pr_4015.rs",
    "content": "use leptos::{context::Provider, prelude::*};\nuse leptos_router::{\n    components::{ParentRoute, Route},\n    nested_router::Outlet,\n    path,\n};\n\n#[component]\npub fn Routes4015() -> impl leptos_router::MatchNestedRoutes + Clone {\n    view! {\n        <ParentRoute path=path!(\"4015\") view=|| view! {\n            <Provider value=42i32>\n                <Outlet/>\n            </Provider>\n        }>\n            <Route path=path!(\"\") view=Child/>\n        </ParentRoute>\n    }\n    .into_inner()\n}\n\n#[component]\nfn Child() -> impl IntoView {\n    let value = use_context::<i32>();\n\n    view! {\n        <p id=\"result\">{format!(\"{value:?}\")}</p>\n    }\n}\n"
  },
  {
    "path": "examples/regression/src/pr_4091.rs",
    "content": "use leptos::{context::Provider, prelude::*};\nuse leptos_router::{\n    components::{ParentRoute, Route, A},\n    nested_router::Outlet,\n    path,\n};\n\n// FIXME This should be a set rather than a naive vec for push and pop, as\n// it may be possible for unexpected token be popped/pushed on multi-level\n// navigation.  For basic naive tests it should be Fine(TM).\n#[derive(Clone)]\nstruct Expectations(Vec<&'static str>);\n\n#[component]\npub fn Routes4091() -> impl leptos_router::MatchNestedRoutes + Clone {\n    view! {\n        <ParentRoute path=path!(\"4091\") view=Container>\n            <Route path=path!(\"\") view=Root/>\n            <Route path=path!(\"test1\") view=Test1/>\n        </ParentRoute>\n    }\n    .into_inner()\n}\n\n#[component]\nfn Container() -> impl IntoView {\n    let rw_signal = RwSignal::new(Expectations(Vec::new()));\n    provide_context(rw_signal);\n\n    view! {\n        <nav id=\"nav\">\n            <ul>\n                <li><A href=\"/\">\"Home\"</A></li>\n                <li><A href=\"./\">\"4091 Home\"</A></li>\n                <li><A href=\"test1\">\"test1\"</A></li>\n            </ul>\n        </nav>\n        <div id=\"result\">{move || {\n            rw_signal.with(|ex| ex.0.iter().fold(String::new(), |a, b| a + b + \" \"))\n        }}</div>\n        <Provider value=rw_signal>\n            <Outlet/>\n        </Provider>\n    }\n}\n\n#[component]\nfn Root() -> impl IntoView {\n    view! {\n        <div>\"This is Root\"</div>\n    }\n}\n\n#[component]\nfn Test1() -> impl IntoView {\n    let signal = expect_context::<RwSignal<Expectations>>();\n\n    on_cleanup(move || {\n        signal.update(|ex| {\n            ex.0.pop();\n        });\n    });\n\n    view! {\n        {move || signal.update(|ex| ex.0.push(\"Test1\"))}\n        <div>\"This is Test1\"</div>\n    }\n}\n"
  },
  {
    "path": "examples/regression/style/main.scss",
    "content": "body {\n    font-family: sans-serif;\n}\n"
  },
  {
    "path": "examples/router/.cargo/config.toml",
    "content": "[target.wasm32-unknown-unknown]\nrustflags = [\"-C\", \"panic=abort\"]\n"
  },
  {
    "path": "examples/router/.gitignore",
    "content": " Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# Support playwright testing\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\npnpm-lock.yaml\n\n# Support trunk\ndist\n\n"
  },
  {
    "path": "examples/router/Cargo.toml",
    "content": "[package]\nname = \"router\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.dev]\npanic = \"abort\"\n\n[profile.release]\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[dependencies]\nconsole_log = \"1.0\"\nleptos = { path = \"../../leptos\", features = [\"csr\", \"tracing\"] }\nleptos_router = { path = \"../../router\" }                         #, features = [\"tracing\"] }\nleptos_router_macro = { path = \"../../router_macro\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\nfutures = \"0.3.30\"\nconsole_error_panic_hook = \"0.1.7\"\ntracing-subscriber = \"0.3.18\"\ntracing-subscriber-wasm = \"0.1.0\"\ntracing = \"0.1.40\"\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.50\"\n"
  },
  {
    "path": "examples/router/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/trunk_server.toml\" },\n  { path = \"../cargo-make/playwright-test.toml\" },\n]\n"
  },
  {
    "path": "examples/router/README.md",
    "content": "# Leptos Router Example\n\nThis example demonstrates how Leptos’s router works for client side routing.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/router/e2e/package.json",
    "content": "{\n  \"private\": \"true\",\n  \"scripts\": {},\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.35.1\"\n  }\n}\n"
  },
  {
    "path": "examples/router/e2e/playwright.config.ts",
    "content": "import { defineConfig, devices } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://github.com/motdotla/dotenv\n */\n// require('dotenv').config();\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nexport default defineConfig({\n  testDir: \"./tests\",\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !!process.env.CI,\n  /* Retry on CI only */\n  retries: process.env.CI ? 10 : 10,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.CI ? 1 : undefined,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: \"list\",\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    baseURL: \"http://127.0.0.1:8080\",\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: \"on-first-retry\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"chromium\",\n      use: { ...devices[\"Desktop Chrome\"] },\n    },\n\n    // {\n    //   name: \"firefox\",\n    //   use: { ...devices[\"Desktop Firefox\"] },\n    // },\n\n    // {\n    //   name: \"webkit\",\n    //   use: { ...devices[\"Desktop Safari\"] },\n    // },\n\n    /* Test against mobile viewports. */\n    // {\n    //   name: 'Mobile Chrome',\n    //   use: { ...devices['Pixel 5'] },\n    // },\n    // {\n    //   name: 'Mobile Safari',\n    //   use: { ...devices['iPhone 12'] },\n    // },\n\n    /* Test against branded browsers. */\n    // {\n    //   name: 'Microsoft Edge',\n    //   use: { ...devices['Desktop Edge'], channel: 'msedge' },\n    // },\n    // {\n    //   name: 'Google Chrome',\n    //   use: { ..devices['Desktop Chrome'], channel: 'chrome' },\n    // },\n  ],\n\n  /* Run your local dev server before starting the tests */\n  // webServer: {\n  //   command: \"cd ../ && trunk serve\",\n  //   url: \"http://127.0.0.1:8080\",\n  //   reuseExistingServer: false, //!process.env.CI,\n  // },\n});\n"
  },
  {
    "path": "examples/router/e2e/tests/router.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest.describe(\"Test Router example\", () => {\n  test.beforeEach(async ({ page }) => {\n    await page.goto(\"/\");\n  });\n\n  test(\"Starts on correct home page\", async({ page }) => {  \n       await expect(page.getByText(\"Select a contact.\")).toBeVisible();\n  });\n\n  const links = [\n    { label: \"Bill Smith\", url: \"/0\" },\n    { label: \"Tim Jones\", url: \"/1\" },\n    { label: \"Sally Stevens\", url: \"/2\" },\n    { label: \"About\", url: \"/about\" },\n    { label: \"Settings\", url: \"/settings\" },\n  ];\n  links.forEach(({ label, url }) => {\n    test(`Can navigate to ${label}`, async ({ page }) => {\n      await page.getByRole(\"link\", { name: label }).click();\n\n      await expect(page.getByRole(\"heading\", { name: label })).toBeVisible();\n      await expect(page).toHaveURL(url);\n    });\n  });\n\n  test(\"Can redirect to home\", async ({ page }) => {\n    await page.getByRole(\"link\", { name: \"About\" }).click();\n\n    await page.getByRole(\"link\", { name: \"Redirect to Home\" }).click();\n    await expect(page).toHaveURL(\"/\");\n  });\n});\n"
  },
  {
    "path": "examples/router/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t\t<link data-trunk rel=\"css\" href=\"style.css\"/>\n\t</head>\n\t<body></body>\n</html>\n"
  },
  {
    "path": "examples/router/package.json",
    "content": "{\n  \"private\": true,\n  \"scripts\": {\n    \"start-server\": \"trunk serve\",\n    \"e2e\": \"cargo make test-playwright\",\n    \"e2e:auto-start\": \"start-server-and-test start-server http://127.0.0.1:8080 e2e\"\n  },\n  \"devDependencies\": {\n    \"start-server-and-test\": \"^2.0.0\"\n  }\n}\n"
  },
  {
    "path": "examples/router/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\ncomponents = [\"rust-src\"]\n"
  },
  {
    "path": "examples/router/src/api.rs",
    "content": "use futures::{\n    channel::oneshot::{self, Canceled},\n    Future,\n};\nuse leptos::leptos_dom::helpers::set_timeout;\nuse serde::{Deserialize, Serialize};\nuse std::time::Duration;\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct ContactSummary {\n    pub id: usize,\n    pub first_name: String,\n    pub last_name: String,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct Contact {\n    pub id: usize,\n    pub first_name: String,\n    pub last_name: String,\n    pub address_1: String,\n    pub address_2: String,\n    pub city: String,\n    pub state: String,\n    pub zip: String,\n    pub email: String,\n    pub phone: String,\n}\n\npub async fn get_contacts(_search: String) -> Vec<ContactSummary> {\n    // fake an API call with an artificial delay\n    _ = delay(Duration::from_millis(300)).await;\n    vec![\n        ContactSummary {\n            id: 0,\n            first_name: \"Bill\".into(),\n            last_name: \"Smith\".into(),\n        },\n        ContactSummary {\n            id: 1,\n            first_name: \"Tim\".into(),\n            last_name: \"Jones\".into(),\n        },\n        ContactSummary {\n            id: 2,\n            first_name: \"Sally\".into(),\n            last_name: \"Stevens\".into(),\n        },\n    ]\n}\n\npub async fn get_contact(id: Option<usize>) -> Option<Contact> {\n    // fake an API call with an artificial delay\n    _ = delay(Duration::from_millis(500)).await;\n    match id {\n        Some(0) => Some(Contact {\n            id: 0,\n            first_name: \"Bill\".into(),\n            last_name: \"Smith\".into(),\n            address_1: \"12 Mulberry Lane\".into(),\n            address_2: \"\".into(),\n            city: \"Boston\".into(),\n            state: \"MA\".into(),\n            zip: \"02129\".into(),\n            email: \"bill@smith.com\".into(),\n            phone: \"617-121-1221\".into(),\n        }),\n        Some(1) => Some(Contact {\n            id: 1,\n            first_name: \"Tim\".into(),\n            last_name: \"Jones\".into(),\n            address_1: \"56 Main Street\".into(),\n            address_2: \"\".into(),\n            city: \"Chattanooga\".into(),\n            state: \"TN\".into(),\n            zip: \"13371\".into(),\n            email: \"timjones@lmail.com\".into(),\n            phone: \"232-123-1337\".into(),\n        }),\n        Some(2) => Some(Contact {\n            id: 2,\n            first_name: \"Sally\".into(),\n            last_name: \"Stevens\".into(),\n            address_1: \"404 E 123rd St\".into(),\n            address_2: \"Apt 7E\".into(),\n            city: \"New York\".into(),\n            state: \"NY\".into(),\n            zip: \"10082\".into(),\n            email: \"sally.stevens@wahoo.net\".into(),\n            phone: \"242-121-3789\".into(),\n        }),\n        _ => None,\n    }\n}\n\nfn delay(\n    duration: Duration,\n) -> impl Future<Output = Result<(), Canceled>> + Send {\n    let (tx, rx) = oneshot::channel();\n    set_timeout(\n        move || {\n            _ = tx.send(());\n        },\n        duration,\n    );\n    rx\n}\n"
  },
  {
    "path": "examples/router/src/lib.rs",
    "content": "mod api;\nuse crate::api::*;\nuse leptos::{either::Either, prelude::*};\nuse leptos_router::{\n    components::{\n        Form, Outlet, ParentRoute, ProtectedRoute, Redirect, Route, Router,\n        Routes, RoutingProgress, A,\n    },\n    hooks::{use_navigate, use_params, use_query_map},\n    params::Params,\n};\nuse leptos_router_macro::path;\nuse std::time::Duration;\nuse tracing::info;\n\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\nstruct ExampleContext(i32);\n\n#[component]\npub fn RouterExample() -> impl IntoView {\n    info!(\"rendering <RouterExample/>\");\n\n    // contexts are passed down through the route tree\n    provide_context(ExampleContext(0));\n\n    // this signal will be used to set whether we are allowed to access a protected route\n    let (logged_in, set_logged_in) = signal(true);\n    let (is_routing, set_is_routing) = signal(false);\n\n    view! {\n        <Router set_is_routing>\n            // shows a progress bar while async data are loading\n            <div class=\"routing-progress\">\n                <RoutingProgress is_routing max_time=Duration::from_millis(250) />\n            </div>\n            <nav>\n                // ordinary <a> elements can be used for client-side navigation\n                // using <A> has two effects:\n                // 1) ensuring that relative routing works properly for nested routes\n                // 2) setting the `aria-current` attribute on the current link,\n                // for a11y and styling purposes\n                <A href=\"/\">\"Contacts\"</A>\n                <A href=\"/about\">\"About\"</A>\n                <A href=\"/settings\">\"Settings\"</A>\n                <A href=\"/redirect-home\">\"Redirect to Home\"</A>\n                <button on:click=move |_| {\n                    set_logged_in.update(|n| *n = !*n)\n                }>{move || if logged_in.get() { \"Log Out\" } else { \"Log In\" }}</button>\n            </nav>\n            <main>\n                <Routes transition=true fallback=|| \"This page could not be found.\">\n                    // paths can be created using the path!() macro, or provided as types like\n                    // StaticSegment(\"about\")\n                    <Route path=path!(\"about\") view=About />\n                    <ProtectedRoute\n                        path=path!(\"settings\")\n                        condition=move || Some(logged_in.get())\n                        redirect_path=|| \"/\"\n                        view=Settings\n                    />\n                    <Route path=path!(\"redirect-home\") view=|| view! { <Redirect path=\"/\" /> } />\n                    <ContactRoutes />\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n// You can define other routes in their own component.\n// Routes implement the MatchNestedRoutes\n#[component(transparent)]\npub fn ContactRoutes() -> impl leptos_router::MatchNestedRoutes + Clone {\n    view! {\n        <ParentRoute path=path!(\"\") view=ContactList>\n            <Route path=path!(\"/\") view=|| \"Select a contact.\" />\n            <Route path=path!(\"/:id\") view=Contact />\n        </ParentRoute>\n    }\n    .into_inner()\n}\n\n#[component]\npub fn ContactList() -> impl IntoView {\n    info!(\"rendering <ContactList/>\");\n\n    // contexts are passed down through the route tree\n    provide_context(ExampleContext(42));\n\n    Owner::on_cleanup(|| {\n        info!(\"cleaning up <ContactList/>\");\n    });\n\n    let query = use_query_map();\n    let search = Memo::new(move |_| query.read().get(\"q\").unwrap_or_default());\n    let contacts = AsyncDerived::new(move || {\n        leptos::logging::log!(\"reloading contacts\");\n        get_contacts(search.get())\n    });\n    let contacts = move || {\n        Suspend::new(async move {\n            // this data doesn't change frequently so we can use .map().collect() instead of a keyed <For/>\n            contacts.await\n                .into_iter()\n                .map(|contact| {\n                    view! {\n                        <li>\n                            <A href=contact.id.to_string()>\n                                <span>{contact.first_name} \" \" {contact.last_name}</span>\n                            </A>\n                        </li>\n                    }\n                })\n                .collect::<Vec<_>>()\n        })\n    };\n\n    view! {\n        <div class=\"contact-list\">\n            <h1>\"Contacts\"</h1>\n            <Suspense fallback=move || view! { <p>\"Loading contacts...\"</p> }>\n                <ul>{contacts}</ul>\n            </Suspense>\n            <Outlet />\n        </div>\n    }\n}\n\n#[derive(Params, PartialEq, Clone, Debug)]\npub struct ContactParams {\n    // Params isn't implemented for usize, only Option<usize>\n    id: Option<usize>,\n}\n\n#[component]\npub fn Contact() -> impl IntoView {\n    info!(\"rendering <Contact/>\");\n\n    info!(\n        \"ExampleContext should be Some(42). It is {:?}\",\n        use_context::<ExampleContext>()\n    );\n\n    Owner::on_cleanup(|| {\n        info!(\"cleaning up <Contact/>\");\n    });\n\n    let params = use_params::<ContactParams>();\n\n    let contact = AsyncDerived::new(move || {\n        get_contact(\n            params\n                .get()\n                .map(|params| params.id.unwrap_or_default())\n                .ok(),\n        )\n    });\n\n    let contact_display = move || {\n        Suspend::new(async move {\n            match contact.await {\n                None => Either::Left(\n                    view! { <p>\"No contact with this ID was found.\"</p> },\n                ),\n                Some(contact) => Either::Right(view! {\n                    <section class=\"card\">\n                        <h1>{contact.first_name} \" \" {contact.last_name}</h1>\n                        <p>{contact.address_1} <br /> {contact.address_2}</p>\n                    </section>\n                }),\n            }\n        })\n    };\n\n    view! {\n        <div class=\"contact\">\n            <Transition fallback=move || {\n                view! { <p>\"Loading...\"</p> }\n            }>{contact_display}</Transition>\n        </div>\n    }\n}\n\n#[component]\npub fn About() -> impl IntoView {\n    info!(\"rendering <About/>\");\n\n    Owner::on_cleanup(|| {\n        info!(\"cleaning up <About/>\");\n    });\n\n    info!(\n        \"ExampleContext should be Some(0). It is {:?}\",\n        use_context::<ExampleContext>()\n    );\n\n    // use_navigate allows you to navigate programmatically by calling a function\n    let navigate = use_navigate();\n\n    // note: this is just an illustration of how to use `use_navigate`\n    // <button on:click> to navigate is an *anti-pattern*\n    // you should ordinarily use a link instead,\n    // both semantically and so your link will work before WASM loads\n    view! {\n        <button on:click=move |_| navigate(\"/\", Default::default())>\"Home\"</button>\n        <h1>\"About\"</h1>\n        <p>\n            \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\"\n        </p>\n    }\n}\n\n#[component]\npub fn Settings() -> impl IntoView {\n    info!(\"rendering <Settings/>\");\n\n    Owner::on_cleanup(|| {\n        info!(\"cleaning up <Settings/>\");\n    });\n\n    view! {\n        <h1>\"Settings\"</h1>\n        <Form action=\"\">\n            <fieldset>\n                <legend>\"Name\"</legend>\n                <input type=\"text\" name=\"first_name\" placeholder=\"First\" />\n                <input type=\"text\" name=\"last_name\" placeholder=\"Last\" />\n            </fieldset>\n            <input type=\"submit\" />\n            <p>\n                \"This uses the \" <code>\"<Form/>\"</code>\n                \" component, which enhances forms by using client-side navigation for \"\n                <code>\"GET\"</code> \" requests, and client-side requests for \" <code>\"POST\"</code>\n                \" requests, without requiring a full page reload.\"\n            </p>\n        </Form>\n    }\n}\n"
  },
  {
    "path": "examples/router/src/main.rs",
    "content": "use leptos::prelude::*;\nuse router::*;\nuse tracing_subscriber::fmt;\nuse tracing_subscriber_wasm::MakeConsoleWriter;\n\npub fn main() {\n    fmt()\n        .with_writer(\n            MakeConsoleWriter::default()\n                .map_trace_level_to(tracing::Level::DEBUG),\n        )\n        .without_time()\n        .init();\n    console_error_panic_hook::set_once();\n    mount_to_body(RouterExample);\n}\n"
  },
  {
    "path": "examples/router/style.css",
    "content": ".routing-progress {\n\twidth: 100%;\n\theight: 20px;\n}\n\na[aria-current] {\n    font-weight: bold;\n}\n\n.outlet {\n  border: 1px dotted grey;\n}\n\n.contact, .contact-list {\n    border: 1px solid #c0c0c0;\n    border-radius: 3px;\n    padding: 1rem;\n}\n\n.contact {\n\tview-transition-name: contact;\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes fadeOut {\n  from {\n    opacity: 1;\n  }\n\n  to {\n    opacity: 0;\n  }\n}\n\n.router-outlet-0 main {\n  view-transition-name: main;\n}\n\n.router-back main {\n  view-transition-name: main-back;\n}\n\n.router-outlet-1 .contact-list {\n  view-transition-name: contact;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n\t::view-transition-old(contact) {\n\t  animation: 0.5s fadeOut;\n\t}\n\n\t::view-transition-new(contact) {\n\t  animation: 0.5s fadeIn;\n\t}\n\n\t::view-transition-old(main) {\n\t  animation: 0.5s slideOut;\n\t}\n\n\t::view-transition-new(main) {\n\t  animation: 0.5s slideIn;\n\t}\n\n\t::view-transition-old(main-back) {\n\t\tcolor: red;\n\t\tanimation: 0.5s slideOutBack;\n\t}\n\n\t::view-transition-new(main-back) {\n\t\tcolor: blue;\n\t\tanimation: 0.5s slideInBack;\n\t}\n}\n\n@keyframes slideIn {\n  from {\n    transform: translate(100vw, 0);\n  }\n  to {\n    transform: translate(0px, 0px);\n  }\n}\n\n@keyframes slideOut {\n  from {\n    transform: translate(0px, 0px);\n  }\n  to {\n    transform: translate(-100vw, 0);\n  }\n}\n\n@keyframes slideInBack {\n  from {\n    transform: translate(-100vw, 0);\n  }\n\n  to {\n    transform: translate(0px, 0px);\n  }\n}\n\n@keyframes slideOutBack {\n  from {\n    transform: translate(0px, 0px);\n  }\n\n  to {\n    transform: translate(100vw, 0);\n  }\n}\n"
  },
  {
    "path": "examples/server_fns_axum/Cargo.toml",
    "content": "[package]\nname = \"server_fns_axum\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.30\"\nhttp = \"1.1\"\nleptos = { path = \"../../leptos\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nserver_fn = { path = \"../../server_fn\", features = [\n  \"serde-lite\",\n  \"rkyv\",\n  \"multipart\",\n  \"postcard\",\n] }\nlog = \"0.4.22\"\nsimple_logger = \"5.0\"\nserde = { version = \"1.0\", features = [\"derive\"] }\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.5.2\", optional = true }\ntower-http = { version = \"0.6.2\", features = [\n  \"fs\",\n  \"tracing\",\n  \"trace\",\n], optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nthiserror = \"2.0.12\"\nwasm-bindgen = \"0.2.93\"\nserde_toml = \"0.0.1\"\ntoml = \"0.8.19\"\nweb-sys = { version = \"0.3.70\", features = [\"FileList\", \"File\"] }\nstrum = { version = \"0.27.1\", features = [\"strum_macros\", \"derive\"] }\nnotify = { version = \"8.0\", optional = true }\npin-project-lite = \"0.2.14\"\nasync-broadcast = { version = \"0.7.1\", optional = true }\nbytecheck = \"0.8.0\"\nrkyv = { version = \"0.8.8\" }\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"dep:leptos_axum\",\n  \"dep:notify\",\n  \"dep:async-broadcast\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"server_fns_axum\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"cargo make test-ui\"\nend2end-dir = \"e2e\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/server_fns_axum/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/server_fns_axum/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"server_fns_axum\"\n"
  },
  {
    "path": "examples/server_fns_axum/README.md",
    "content": "# Leptos Todo App Sqlite with Axum\n\nThis example creates a basic todo app with an Axum backend that uses Leptos' server functions to call sqlx from the client and seamlessly run it on the server.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## E2E Testing\n\nSee the [E2E README](./e2e/README.md) for more information about the testing strategy.\n\n## Rendering\n\nSee the [SSR Notes](../SSR_NOTES.md) for more information about Server Side Rendering.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/server_fns_axum/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/server_fns_axum/src/app.rs",
    "content": "use futures::{Sink, Stream, StreamExt};\nuse http::Method;\nuse leptos::{html::Input, prelude::*, task::spawn_local};\nuse serde::{de::DeserializeOwned, Deserialize, Serialize};\nuse server_fn::{\n    client::{browser::BrowserClient, Client},\n    codec::{\n        Encoding, FromReq, FromRes, GetUrl, IntoReq, IntoRes, MultipartData,\n        MultipartFormData, Postcard, Rkyv, RkyvEncoding, SerdeLite,\n        StreamingText, TextStream,\n    },\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    request::{browser::BrowserRequest, ClientReq, Req},\n    response::{browser::BrowserResponse, ClientRes, TryRes},\n    ContentType, Format, FormatType,\n};\nuse std::future::Future;\n#[cfg(feature = \"ssr\")]\nuse std::sync::{\n    atomic::{AtomicU8, Ordering},\n    Mutex,\n};\nuse strum::{Display, EnumString};\nuse wasm_bindgen::JsCast;\nuse web_sys::{FormData, HtmlFormElement, SubmitEvent};\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\" />\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n                <AutoReload options=options.clone() />\n                <HydrationScripts options />\n                <meta name=\"color-scheme\" content=\"dark light\" />\n                <link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\" />\n                <link rel=\"stylesheet\" id=\"leptos\" href=\"/pkg/server_fns_axum.css\" />\n            </head>\n            <body>\n                <App />\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    view! {\n        <header>\n            <h1>\"Server Function Demo\"</h1>\n        </header>\n        <main>\n            <HomePage />\n        </main>\n    }\n}\n\n#[component]\npub fn HomePage() -> impl IntoView {\n    view! {\n        <h2>\"Some Simple Server Functions\"</h2>\n        <SpawnLocal />\n        <WithAnAction />\n        <WithActionForm />\n        <h2>\"Custom Error Types\"</h2>\n        <CustomErrorTypes />\n        <h2>\"Alternative Encodings\"</h2>\n        <ServerFnArgumentExample />\n        <RkyvExample />\n        <PostcardExample />\n        <FileUpload />\n        <FileUploadWithProgress />\n        <FileWatcher />\n        <CustomEncoding />\n        <CustomClientExample />\n    }\n}\n\n/// A server function is really just an API call to your server. But it provides a plain async\n/// function as a wrapper around that. This means you can call it like any other async code, just\n/// by spawning a task with `spawn_local`.\n///\n/// In reality, you usually want to use a resource to load data from the server or an action to\n/// mutate data on the server. But a simple `spawn_local` can make it more obvious what's going on.\n#[component]\npub fn SpawnLocal() -> impl IntoView {\n    /// A basic server function can be called like any other async function.\n    ///\n    /// You can define a server function at any scope. This one, for example, is only available\n    /// inside the SpawnLocal component. **However**, note that all server functions are publicly\n    /// available API endpoints: This scoping means you can only call this server function\n    /// from inside this component, but it is still available at its URL to any caller, from within\n    /// your app or elsewhere.\n    #[server]\n    pub async fn shouting_text(input: String) -> Result<String, ServerFnError> {\n        // insert a simulated wait\n        tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n        Ok(input.to_ascii_uppercase())\n    }\n\n    let input_ref = NodeRef::<Input>::new();\n    let (shout_result, set_shout_result) = signal(\"Click me\".to_string());\n\n    view! {\n        <h3>Using <code>spawn_local</code></h3>\n        <p>\n            \"You can call a server function by using \" <code>\"spawn_local\"</code>\n            \" in an event listener. \"\n            \"Clicking this button should alert with the uppercase version of the input.\"\n        </p>\n        <input node_ref=input_ref placeholder=\"Type something here.\" />\n        <button on:click=move |_| {\n            let value = input_ref.get().unwrap().value();\n            spawn_local(async move {\n                let uppercase_text = shouting_text(value).await.unwrap_or_else(|e| e.to_string());\n                set_shout_result.set(uppercase_text);\n            });\n        }>\n\n            {shout_result}\n        </button>\n    }\n}\n\n/// Pretend this is a database and we're storing some rows in memory!\n/// This exists only on the server.\n#[cfg(feature = \"ssr\")]\nstatic ROWS: Mutex<Vec<String>> = Mutex::new(Vec::new());\n\n/// Imagine this server function mutates some state on the server, like a database row.\n/// Every third time, it will return an error.\n///\n/// This kind of mutation is often best handled by an Action.\n/// Remember, if you're loading data, use a resource; if you're running an occasional action,\n/// use an action.\n#[server]\npub async fn add_row(text: String) -> Result<usize, ServerFnError> {\n    static N: AtomicU8 = AtomicU8::new(0);\n\n    // insert a simulated wait\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n\n    let nth_run = N.fetch_add(1, Ordering::Relaxed);\n    // this will print on the server, like any server function\n    println!(\"Adding {text:?} to the database!\");\n    if nth_run % 3 == 2 {\n        Err(ServerFnError::new(\"Oh no! Couldn't add to database!\"))\n    } else {\n        let mut rows = ROWS.lock().unwrap();\n        rows.push(text);\n        Ok(rows.len())\n    }\n}\n\n/// Simply returns the number of rows.\n#[server]\npub async fn get_rows() -> Result<usize, ServerFnError> {\n    // insert a simulated wait\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n\n    Ok(ROWS.lock().unwrap().len())\n}\n\n/// An action abstracts over the process of spawning a future and setting a signal when it\n/// resolves. Its .input() signal holds the most recent argument while it's still pending,\n/// and its .value() signal holds the most recent result. Its .version() signal can be fed\n/// into a resource, telling it to refetch whenever the action has successfully resolved.\n///\n/// This makes actions useful for mutations, i.e., some server function that invalidates\n/// loaded previously loaded from another server function.\n#[component]\npub fn WithAnAction() -> impl IntoView {\n    let input_ref = NodeRef::<Input>::new();\n\n    // a server action can be created by using the server function's type name as a generic\n    // the type name defaults to the PascalCased function name\n    let action = ServerAction::<AddRow>::new();\n\n    // this resource will hold the total number of rows\n    // passing it action.version() means it will refetch whenever the action resolves successfully\n    let row_count =\n        Resource::new(move || action.version().get(), |_| get_rows());\n\n    view! {\n        <h3>Using <code>Action::new</code></h3>\n        <p>\n            \"Some server functions are conceptually \\\"mutations,\\\", which change something on the server. \"\n            \"These often work well as actions.\"\n        </p>\n        <input node_ref=input_ref placeholder=\"Type something here.\" />\n        <button on:click=move |_| {\n            let text = input_ref.get().unwrap().value();\n            action.dispatch(text.into());\n        }>\n\n            Submit\n        </button>\n        <p>You submitted: {move || format!(\"{:?}\", action.input().get())}</p>\n        <p>The result was: {move || format!(\"{:?}\", action.value().get())}</p>\n        <Transition>\n            <p>Total rows: {row_count}</p>\n        </Transition>\n    }\n}\n\n/// An <ActionForm/> lets you do the same thing as dispatching an action, but automates the\n/// creation of the dispatched argument struct using a <form>. This means it also gracefully\n/// degrades well when JS/WASM are not available.\n///\n/// Try turning off WASM in your browser. The form still works, and successfully displays the error\n/// message if the server function returns an error. Otherwise, it loads the new resource data.\n#[component]\npub fn WithActionForm() -> impl IntoView {\n    let action = ServerAction::<AddRow>::new();\n    let row_count =\n        Resource::new(move || action.version().get(), |_| get_rows());\n\n    view! {\n        <h3>Using <code>\"<ActionForm/>\"</code></h3>\n        <p>\n            <code>\"<ActionForm/>\"</code>\n            \"lets you use an HTML \"\n            <code>\"<form>\"</code>\n            \"to call a server function in a way that gracefully degrades.\"\n        </p>\n        <ActionForm action>\n            <input\n                // the `name` of the input corresponds to the argument name\n                name=\"text\"\n                placeholder=\"Type something here.\"\n            />\n            <button>Submit</button>\n        </ActionForm>\n        <p>You submitted: {move || format!(\"{:?}\", action.input().get())}</p>\n        <p>The result was: {move || format!(\"{:?}\", action.value().get())}</p>\n        <Transition>\n            archive underaligned: need alignment 4 but have alignment 1\n            <p>Total rows: {row_count}</p>\n        </Transition>\n    }\n}\n\n/// The plain `#[server]` macro gives sensible defaults for the settings needed to create a server\n/// function, but those settings can also be customized. For example, you can set a specific unique\n/// path rather than the hashed path, or you can choose a different combination of input and output\n/// encodings.\n///\n/// Arguments to the server macro can be specified as named key-value pairs, like `name = value`.\n#[server(\n    // this server function will be exposed at /api2/custom_path\n    prefix = \"/api2\",\n    endpoint = \"custom_path\",\n    // it will take its arguments as a URL-encoded GET request (useful for caching)\n    input = GetUrl,\n    // it will return its output using SerdeLite\n    // (this needs to be enabled with the `serde-lite` feature on the `server_fn` crate\n    output = SerdeLite,\n)]\n// You can use the `#[middleware]` macro to add appropriate middleware\n// In this case, any `tower::Layer` that takes services of `Request<Body>` will work\n#[middleware(crate::middleware::LoggingLayer)]\npub async fn length_of_input(input: String) -> Result<usize, ServerFnError> {\n    println!(\"2. Running server function.\");\n    // insert a simulated wait\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n    Ok(input.len())\n}\n\n#[component]\npub fn ServerFnArgumentExample() -> impl IntoView {\n    let input_ref = NodeRef::<Input>::new();\n    let (result, set_result) = signal(0);\n\n    view! {\n        <h3>Custom arguments to the <code>#[server]</code> \" macro\"</h3>\n        <p>This example shows how to specify additional behavior, including:</p>\n        <ul>\n            <li>Specific server function <strong>paths</strong></li>\n            <li>Mixing and matching input and output <strong>encodings</strong></li>\n            <li>Adding custom <strong>middleware</strong>on a per-server-fn basis</li>\n        </ul>\n        <input node_ref=input_ref placeholder=\"Type something here.\" />\n        <button on:click=move |_| {\n            let value = input_ref.get().unwrap().value();\n            spawn_local(async move {\n                let length = length_of_input(value).await.unwrap_or(0);\n                set_result.set(length);\n            });\n        }>\n\n            Click to see length\n        </button>\n        <p>Length is {result}</p>\n    }\n}\n\n/// `server_fn` supports a wide variety of input and output encodings, each of which can be\n/// referred to as a PascalCased struct name\n/// - Toml\n/// - Cbor\n/// - Rkyv\n/// - etc.\n#[server(\n    input = Rkyv,\n    output = Rkyv\n)]\npub async fn rkyv_example(input: String) -> Result<String, ServerFnError> {\n    // insert a simulated wait\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n    Ok(input.to_ascii_uppercase())\n}\n\n#[component]\npub fn RkyvExample() -> impl IntoView {\n    let input_ref = NodeRef::<Input>::new();\n    let (input, set_input) = signal(String::new());\n    let rkyv_result = Resource::new(move || input.get(), rkyv_example);\n\n    view! {\n        <h3>Using <code>rkyv</code>encoding</h3>\n        <input node_ref=input_ref placeholder=\"Type something here.\" />\n        <button on:click=move |_| {\n            let value = input_ref.get().unwrap().value();\n            set_input.set(value);\n        }>\n\n            Click to capitalize\n        </button>\n        <p>{input}</p>\n        <Transition>{rkyv_result}</Transition>\n    }\n}\n\n#[component]\npub fn FileUpload() -> impl IntoView {\n    /// A simple file upload function, which does just returns the length of the file.\n    ///\n    /// On the server, this uses the `multer` crate, which provides a streaming API.\n    #[server(\n        input = MultipartFormData,\n    )]\n    pub async fn file_length(\n        data: MultipartData,\n    ) -> Result<usize, ServerFnError> {\n        // `.into_inner()` returns the inner `multer` stream\n        // it is `None` if we call this on the client, but always `Some(_)` on the server, so is safe to\n        // unwrap\n        let mut data = data.into_inner().unwrap();\n\n        // this will just measure the total number of bytes uploaded\n        let mut count = 0;\n        while let Ok(Some(mut field)) = data.next_field().await {\n            println!(\"\\n[NEXT FIELD]\\n\");\n            let name = field.name().unwrap_or_default().to_string();\n            println!(\"  [NAME] {name}\");\n            while let Ok(Some(chunk)) = field.chunk().await {\n                let len = chunk.len();\n                count += len;\n                println!(\"      [CHUNK] {len}\");\n                // in a real server function, you'd do something like saving the file here\n            }\n        }\n\n        Ok(count)\n    }\n\n    let upload_action = Action::new_local(|data: &FormData| {\n        // `MultipartData` implements `From<FormData>`\n        file_length(data.clone().into())\n    });\n\n    view! {\n        <h3>File Upload</h3>\n        <p>Uploading files is fairly easy using multipart form data.</p>\n        <form on:submit=move |ev: SubmitEvent| {\n            ev.prevent_default();\n            let target = ev.target().unwrap().unchecked_into::<HtmlFormElement>();\n            let form_data = FormData::new_with_form(&target).unwrap();\n            upload_action.dispatch_local(form_data);\n        }>\n            <input type=\"file\" name=\"file_to_upload\" />\n            <input type=\"submit\" />\n        </form>\n        <p>\n            {move || {\n                if upload_action.input().read().is_none() && upload_action.value().read().is_none()\n                {\n                    \"Upload a file.\".to_string()\n                } else if upload_action.pending().get() {\n                    \"Uploading...\".to_string()\n                } else if let Some(Ok(value)) = upload_action.value().get() {\n                    value.to_string()\n                } else {\n                    format!(\"{:?}\", upload_action.value().get())\n                }\n            }}\n\n        </p>\n    }\n}\n\n/// This component uses server functions to upload a file, while streaming updates on the upload\n/// progress.\n#[component]\npub fn FileUploadWithProgress() -> impl IntoView {\n    /// In theory, you could create a single server function which\n    /// 1) received multipart form data\n    /// 2) returned a stream that contained updates on the progress\n    ///\n    /// In reality, browsers do not actually support duplexing requests in this way. In other\n    /// words, every existing browser actually requires that the request stream be complete before\n    /// it begins processing the response stream.\n    ///\n    /// Instead, we can create two separate server functions:\n    /// 1) one that receives multipart form data and begins processing the upload\n    /// 2) a second that returns a stream of updates on the progress\n    ///\n    /// This requires us to store some global state of all the uploads. In a real app, you probably\n    /// shouldn't do exactly what I'm doing here in the demo. For example, this map just\n    /// distinguishes between files by filename, not by user.\n    #[cfg(feature = \"ssr\")]\n    mod progress {\n        use async_broadcast::{broadcast, Receiver, Sender};\n        use futures::Stream;\n        use std::{\n            collections::HashMap,\n            sync::{LazyLock, RwLock},\n        };\n\n        struct File {\n            total: usize,\n            tx: Sender<usize>,\n            rx: Receiver<usize>,\n        }\n\n        static FILES: LazyLock<RwLock<HashMap<String, File>>> =\n            LazyLock::new(|| RwLock::new(HashMap::new()));\n\n        pub async fn add_chunk(filename: &str, len: usize) {\n            println!(\"[{filename}]\\tadding {len}\");\n            let (tx, new_total) = {\n                let mut lock = FILES.write().unwrap();\n                let entry =\n                    lock.entry(filename.to_string()).or_insert_with(|| {\n                        println!(\"[{filename}]\\tinserting channel\");\n                        // NOTE: this channel capacity is set arbitrarily for this demo code.\n                        // it allows for up to exactly 1048 chunks to be sent, which sets an upper cap\n                        // on upload size (the precise details vary by client)\n                        // in a real system, you will want to create some more reasonable ways of\n                        // sending and sharing notifications\n                        //\n                        // see https://github.com/leptos-rs/leptos/issues/4397 for related discussion\n                        let (tx, rx) = broadcast(1048);\n                        File { total: 0, tx, rx }\n                    });\n                entry.total += len;\n                let new_total = entry.total;\n\n                (entry.tx.clone(), new_total)\n            };\n\n            // now we send the message and don't have to worry about it\n            tx.broadcast(new_total)\n                .await\n                .expect(\"couldn't send a message over channel\");\n        }\n\n        pub fn for_file(filename: &str) -> impl Stream<Item = usize> {\n            let mut lock = FILES.write().unwrap();\n            let entry = lock.entry(filename.to_string()).or_insert_with(|| {\n                println!(\"[{filename}]\\tinserting channel\");\n                let (tx, rx) = broadcast(128);\n                File { total: 0, tx, rx }\n            });\n            entry.rx.clone()\n        }\n    }\n\n    #[server(\n        input = MultipartFormData,\n    )]\n    pub async fn upload_file(data: MultipartData) -> Result<(), ServerFnError> {\n        let mut data = data.into_inner().unwrap();\n\n        while let Ok(Some(mut field)) = data.next_field().await {\n            let name =\n                field.file_name().expect(\"no filename on field\").to_string();\n            while let Ok(Some(chunk)) = field.chunk().await {\n                let len = chunk.len();\n                println!(\"[{name}]\\t{len}\");\n                progress::add_chunk(&name, len).await;\n                // in a real server function, you'd do something like saving the file here\n            }\n        }\n\n        Ok(())\n    }\n\n    #[server(output = StreamingText)]\n    pub async fn file_progress(\n        filename: String,\n    ) -> Result<TextStream, ServerFnError> {\n        println!(\"getting progress on {filename}\");\n        // get the stream of current length for the file\n        let progress = progress::for_file(&filename);\n        // separate each number with a newline\n        // the HTTP response might pack multiple lines of this into a single chunk\n        // we need some way of dividing them up\n        let progress = progress.map(|bytes| Ok(format!(\"{bytes}\\n\")));\n        Ok(TextStream::new(progress))\n    }\n\n    let (filename, set_filename) = signal(None);\n    let (max, set_max) = signal(None);\n    let (current, set_current) = signal(None);\n    let on_submit = move |ev: SubmitEvent| {\n        ev.prevent_default();\n        let target = ev.target().unwrap().unchecked_into::<HtmlFormElement>();\n        let form_data = FormData::new_with_form(&target).unwrap();\n        let file = form_data\n            .get(\"file_to_upload\")\n            .unchecked_into::<web_sys::File>();\n        let filename = file.name();\n        let size = file.size() as usize;\n        set_filename.set(Some(filename.clone()));\n        set_max.set(Some(size));\n        set_current.set(None);\n\n        spawn_local(async move {\n            let mut progress = file_progress(filename)\n                .await\n                .expect(\"couldn't initialize stream\")\n                .into_inner();\n            while let Some(Ok(len)) = progress.next().await {\n                // the TextStream from the server function will be a series of `usize` values\n                // however, the response itself may pack those chunks into a smaller number of\n                // chunks, each with more text in it\n                // so we've padded them with newspace, and will split them out here\n                // each value is the latest total, so we'll just take the last one\n                let len = len\n                    .split('\\n')\n                    .filter(|n| !n.is_empty())\n                    .next_back()\n                    .expect(\n                        \"expected at least one non-empty value from \\\n                         newline-delimited rows\",\n                    )\n                    .parse::<usize>()\n                    .expect(\"invalid length\");\n                set_current.set(Some(len));\n            }\n        });\n        spawn_local(async move {\n            upload_file(form_data.into())\n                .await\n                .expect(\"couldn't upload file\");\n        });\n    };\n\n    view! {\n        <h3>File Upload with Progress</h3>\n        <p>A file upload with progress can be handled with two separate server functions.</p>\n        <aside>See the doc comment on the component for an explanation.</aside>\n        <form on:submit=on_submit>\n            <input type=\"file\" name=\"file_to_upload\" />\n            <input type=\"submit\" />\n        </form>\n        {move || filename.get().map(|filename| view! { <p>Uploading {filename}</p> })}\n        <ShowLet some=max let:max>\n            <progress\n                max=max\n                value=move || current.get().unwrap_or_default()\n            ></progress>\n        </ShowLet>\n    }\n}\n#[component]\npub fn FileWatcher() -> impl IntoView {\n    #[server(input = GetUrl, output = StreamingText)]\n    pub async fn watched_files() -> Result<TextStream, ServerFnError> {\n        use notify::{\n            Config, Error, Event, RecommendedWatcher, RecursiveMode, Watcher,\n        };\n        use std::path::Path;\n\n        let (tx, rx) = futures::channel::mpsc::unbounded();\n\n        let mut watcher = RecommendedWatcher::new(\n            move |res: Result<Event, Error>| {\n                if let Ok(ev) = res {\n                    if let Some(path) = ev.paths.last() {\n                        let filename = path\n                            .file_name()\n                            .unwrap()\n                            .to_str()\n                            .unwrap()\n                            .to_string();\n                        _ = tx.unbounded_send(filename); //res);\n                    }\n                }\n            },\n            Config::default(),\n        )?;\n        watcher\n            .watch(Path::new(\"./watched_files\"), RecursiveMode::Recursive)?;\n        std::mem::forget(watcher);\n\n        Ok(TextStream::from(rx))\n    }\n\n    let (files, set_files) = signal(Vec::new());\n\n    Effect::new(move |_| {\n        spawn_local(async move {\n            while let Some(res) =\n                watched_files().await.unwrap().into_inner().next().await\n            {\n                if let Ok(filename) = res {\n                    set_files.update(|n| n.push(filename));\n                }\n            }\n        });\n    });\n\n    view! {\n        <h3>Watching files and returning a streaming response</h3>\n        <p>Files changed since you loaded the page:</p>\n        <ul>\n            {move || {\n                files\n                    .get()\n                    .into_iter()\n                    .map(|file| {\n                        view! {\n                            <li>\n                                <code>{file}</code>\n                            </li>\n                        }\n                    })\n                    .collect::<Vec<_>>()\n            }}\n\n        </ul>\n        <p>\n            <em>\n                Add or remove some text files in the <code>watched_files</code>\n                directory and see the list of changes here.\n            </em>\n        </p>\n    }\n}\n\n/// The `ServerFnError` type is generic over a custom error type, which defaults to `NoCustomError`\n/// for backwards compatibility and to support the most common use case.\n///\n/// A custom error type should implement `FromStr` and `Display`, which allows it to be converted\n/// into and from a string easily to be sent over the network. It does *not* need to implement\n/// `Serialize` and `Deserialize`, although these can be used to generate the `FromStr`/`Display`\n/// implementations if you'd like. However, it's much lighter weight to use something like `strum`\n/// simply to generate those trait implementations.\n#[server]\npub async fn ascii_uppercase(text: String) -> Result<String, MyErrors> {\n    other_error()?;\n    Ok(ascii_uppercase_inner(text)?)\n}\n\npub fn other_error() -> Result<(), String> {\n    Ok(())\n}\n\npub fn ascii_uppercase_inner(text: String) -> Result<String, InvalidArgument> {\n    if text.len() < 5 {\n        Err(InvalidArgument::TooShort)\n    } else if text.len() > 15 {\n        Err(InvalidArgument::TooLong)\n    } else if text.is_ascii() {\n        Ok(text.to_ascii_uppercase())\n    } else {\n        Err(InvalidArgument::NotAscii)\n    }\n}\n\n#[server]\npub async fn ascii_uppercase_classic(\n    text: String,\n) -> Result<String, ServerFnError<InvalidArgument>> {\n    Ok(ascii_uppercase_inner(text)?)\n}\n\n// The EnumString and Display derive macros are provided by strum\n#[derive(\n    thiserror::Error,\n    Debug,\n    Clone,\n    Display,\n    EnumString,\n    Serialize,\n    Deserialize,\n    rkyv::Archive,\n    rkyv::Serialize,\n    rkyv::Deserialize,\n)]\npub enum InvalidArgument {\n    TooShort,\n    TooLong,\n    NotAscii,\n}\n\n#[derive(\n    thiserror::Error,\n    Debug,\n    Clone,\n    Display,\n    Serialize,\n    Deserialize,\n    rkyv::Archive,\n    rkyv::Serialize,\n    rkyv::Deserialize,\n)]\npub enum MyErrors {\n    InvalidArgument(InvalidArgument),\n    ServerFnError(ServerFnErrorErr),\n    Other(String),\n}\n\nimpl From<InvalidArgument> for MyErrors {\n    fn from(value: InvalidArgument) -> Self {\n        MyErrors::InvalidArgument(value)\n    }\n}\n\nimpl From<String> for MyErrors {\n    fn from(value: String) -> Self {\n        MyErrors::Other(value)\n    }\n}\n\nimpl FromServerFnError for MyErrors {\n    type Encoder = RkyvEncoding;\n\n    fn from_server_fn_error(value: ServerFnErrorErr) -> Self {\n        MyErrors::ServerFnError(value)\n    }\n}\n\n#[component]\npub fn CustomErrorTypes() -> impl IntoView {\n    let input_ref = NodeRef::<Input>::new();\n    let (result, set_result) = signal(None);\n    let (result_classic, set_result_classic) = signal(None);\n\n    view! {\n        <h3>Using custom error types</h3>\n        <p>\n            \"Server functions can use a custom error type that is preserved across the network boundary.\"\n        </p>\n        <p>\n            \"Try typing a message that is between 5 and 15 characters of ASCII text below. Then try breaking \\\n            the rules!\"\n        </p>\n        <input node_ref=input_ref placeholder=\"Type something here.\" />\n        <button on:click=move |_| {\n            let value = input_ref.get().unwrap().value();\n            spawn_local(async move {\n                let data = ascii_uppercase(value.clone()).await;\n                let data_classic = ascii_uppercase_classic(value).await;\n                set_result.set(Some(data));\n                set_result_classic.set(Some(data_classic));\n            });\n        }>\n\n            \"Submit\"\n        </button>\n        <p>{move || format!(\"{:?}\", result.get())}</p>\n        <p>{move || format!(\"{:?}\", result_classic.get())}</p>\n    }\n}\n\n/// Server function encodings are just types that implement a few traits.\n/// This means that you can implement your own encodings, by implementing those traits!\n///\n/// Here, we'll create a custom encoding that serializes and deserializes the server fn\n/// using TOML. Why would you ever want to do this? I don't know, but you can!\npub struct Toml;\n\n/// A newtype wrapper around server fn data that will be TOML-encoded.\n///\n/// This is needed because of Rust rules around implementing foreign traits for foreign types.\n/// It will be fed into the `custom = ` argument to the server fn below.\n#[derive(Serialize, Deserialize)]\npub struct TomlEncoded<T>(T);\n\nimpl ContentType for Toml {\n    const CONTENT_TYPE: &'static str = \"application/toml\";\n}\n\nimpl FormatType for Toml {\n    const FORMAT_TYPE: Format = Format::Text;\n}\n\nimpl Encoding for Toml {\n    const METHOD: Method = Method::POST;\n}\n\nimpl<T, Request, Err> IntoReq<Toml, Request, Err> for TomlEncoded<T>\nwhere\n    Request: ClientReq<Err>,\n    T: Serialize,\n    Err: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, Err> {\n        let data = toml::to_string(&self.0).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_post(path, Toml::CONTENT_TYPE, accepts, data)\n    }\n}\n\nimpl<T, Request, Err> FromReq<Toml, Request, Err> for TomlEncoded<T>\nwhere\n    Request: Req<Err> + Send,\n    T: DeserializeOwned,\n    Err: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, Err> {\n        let string_data = req.try_into_string().await?;\n        toml::from_str::<T>(&string_data)\n            .map(TomlEncoded)\n            .map_err(|e| ServerFnErrorErr::Args(e.to_string()).into_app_error())\n    }\n}\n\nimpl<T, Response, Err> IntoRes<Toml, Response, Err> for TomlEncoded<T>\nwhere\n    Response: TryRes<Err>,\n    T: Serialize + Send,\n    Err: FromServerFnError,\n{\n    async fn into_res(self) -> Result<Response, Err> {\n        let data = toml::to_string(&self.0).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Response::try_from_string(Toml::CONTENT_TYPE, data)\n    }\n}\n\nimpl<T, Response, Err> FromRes<Toml, Response, Err> for TomlEncoded<T>\nwhere\n    Response: ClientRes<Err> + Send,\n    T: DeserializeOwned,\n    Err: FromServerFnError,\n{\n    async fn from_res(res: Response) -> Result<Self, Err> {\n        let data = res.try_into_string().await?;\n        toml::from_str(&data).map(TomlEncoded).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })\n    }\n}\n\n#[derive(Serialize, Deserialize)]\npub struct WhyNotResult {\n    original: String,\n    modified: String,\n}\n\n#[server(\n    input = Toml,\n    output = Toml,\n    custom = TomlEncoded\n)]\npub async fn why_not(\n    original: String,\n    addition: String,\n) -> Result<TomlEncoded<WhyNotResult>, ServerFnError> {\n    // insert a simulated wait\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n    Ok(TomlEncoded(WhyNotResult {\n        modified: format!(\"{original}{addition}\"),\n        original,\n    }))\n}\n\n#[component]\npub fn CustomEncoding() -> impl IntoView {\n    let input_ref = NodeRef::<Input>::new();\n    let (result, set_result) = signal(\"foo\".to_string());\n\n    view! {\n        <h3>Custom encodings</h3>\n        <p>\n            \"This example creates a custom encoding that sends server fn data using TOML. Why? Well... why not?\"\n        </p>\n        <input node_ref=input_ref placeholder=\"Type something here.\" />\n        <button on:click=move |_| {\n            let value = input_ref.get().unwrap().value();\n            spawn_local(async move {\n                let new_value = why_not(value, \", but in TOML!!!\".to_string()).await.unwrap();\n                set_result.set(new_value.0.modified);\n            });\n        }>\n\n            Submit\n        </button>\n        <p>{result}</p>\n    }\n}\n\n/// Middleware lets you modify the request/response on the server.\n///\n/// On the client, you might also want to modify the request. For example, you may need to add a\n/// custom header for authentication on every request. You can do this by creating a \"custom\n/// client.\"\n#[component]\npub fn CustomClientExample() -> impl IntoView {\n    // Define a type for our client.\n    pub struct CustomClient;\n\n    // Implement the `Client` trait for it.\n    impl<E, IS, OS> Client<E, IS, OS> for CustomClient\n    where\n        E: FromServerFnError,\n        IS: FromServerFnError,\n        OS: FromServerFnError,\n    {\n        // BrowserRequest and BrowserResponse are the defaults used by other server functions.\n        // They are wrappers for the underlying Web Fetch API types.\n        type Request = BrowserRequest;\n        type Response = BrowserResponse;\n\n        // Our custom `send()` implementation does all the work.\n        fn send(\n            req: Self::Request,\n        ) -> impl Future<Output = Result<Self::Response, E>> + Send {\n            // BrowserRequest derefs to the underlying Request type from gloo-net,\n            // so we can get access to the headers here\n            let headers = req.headers();\n            // modify the headers by appending one\n            headers.append(\"X-Custom-Header\", \"foobar\");\n            // delegate back out to BrowserClient to send the modified request\n            <BrowserClient as Client<E, IS, OS>>::send(req)\n        }\n\n        fn open_websocket(\n            path: &str,\n        ) -> impl Future<\n            Output = Result<\n                (\n                    impl Stream<\n                            Item = Result<server_fn::Bytes, server_fn::Bytes>,\n                        > + Send\n                        + 'static,\n                    impl Sink<server_fn::Bytes> + Send + 'static,\n                ),\n                E,\n            >,\n        > + Send {\n            <BrowserClient as Client<E, IS, OS>>::open_websocket(path)\n        }\n\n        fn spawn(future: impl Future<Output = ()> + Send + 'static) {\n            <BrowserClient as Client<E, IS, OS>>::spawn(future)\n        }\n    }\n\n    // Specify our custom client with `client = `\n    #[server(client = CustomClient)]\n    pub async fn fn_with_custom_client() -> Result<(), ServerFnError> {\n        use http::header::HeaderMap;\n        use leptos_axum::extract;\n\n        let headers: HeaderMap = extract().await?;\n        let custom_header = headers.get(\"X-Custom-Header\");\n        println!(\"X-Custom-Header = {custom_header:?}\");\n        Ok(())\n    }\n\n    view! {\n        <h3>Custom clients</h3>\n        <p>\n            You can define a custom server function client to do something like adding a header to every request.\n        </p>\n        <p>\n            Check the network request in your browser devtools to see how this client adds a custom header.\n        </p>\n        <button on:click=|_| spawn_local(async {\n            fn_with_custom_client().await.unwrap()\n        })>Click me</button>\n    }\n}\n\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]\npub struct PostcardData {\n    name: String,\n    age: u32,\n    hobbies: Vec<String>,\n}\n\n/// This server function uses Postcard for both input and output encoding.\n/// Postcard provides efficient binary serialization, almost as fast as rkyv, while also being\n/// serde compatible\n#[server(input = Postcard, output = Postcard)]\npub async fn postcard_example(\n    data: PostcardData,\n) -> Result<PostcardData, ServerFnError> {\n    // Simulate some processing time\n    tokio::time::sleep(std::time::Duration::from_millis(250)).await;\n\n    // Modify the data to demonstrate server-side changes\n    let mut modified_data = data.clone();\n    modified_data.age += 1;\n    modified_data.hobbies.push(\"Rust programming\".to_string());\n\n    Ok(modified_data)\n}\n\n/// This component demonstrates the usage of Postcard encoding with server functions.\n/// It allows incrementing the age of a person and shows how the data is\n/// serialized, sent to the server, processed, and returned.\n#[component]\npub fn PostcardExample() -> impl IntoView {\n    // Initialize the input data\n    let (input, set_input) = signal(PostcardData {\n        name: \"Alice\".to_string(),\n        age: 30,\n        hobbies: vec![\"reading\".to_string(), \"hiking\".to_string()],\n    });\n\n    // Create a resource that will call the server function whenever the input changes\n    let postcard_result = Resource::new(\n        move || input.get(),\n        |data| async move { postcard_example(data).await },\n    );\n\n    view! {\n        <h3>Using <code>postcard</code>encoding</h3>\n        <p>\"This example demonstrates using Postcard for efficient binary serialization.\"</p>\n        <button on:click=move |_| {\n            set_input\n                .update(|data| {\n                    data.age += 1;\n                });\n        }>\"Increment Age\"</button>\n        // Display the current input data\n        <p>\"Input: \" {move || format!(\"{:?}\", input.get())}</p>\n        <Transition>\n            // Display the result from the server, which will update automatically\n            // when the input changes due to the resource\n            <p>\"Result: \" {move || postcard_result.get().map(|r| format!(\"{:?}\", r))}</p>\n        </Transition>\n    }\n}\n"
  },
  {
    "path": "examples/server_fns_axum/src/error_template.rs",
    "content": "use crate::errors::TodoAppError;\nuse leptos::prelude::*;\n#[cfg(feature = \"ssr\")]\nuse leptos_axum::ResponseOptions;\n\n// A basic function to display errors served by the error boundaries. Feel free to do more complicated things\n// here than just displaying them\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional)] errors: Option<RwSignal<Errors>>,\n) -> impl IntoView {\n    let errors = match outside_errors {\n        Some(e) => RwSignal::new(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n\n    // Get Errors from Signal\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors: Vec<TodoAppError> = errors\n        .get()\n        .into_iter()\n        .filter_map(|(_, v)| v.downcast_ref::<TodoAppError>().cloned())\n        .collect();\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    #[cfg(feature = \"ssr\")]\n    {\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors[0].status_code());\n        }\n    }\n\n    view! {\n        <h1>\"Errors\"</h1>\n        <For\n            // a function that returns the items we're iterating over; a signal is fine\n            each=move || { errors.clone().into_iter().enumerate() }\n            // a unique key for each item as a reference\n            key=|(index, _error)| *index\n            // renders each item to a view\n            children=move |error| {\n                let error_string = error.1.to_string();\n                let error_code = error.1.status_code();\n                view! {\n                    <h2>{error_code.to_string()}</h2>\n                    <p>\"Error: \" {error_string}</p>\n                }\n            }\n        />\n    }\n}\n"
  },
  {
    "path": "examples/server_fns_axum/src/errors.rs",
    "content": "use http::status::StatusCode;\nuse thiserror::Error;\n\n#[derive(Debug, Clone, Error)]\npub enum TodoAppError {\n    #[error(\"Not Found\")]\n    NotFound,\n    #[error(\"Internal Server Error\")]\n    InternalServerError,\n}\n\nimpl TodoAppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            TodoAppError::NotFound => StatusCode::NOT_FOUND,\n            TodoAppError::InternalServerError => {\n                StatusCode::INTERNAL_SERVER_ERROR\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/server_fns_axum/src/lib.rs",
    "content": "pub mod app;\npub mod error_template;\npub mod errors;\n#[cfg(feature = \"ssr\")]\npub mod middleware;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::app::App;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/server_fns_axum/src/main.rs",
    "content": "use crate::app::*;\nuse axum::Router;\nuse leptos::{config::get_configuration, logging};\nuse leptos_axum::{generate_route_list, LeptosRoutes};\nuse server_fns_axum::*;\n\n// cargo make cli: error: unneeded `return` statement\n#[allow(clippy::needless_return)]\n#[tokio::main]\nasync fn main() {\n    simple_logger::init_with_level(log::Level::Error)\n        .expect(\"couldn't initialize logging\");\n\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    logging::log!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n"
  },
  {
    "path": "examples/server_fns_axum/src/middleware.rs",
    "content": "use axum::body::Body;\nuse http::Request;\nuse pin_project_lite::pin_project;\nuse std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\nuse tower::{Layer, Service};\n\npub struct LoggingLayer;\n\nimpl<S> Layer<S> for LoggingLayer {\n    type Service = LoggingService<S>;\n\n    fn layer(&self, inner: S) -> Self::Service {\n        LoggingService { inner }\n    }\n}\n\npub struct LoggingService<T> {\n    inner: T,\n}\n\nimpl<T> Service<Request<Body>> for LoggingService<T>\nwhere\n    T: Service<Request<Body>>,\n{\n    type Response = T::Response;\n    type Error = T::Error;\n    type Future = LoggingServiceFuture<T::Future>;\n\n    fn poll_ready(\n        &mut self,\n        cx: &mut Context<'_>,\n    ) -> Poll<Result<(), Self::Error>> {\n        self.inner.poll_ready(cx)\n    }\n\n    fn call(&mut self, req: Request<Body>) -> Self::Future {\n        println!(\"1. Running my middleware!\");\n\n        LoggingServiceFuture {\n            inner: self.inner.call(req),\n        }\n    }\n}\n\npin_project! {\n    pub struct LoggingServiceFuture<T> {\n        #[pin]\n        inner: T,\n    }\n}\n\nimpl<T> Future for LoggingServiceFuture<T>\nwhere\n    T: Future,\n{\n    type Output = T::Output;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        match this.inner.poll(cx) {\n            Poll::Pending => Poll::Pending,\n            Poll::Ready(output) => {\n                println!(\"3. Running my middleware!\");\n                Poll::Ready(output)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/server_fns_axum/style.css",
    "content": ".pending {\n\tcolor: purple;\n}\n"
  },
  {
    "path": "examples/server_fns_axum/watched_files/.gitkeep",
    "content": ""
  },
  {
    "path": "examples/slots/Cargo.toml",
    "content": "[package]\nname = \"slots\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_log = \"1.0\"\nlog = \"0.4.22\"\nconsole_error_panic_hook = \"0.1.7\"\n"
  },
  {
    "path": "examples/slots/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/slots/README.md",
    "content": "# Leptos `<Component slot/>` Example\n\nThis example shows how to use Slots in Leptos.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/slots/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/slots/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/slots/src/lib.rs",
    "content": "use leptos::prelude::*;\n\n// Slots are created in similar manner to components, except that they use the #[slot] macro.\n#[slot]\nstruct Then {\n    children: ChildrenFn,\n}\n\n// Props work just like component props, for example, you can specify a prop as optional by prefixing\n// the type with Option<...> and marking the option as #[prop(optional)].\n#[slot]\nstruct ElseIf {\n    cond: Signal<bool>,\n    children: ChildrenFn,\n}\n\n#[slot]\nstruct Fallback {\n    children: ChildrenFn,\n}\n\n// Slots are added to components like any other prop.\n#[component]\nfn SlotIf(\n    cond: Signal<bool>,\n    then: Then,\n    #[prop(default=vec![])] else_if: Vec<ElseIf>,\n    #[prop(optional)] fallback: Option<Fallback>,\n) -> impl IntoView {\n    move || {\n        if cond.get() {\n            (then.children)().into_any()\n        } else if let Some(else_if) = else_if.iter().find(|i| i.cond.get()) {\n            (else_if.children)().into_any()\n        } else if let Some(fallback) = &fallback {\n            (fallback.children)().into_any()\n        } else {\n            ().into_any()\n        }\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    let (count, set_count) = signal(0);\n    let is_even = Signal::derive(move || count.get() % 2 == 0);\n    let is_div5 = Signal::derive(move || count.get() % 5 == 0);\n    let is_div7 = Signal::derive(move || count.get() % 7 == 0);\n\n    view! {\n        <button on:click=move |_| set_count.update(|value| *value += 1)>\"+1\"</button>\n        \" \"{count}\" is \"\n        <SlotIf cond=is_even>\n            // The slot name can be emitted if it would match the slot struct name (in snake case).\n            <Then slot>\"even\"</Then>\n            // Props are passed just like on normal components.\n            <ElseIf slot cond=is_div5>\"divisible by 5\"</ElseIf>\n            <ElseIf slot cond=is_div7>\"divisible by 7\"</ElseIf>\n            <Fallback slot>\"odd\"</Fallback>\n        </SlotIf>\n    }\n}\n"
  },
  {
    "path": "examples/slots/src/main.rs",
    "content": "use slots::App;\n\npub fn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(App);\n}\n"
  },
  {
    "path": "examples/spread/Cargo.toml",
    "content": "[package]\nname = \"spread\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_error_panic_hook = \"0.1.7\"\n"
  },
  {
    "path": "examples/spread/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/spread/README.md",
    "content": "# Leptos Attribute and EventHandler spreading Example\n\nThis example creates a simple element in a client side rendered app with Rust and WASM!\n\nDynamic sets of attributes and event handler are spread onto the element with little effort.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/spread/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/spread/src/lib.rs",
    "content": "use leptos::prelude::*;\n\n/// Demonstrates how attributes and event handlers can be spread onto elements.\n#[component]\npub fn SpreadingExample() -> impl IntoView {\n    fn alert(msg: impl AsRef<str>) {\n        let _ = window().alert_with_message(msg.as_ref());\n    }\n\n    // you can easily create sets of spreadable attributes by using the <{..} ___/> syntax\n    // this is expanded to a tuple of attributes; it has no meaning on its own, but can be spread\n    // onto an HTML element or component\n    let attrs_only = view! { <{..} class=\"foo\"/> };\n    let event_handlers_only = view! { <{..} on:click=move |_| {\n        alert(\"event_handlers_only clicked\");\n    }/> };\n    let combined = view! { <{..} class=\"bar\" on:click=move |_| alert(\"combined clicked\") /> };\n    let partial_attrs =\n        view! { <{..} id=\"snood\" class=\"baz\" data-foo=\"bar\" /> };\n    let partial_event_handlers = view! { <{..} on:click=move |_| alert(\"partial_event_handlers clicked\") /> };\n    let spread_onto_component = view! {\n        <{..} aria-label=\"a component with attribute spreading\"/>\n    };\n\n    /* with the correct imports, you can use a tuple/builder syntax as well\n        let attrs_only = class(\"foo\");\n        let event_handlers_only = on(ev::click, move |_e: ev::MouseEvent| {\n            alert(\"event_handlers_only clicked\");\n        });\n        let combined = (\n            class(\"bar\"),\n            on(ev::click, move |_e: ev::MouseEvent| {\n                alert(\"combined clicked\");\n            }),\n        );\n        let partial_attrs = (id(\"snood\"), class(\"baz\"));\n        let partial_event_handlers = on(ev::click, move |_e: ev::MouseEvent| {\n            alert(\"partial_event_handlers clicked\");\n        });\n    */\n\n    view! {\n        <p>\n            \"You can spread any valid attribute, including a tuple of attributes, with the {..attr} syntax\"\n        </p>\n        <div {..attrs_only.clone()}>\"<div {..attrs_only} />\"</div>\n\n        <div {..event_handlers_only.clone()}>\"<div {..event_handlers_only} />\"</div>\n\n        <div {..combined.clone()}>\"<div {..combined} />\"</div>\n\n        <div {..partial_attrs.clone()} {..partial_event_handlers.clone()}>\n            \"<div {..partial_attrs} {..partial_event_handlers} />\"\n        </div>\n\n        <hr/>\n\n        <p>\n            \"The .. is not required to spread; you can pass any valid attribute in a block by itself.\"\n        </p>\n        <div {attrs_only}>\"<div {attrs_only} />\"</div>\n\n        <div {event_handlers_only}>\"<div {event_handlers_only} />\"</div>\n\n        <div {combined}>\"<div {combined} />\"</div>\n\n        <div {partial_attrs} {partial_event_handlers}>\n            \"<div {partial_attrs} {partial_event_handlers} />\"\n        </div>\n\n        <hr/>\n\n        // attributes that are spread onto a component will be applied to *all* elements returned as part of\n        // the component's view. to apply attributes to a subset of the component, pass them via a component prop\n        <ComponentThatTakesSpread\n            // the class:, style:, prop:, on: syntaxes work just as they do on elements\n            class:foo=true\n            style:font-weight=\"bold\"\n            prop:cool=42\n            on:click=move |_| alert(\"clicked ComponentThatTakesSpread\")\n            // props are passed as they usually are on components\n            some_prop=13\n            // to pass a plain HTML attribute, prefix it with attr:\n            attr:id=\"foo\"\n            // or, if you want to include multiple attributes, rather than prefixing each with\n            // attr:, you can separate them from component props with the spread {..}\n            {..} // everything after this is treated as an HTML attribute\n            title=\"ooh, a title!\"\n            {..spread_onto_component}\n        />\n    }\n    // TODO check below\n    // Overwriting an event handler, here on:click, will result in a panic in debug builds. In release builds, the initial handler is kept.\n    // If spreading is used, prefer manually merging event handlers in the binding list instead.\n    //<div {..mixed} on:click=|_e| { alert(\"I will never be seen...\"); }>\n    //    \"with overwritten click handler\"\n    //</div>\n}\n\n#[component]\npub fn ComponentThatTakesSpread(some_prop: i32) -> impl IntoView {\n    leptos::logging::log!(\"some_prop = {some_prop}\");\n    view! {\n        <button>\"<ComponentThatTakesSpread/>\"</button>\n        <p>\n            \"Attributes applied to a component apply to all top-level elements returned by a component.\"\n        </p>\n    }\n}\n"
  },
  {
    "path": "examples/spread/src/main.rs",
    "content": "use spread::SpreadingExample;\n\npub fn main() {\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(SpreadingExample)\n}\n"
  },
  {
    "path": "examples/ssr_modes/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n"
  },
  {
    "path": "examples/ssr_modes/Cargo.toml",
    "content": "[package]\nname = \"ssr_modes\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nactix-files = { version = \"0.6.6\", optional = true }\nactix-web = { version = \"4.8\", optional = true, features = [\"macros\"] }\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\nleptos = { path = \"../../leptos\" }\nleptos_meta = { path = \"../../meta\" }\nleptos_actix = { path = \"../../integrations/actix\", optional = true }\nleptos_router = { path = \"../../router\" }\nlog = \"0.4.22\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nthiserror = \"2.0.12\"\ntokio = { version = \"1.39\", features = [\"time\"] }\nwasm-bindgen = \"0.2.93\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:actix-files\",\n  \"dep:actix-web\",\n  \"dep:leptos_actix\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"actix-files\", \"actix-web\", \"leptos_actix\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"ssr_modes\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"assets\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/ssr_modes/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 henrik\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/ssr_modes/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"ssr_modes\"\n"
  },
  {
    "path": "examples/ssr_modes/README.md",
    "content": "# SSR Modes Example\n\nThis example shows the different \"rendering modes\" that can be used while server-side rendering an application.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Server-Side Rendering Modes\n\n1. **Synchronous**: Serve an HTML shell that includes `fallback` for any `Suspense`. Load data on the client, replacing `fallback` once they're loaded.\n\n   - _Pros_: App shell appears very quickly: great TTFB (time to first byte).\n   - _Cons_: Resources load relatively slowly; you need to wait for JS + Wasm to load before even making a request.\n\n2. **Out-of-order streaming**: Serve an HTML shell that includes `fallback` for any `Suspense`. Load data on the **server**, streaming it down to the client as it resolves, and streaming down HTML for `Suspense` nodes.\n\n   - _Pros_: Combines the best of **synchronous** and **`async`**, with a very fast shell and resources that begin loading on the server.\n   - _Cons_: Requires JS for suspended fragments to appear in correct order. Weaker meta tag support when it depends on data that's under suspense (has already streamed down `<head>`)\n\n3. **In-order streaming**: Walk through the tree, returning HTML synchronously as in synchronous rendering and out-of-order streaming until you hit a `Suspense`. At that point, wait for all its data to load, then render it, then the rest of the tree.\n\n   - _Pros_: Does not require JS for HTML to appear in correct order.\n   - _Cons_: Loads the shell more slowly than out-of-order streaming or synchronous rendering because it needs to pause at every `Suspense`. Cannot begin hydration until the entire page has loaded, so earlier pieces\n     of the page will not be interactive until the suspended chunks have loaded.\n\n4. **`async`**: Load all resources on the server. Wait until all data are loaded, and render HTML in one sweep.\n   - _Pros_: Better handling for meta tags (because you know async data even before you render the `<head>`). Faster complete load than **synchronous** because async resources begin loading on server.\n   - _Cons_: Slower load time/TTFB: you need to wait for all async resources to load before displaying anything on the client.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/ssr_modes/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/ssr_modes/src/app.rs",
    "content": "use leptos::prelude::*;\nuse leptos_meta::*;\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router},\n    hooks::use_params,\n    params::Params,\n    ParamSegment, SsrMode, StaticSegment,\n};\nuse serde::{Deserialize, Serialize};\nuse std::sync::LazyLock;\nuse thiserror::Error;\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n    let fallback = || view! { \"Page not found.\" }.into_view();\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/ssr_modes.css\" />\n        <Title text=\"Welcome to Leptos\" />\n        <Meta name=\"color-scheme\" content=\"dark light\" />\n        <Router>\n            <main>\n                <FlatRoutes fallback>\n                    // We’ll load the home page with out-of-order streaming and <Suspense/>\n                    <Route path=StaticSegment(\"\") view=HomePage />\n\n                    // We'll load the posts with async rendering, so they can set\n                    // the title and metadata *after* loading the data\n                    <Route\n                        path=(StaticSegment(\"post\"), ParamSegment(\"id\"))\n                        view=Post\n                        ssr=SsrMode::Async\n                    />\n                    <Route\n                        path=(StaticSegment(\"post_in_order\"), ParamSegment(\"id\"))\n                        view=Post\n                        ssr=SsrMode::InOrder\n                    />\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[component]\nfn HomePage() -> impl IntoView {\n    // load the posts\n    let posts = Resource::new(|| (), |_| list_post_metadata());\n    let posts = move || {\n        posts\n            .get()\n            .map(|n| n.unwrap_or_default())\n            .unwrap_or_default()\n    };\n\n    let posts2 = Resource::new(|| (), |_| list_post_metadata());\n    let posts2 = Resource::new(\n        || (),\n        move |_| async move { posts2.await.as_ref().map(Vec::len).unwrap_or(0) },\n    );\n\n    view! {\n        <h1>\"My Great Blog\"</h1>\n        <Suspense fallback=move || view! { <p>\"Loading posts...\"</p> }>\n            <p>\"number of posts: \" {Suspend::new(async move { posts2.await })}</p>\n        </Suspense>\n        <Suspense fallback=move || view! { <p>\"Loading posts...\"</p> }>\n            <ul>\n                <For each=posts key=|post| post.id let:post>\n                    <li>\n                        <a href=format!(\"/post/{}\", post.id)>{post.title.clone()}</a>\n                        \"|\"\n                        <a href=format!(\"/post_in_order/{}\", post.id)>{post.title} \"(in order)\"</a>\n                    </li>\n                </For>\n            </ul>\n        </Suspense>\n    }\n}\n\n#[derive(Params, Copy, Clone, Debug, PartialEq, Eq)]\npub struct PostParams {\n    id: Option<usize>,\n}\n\n#[component]\nfn Post() -> impl IntoView {\n    let query = use_params::<PostParams>();\n    let id = move || {\n        query.with(|q| {\n            q.as_ref()\n                .map(|q| q.id.unwrap_or_default())\n                .map_err(|_| PostError::InvalidId)\n        })\n    };\n    let post_resource = Resource::new(id, |id| async move {\n        match id {\n            Err(e) => Err(e),\n            Ok(id) => get_post(id)\n                .await\n                .map(|data| data.ok_or(PostError::PostNotFound))\n                .map_err(|_| PostError::ServerError),\n        }\n    });\n\n    let post_view = Suspend::new(async move {\n        match post_resource.await.to_owned() {\n            Ok(Ok(post)) => Ok(view! {\n                <h1>{post.title.clone()}</h1>\n                <p>{post.content.clone()}</p>\n\n                // since we're using async rendering for this page,\n                // this metadata should be included in the actual HTML <head>\n                // when it's first served\n                <Title text=post.title />\n                <Meta name=\"description\" content=post.content />\n            }),\n            _ => Err(PostError::ServerError),\n        }\n    });\n\n    view! {\n        <em>\"The world's best content.\"</em>\n        <Suspense fallback=move || view! { <p>\"Loading post...\"</p> }>\n            <ErrorBoundary fallback=|errors| {\n                view! {\n                    <div class=\"error\">\n                        <h1>\"Something went wrong.\"</h1>\n                        <ul>\n                            {move || {\n                                errors\n                                    .get()\n                                    .into_iter()\n                                    .map(|(_, error)| view! { <li>{error.to_string()}</li> })\n                                    .collect::<Vec<_>>()\n                            }}\n\n                        </ul>\n                    </div>\n                }\n            }>{post_view}</ErrorBoundary>\n        </Suspense>\n    }\n}\n\n// Dummy API\n\nstatic POSTS: LazyLock<[Post; 3]> = LazyLock::new(|| {\n    [\n        Post {\n            id: 0,\n            title: \"My first post\".to_string(),\n            content: \"This is my first post\".to_string(),\n        },\n        Post {\n            id: 1,\n            title: \"My second post\".to_string(),\n            content: \"This is my second post\".to_string(),\n        },\n        Post {\n            id: 2,\n            title: \"My third post\".to_string(),\n            content: \"This is my third post\".to_string(),\n        },\n    ]\n});\n\n#[derive(Error, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub enum PostError {\n    #[error(\"Invalid post ID.\")]\n    InvalidId,\n    #[error(\"Post not found.\")]\n    PostNotFound,\n    #[error(\"Server error.\")]\n    ServerError,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct Post {\n    id: usize,\n    title: String,\n    content: String,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct PostMetadata {\n    id: usize,\n    title: String,\n}\n\n#[server]\npub async fn list_post_metadata() -> Result<Vec<PostMetadata>, ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n    Ok(POSTS\n        .iter()\n        .map(|data| PostMetadata {\n            id: data.id,\n            title: data.title.clone(),\n        })\n        .collect())\n}\n\n#[server]\npub async fn get_post(id: usize) -> Result<Option<Post>, ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n    Ok(POSTS.iter().find(|post| post.id == id).cloned())\n}\n"
  },
  {
    "path": "examples/ssr_modes/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n\n    // initializes logging using the `log` crate\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/ssr_modes/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    use actix_files::Files;\n    use actix_web::*;\n    use leptos::prelude::*;\n    use leptos_actix::{generate_route_list, LeptosRoutes};\n    use leptos_meta::MetaTags;\n    use ssr_modes::app::*;\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n\n    HttpServer::new(move || {\n        // Generate the list of routes in your Leptos App\n        let routes = generate_route_list(App);\n        let leptos_options = &conf.leptos_options;\n        let site_root = &leptos_options.site_root;\n\n        App::new()\n            .leptos_routes(routes, {\n                let leptos_options = leptos_options.clone();\n                move || {\n                    use leptos::prelude::*;\n\n                    view! {\n                        <!DOCTYPE html>\n                        <html lang=\"en\">\n                            <head>\n                                <meta charset=\"utf-8\"/>\n                                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                                <AutoReload options=leptos_options.clone() />\n                                <HydrationScripts options=leptos_options.clone()/>\n                                <MetaTags/>\n                            </head>\n                            <body>\n                                <App/>\n                            </body>\n                        </html>\n                    }\n            }})\n            .service(Files::new(\"/\", site_root.as_ref()))\n        //.wrap(middleware::Compress::default())\n    })\n    .bind(&addr)?\n    .run()\n    .await\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "examples/ssr_modes/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n}\n"
  },
  {
    "path": "examples/ssr_modes_axum/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n"
  },
  {
    "path": "examples/ssr_modes_axum/Cargo.toml",
    "content": "[package]\nname = \"ssr_modes_axum\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\nleptos = { path = \"../../leptos\", features = [\n  \"hydration\",\n] } #\"nightly\", \"hydration\"] }\nleptos_meta = { path = \"../../meta\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../router\" }\nlog = \"0.4.22\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nthiserror = \"2.0.12\"\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.39\", features = [\n  \"rt-multi-thread\",\n  \"macros\",\n  \"time\",\n], optional = true }\nwasm-bindgen = \"0.2.93\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"dep:leptos_axum\",\n  \"leptos_router/ssr\",\n]\n\n[profile.release]\npanic = \"abort\"\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"ssr_modes\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"assets\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3007\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "examples/ssr_modes_axum/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 henrik\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/ssr_modes_axum/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"ssr_modes_axum\"\n"
  },
  {
    "path": "examples/ssr_modes_axum/README.md",
    "content": "# SSR Mode Axum Example\n\nThis example shows the different \"rendering modes\" that can be used while server-side rendering an application.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Server-Side Rendering Modes\n\n1. **Synchronous**: Serve an HTML shell that includes `fallback` for any `Suspense`. Load data on the client, replacing `fallback` once they're loaded.\n\n   - _Pros_: App shell appears very quickly: great TTFB (time to first byte).\n   - _Cons_: Resources load relatively slowly; you need to wait for JS + Wasm to load before even making a request.\n\n2. **Out-of-order streaming**: Serve an HTML shell that includes `fallback` for any `Suspense`. Load data on the **server**, streaming it down to the client as it resolves, and streaming down HTML for `Suspense` nodes.\n\n   - _Pros_: Combines the best of **synchronous** and **`async`**, with a very fast shell and resources that begin loading on the server.\n   - _Cons_: Requires JS for suspended fragments to appear in correct order. Weaker meta tag support when it depends on data that's under suspense (has already streamed down `<head>`)\n\n3. **In-order streaming**: Walk through the tree, returning HTML synchronously as in synchronous rendering and out-of-order streaming until you hit a `Suspense`. At that point, wait for all its data to load, then render it, then the rest of the tree.\n\n   - _Pros_: Does not require JS for HTML to appear in correct order.\n   - _Cons_: Loads the shell more slowly than out-of-order streaming or synchronous rendering because it needs to pause at every `Suspense`. Cannot begin hydration until the entire page has loaded, so earlier pieces\n     of the page will not be interactive until the suspended chunks have loaded.\n\n4. **`async`**: Load all resources on the server. Wait until all data are loaded, and render HTML in one sweep.\n   - _Pros_: Better handling for meta tags (because you know async data even before you render the `<head>`). Faster complete load than **synchronous** because async resources begin loading on server.\n   - _Cons_: Slower load time/TTFB: you need to wait for all async resources to load before displaying anything on the client.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/ssr_modes_axum/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/ssr_modes_axum/src/app.rs",
    "content": "use leptos::prelude::*;\nuse leptos_meta::{MetaTags, *};\nuse leptos_router::{\n    components::{FlatRoutes, ProtectedRoute, Route, Router},\n    hooks::use_params,\n    params::Params,\n    ParamSegment, SsrMode, StaticSegment,\n};\nuse serde::{Deserialize, Serialize};\n#[cfg(feature = \"ssr\")]\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::LazyLock;\nuse thiserror::Error;\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[cfg(feature = \"ssr\")]\nstatic IS_ADMIN: AtomicBool = AtomicBool::new(true);\n\n#[server]\npub async fn is_admin() -> Result<bool, ServerFnError> {\n    Ok(IS_ADMIN.load(Ordering::Relaxed))\n}\n\n#[server]\npub async fn set_is_admin(is_admin: bool) -> Result<(), ServerFnError> {\n    IS_ADMIN.store(is_admin, Ordering::Relaxed);\n    Ok(())\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n    let fallback = || view! { \"Page not found.\" }.into_view();\n    let toggle_admin = ServerAction::<SetIsAdmin>::new();\n    let is_admin =\n        Resource::new(move || toggle_admin.version().get(), |_| is_admin());\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/ssr_modes.css\"/>\n        <Title text=\"Welcome to Leptos\"/>\n        <Meta name=\"color-scheme\" content=\"dark light\"/>\n        <Router>\n            <nav>\n                <a href=\"/\">\"Home\"</a>\n                <a href=\"/admin\">\"Admin\"</a>\n                <Transition>\n                    <ActionForm action=toggle_admin>\n                        <input\n                            type=\"hidden\"\n                            name=\"is_admin\"\n                            value=move || {\n                                (!is_admin.get().and_then(|n| n.ok()).unwrap_or_default())\n                                    .to_string()\n                            }\n                        />\n\n                        <button>\n                            {move || {\n                                if is_admin.get().and_then(Result::ok).unwrap_or_default() {\n                                    \"Log Out\"\n                                } else {\n                                    \"Log In\"\n                                }\n                            }}\n\n                        </button>\n                    </ActionForm>\n                </Transition>\n            </nav>\n            <main>\n                <FlatRoutes fallback>\n                    // We’ll load the home page with out-of-order streaming and <Suspense/>\n                    <Route path=StaticSegment(\"\") view=HomePage/>\n\n                    // We'll load the posts with async rendering, so they can set\n                    // the title and metadata *after* loading the data\n                    <Route\n                        path=(StaticSegment(\"post\"), ParamSegment(\"id\"))\n                        view=Post\n                        ssr=SsrMode::Async\n                    />\n                    <Route\n                        path=(StaticSegment(\"post_in_order\"), ParamSegment(\"id\"))\n                        view=Post\n                        ssr=SsrMode::InOrder\n                    />\n                    <Route\n                        path=(StaticSegment(\"post_partially_blocked\"), ParamSegment(\"id\"))\n                        view=Post\n                    />\n                    <ProtectedRoute\n                        path=StaticSegment(\"admin\")\n                        view=Admin\n                        ssr=SsrMode::Async\n                        condition=move || is_admin.get().map(|n| n.unwrap_or(false))\n                        redirect_path=|| \"/\"\n                    />\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[component]\nfn HomePage() -> impl IntoView {\n    // load the posts\n    let posts = Resource::new(|| (), |_| list_post_metadata());\n    let posts = move || {\n        posts\n            .get()\n            .map(|n| n.unwrap_or_default())\n            .unwrap_or_default()\n    };\n\n    let posts2 = Resource::new(|| (), |_| list_post_metadata());\n    let posts2 = Resource::new(\n        || (),\n        move |_| async move { posts2.await.as_ref().map(Vec::len).unwrap_or(0) },\n    );\n\n    view! {\n        <h1>\"My Great Blog\"</h1>\n        <Suspense fallback=move || view! { <p>\"Loading posts...\"</p> }>\n            <p>\"number of posts: \" {Suspend::new(async move { posts2.await })}</p>\n        </Suspense>\n        <Suspense fallback=move || view! { <p>\"Loading posts...\"</p> }>\n            <ul>\n                <For each=posts key=|post| post.id let:post>\n                    <li>\n                        <a href=format!(\"/post/{}\", post.id)>{post.title.clone()}</a>\n                        \"|\"\n                        <a href=format!(\n                            \"/post_in_order/{}\",\n                            post.id,\n                        )>{post.title.clone()} \"(in order)\"</a>\n                        \"|\"\n                        <a href=format!(\n                            \"/post_partially_blocked/{}\",\n                            post.id,\n                        )>{post.title} \"(partially blocked)\"</a>\n                    </li>\n                </For>\n            </ul>\n        </Suspense>\n    }\n}\n\n#[derive(Params, Copy, Clone, Debug, PartialEq, Eq)]\npub struct PostParams {\n    id: Option<usize>,\n}\n\n#[component]\nfn Post() -> impl IntoView {\n    let query = use_params::<PostParams>();\n    let id = move || {\n        query.with(|q| {\n            q.as_ref()\n                .map(|q| q.id.unwrap_or_default())\n                .map_err(|_| PostError::InvalidId)\n        })\n    };\n    let post_resource = Resource::new_blocking(id, |id| async move {\n        match id {\n            Err(e) => Err(e),\n            Ok(id) => get_post(id)\n                .await\n                .map(|data| data.ok_or(PostError::PostNotFound))\n                .map_err(|_| PostError::ServerError),\n        }\n    });\n    let comments_resource = Resource::new(id, |id| async move {\n        match id {\n            Err(e) => Err(e),\n            Ok(id) => {\n                get_comments(id).await.map_err(|_| PostError::ServerError)\n            }\n        }\n    });\n\n    let post_view = Suspend::new(async move {\n        match post_resource.await {\n            Ok(Ok(post)) => {\n                Ok(view! {\n                    <h1>{post.title.clone()}</h1>\n                    <p>{post.content.clone()}</p>\n\n                    // since we're using async rendering for this page,\n                    // this metadata should be included in the actual HTML <head>\n                    // when it's first served\n                    <Title text=post.title/>\n                    <Meta name=\"description\" content=post.content/>\n                })\n            }\n            _ => Err(PostError::ServerError),\n        }\n    });\n    let comments_view = Suspend::new(async move {\n        match comments_resource.await {\n            Ok(comments) => Ok(view! {\n                <h1>\"Comments\"</h1>\n                <ul>\n                    {comments\n                        .into_iter()\n                        .map(|comment| view! { <li>{comment}</li> })\n                        .collect_view()}\n\n                </ul>\n            }),\n            _ => Err(PostError::ServerError),\n        }\n    });\n\n    view! {\n        <em>\"The world's best content.\"</em>\n        <Suspense fallback=move || view! { <p>\"Loading post...\"</p> }>\n            <ErrorBoundary fallback=|errors| {\n                view! {\n                    <div class=\"error\">\n                        <h1>\"Something went wrong.\"</h1>\n                        <ul>\n                            {move || {\n                                errors\n                                    .get()\n                                    .into_iter()\n                                    .map(|(_, error)| view! { <li>{error.to_string()}</li> })\n                                    .collect::<Vec<_>>()\n                            }}\n\n                        </ul>\n                    </div>\n                }\n            }>{post_view}</ErrorBoundary>\n        </Suspense>\n        <Suspense fallback=move || view! { <p>\"Loading comments...\"</p> }>{comments_view}</Suspense>\n    }\n}\n\n#[component]\npub fn Admin() -> impl IntoView {\n    view! { <p>\"You can only see this page if you're logged in.\"</p> }\n}\n\n// Dummy API\n\nstatic POSTS: LazyLock<[Post; 3]> = LazyLock::new(|| {\n    [\n        Post {\n            id: 0,\n            title: \"My first post\".to_string(),\n            content: \"This is my first post\".to_string(),\n        },\n        Post {\n            id: 1,\n            title: \"My second post\".to_string(),\n            content: \"This is my second post\".to_string(),\n        },\n        Post {\n            id: 2,\n            title: \"My third post\".to_string(),\n            content: \"This is my third post\".to_string(),\n        },\n    ]\n});\n\n#[derive(Error, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub enum PostError {\n    #[error(\"Invalid post ID.\")]\n    InvalidId,\n    #[error(\"Post not found.\")]\n    PostNotFound,\n    #[error(\"Server error.\")]\n    ServerError,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct Post {\n    id: usize,\n    title: String,\n    content: String,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct PostMetadata {\n    id: usize,\n    title: String,\n}\n\n#[server]\npub async fn list_post_metadata() -> Result<Vec<PostMetadata>, ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n    Ok(POSTS\n        .iter()\n        .map(|data| PostMetadata {\n            id: data.id,\n            title: data.title.clone(),\n        })\n        .collect())\n}\n\n#[server]\npub async fn get_post(id: usize) -> Result<Option<Post>, ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n    Ok(POSTS.iter().find(|post| post.id == id).cloned())\n}\n\n#[server]\npub async fn get_comments(id: usize) -> Result<Vec<String>, ServerFnError> {\n    tokio::time::sleep(std::time::Duration::from_secs(2)).await;\n    _ = id;\n    Ok(vec![\"Some comment\".into(), \"Some other comment\".into()])\n}\n"
  },
  {
    "path": "examples/ssr_modes_axum/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/ssr_modes_axum/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::{\n        http::{HeaderName, HeaderValue},\n        Router,\n    };\n    use leptos::{logging::log, prelude::*};\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use ssr_modes_axum::app::*;\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let leptos_options = conf.leptos_options;\n    // Generate the list of routes in your Leptos App\n    let routes = generate_route_list(App);\n\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler_with_context(\n            move || {\n                // if you want to add custom headers to the static file handler response,\n                // you can do that by providing `ResponseOptions` via context\n                let opts = use_context::<leptos_axum::ResponseOptions>()\n                    .unwrap_or_default();\n                opts.insert_header(\n                    HeaderName::from_static(\"cross-origin-opener-policy\"),\n                    HeaderValue::from_static(\"same-origin\"),\n                );\n                opts.insert_header(\n                    HeaderName::from_static(\"cross-origin-embedder-policy\"),\n                    HeaderValue::from_static(\"require-corp\"),\n                );\n                provide_context(opts);\n            },\n            shell,\n        ))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    log!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "examples/ssr_modes_axum/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n}"
  },
  {
    "path": "examples/static_routing/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n"
  },
  {
    "path": "examples/static_routing/Cargo.toml",
    "content": "[package]\nname = \"static_routing\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\nleptos = { path = \"../../leptos\", features = [\n  \"hydration\",\n] } #\"nightly\", \"hydration\"] }\nleptos_meta = { path = \"../../meta\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../router\" }\nlog = \"0.4.22\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nthiserror = \"2.0.12\"\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.39\", features = [\n  \"fs\",\n  \"rt-multi-thread\",\n  \"macros\",\n], optional = true }\ntokio-stream = { version = \"0.1\", features = [\"fs\"], optional = true }\nfutures = \"0.3\"\nwasm-bindgen = \"0.2.93\"\nnotify = { version = \"6\", optional = true }\nhttp = { version = \"1\", optional = true }\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"dep:tokio-stream\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"dep:leptos_axum\",\n  \"leptos_router/ssr\",\n  \"dep:notify\",\n  \"dep:http\",\n]\n\n[profile.release]\npanic = \"abort\"\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"ssr_modes\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"assets\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3007\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "examples/static_routing/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 henrik\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/static_routing/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"ssr_modes_axum\"\n"
  },
  {
    "path": "examples/static_routing/README.md",
    "content": "# Static Routing Example\n\nThis example shows the static routing features, which can be used to generate the HTML content for some routes before a request.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/static_routing/posts/post1.md",
    "content": "# My first blog post\n\nHaving a blog is *fun*.\n"
  },
  {
    "path": "examples/static_routing/posts/post2.md",
    "content": "# My second blog post\n\nComing up with content is hard.\n"
  },
  {
    "path": "examples/static_routing/posts/post3.md",
    "content": "# My third blog post\n\nCould I just have AI write this for me instead?\n"
  },
  {
    "path": "examples/static_routing/posts/post4.md",
    "content": "# My fourth post\n\nHere is some content. It should regenerate the static page.\n"
  },
  {
    "path": "examples/static_routing/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/static_routing/src/app.rs",
    "content": "use futures::{channel::mpsc, Stream};\nuse leptos::prelude::*;\nuse leptos_meta::{MetaTags, *};\nuse leptos_router::{\n    components::{FlatRoutes, Redirect, Route, Router},\n    hooks::use_params,\n    params::Params,\n    path,\n    static_routes::StaticRoute,\n    SsrMode,\n};\nuse serde::{Deserialize, Serialize};\nuse std::path::Path;\nuse thiserror::Error;\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n    let fallback = || view! { \"Page not found.\" }.into_view();\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/ssr_modes.css\"/>\n        <Title text=\"Welcome to Leptos\"/>\n        <Meta name=\"color-scheme\" content=\"dark light\"/>\n        <Router>\n            <nav>\n                <a href=\"/\">\"Home\"</a>\n            </nav>\n            <main>\n                <FlatRoutes fallback>\n                    <Route\n                        path=path!(\"/\")\n                        view=HomePage\n                        ssr=SsrMode::Static(\n                            StaticRoute::new().regenerate(|_| watch_path(Path::new(\"./posts\"))),\n                        )\n                    />\n\n                    <Route\n                        path=path!(\"/about\")\n                        view=move || view! { <Redirect path=\"/\"/> }\n                        ssr=SsrMode::Static(StaticRoute::new())\n                    />\n\n                    <Route\n                        path=path!(\"/post/:slug/\")\n                        view=Post\n                        ssr=SsrMode::Static(\n                            StaticRoute::new()\n                                .prerender_params(|| async move {\n                                    [(\"slug\".into(), list_slugs().await.unwrap_or_default())]\n                                        .into_iter()\n                                        .collect()\n                                })\n                                .regenerate(|params| {\n                                    let slug = params.get(\"slug\").unwrap();\n                                    watch_path(Path::new(&format!(\"./posts/{slug}.md\")))\n                                }),\n                        )\n                    />\n\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[component]\nfn HomePage() -> impl IntoView {\n    // load the posts\n    let posts = Resource::new(|| (), |_| list_posts());\n    let posts = move || {\n        posts\n            .get()\n            .map(|n| n.unwrap_or_default())\n            .unwrap_or_default()\n    };\n\n    view! {\n        <h1>\"My Great Blog\"</h1>\n        <Suspense fallback=move || view! { <p>\"Loading posts...\"</p> }>\n            <ul>\n                <For each=posts key=|post| post.slug.clone() let:post>\n                    <li>\n                        <a href=format!(\"/post/{}/\", post.slug)>{post.title.clone()}</a>\n                    </li>\n                </For>\n            </ul>\n        </Suspense>\n    }\n}\n\n#[derive(Params, Clone, Debug, PartialEq, Eq)]\npub struct PostParams {\n    slug: Option<String>,\n}\n\n#[component]\nfn Post() -> impl IntoView {\n    let query = use_params::<PostParams>();\n    let slug = move || {\n        query\n            .get()\n            .map(|q| q.slug.unwrap_or_default())\n            .map_err(|_| PostError::InvalidId)\n    };\n    let post_resource = Resource::new_blocking(slug, |slug| async move {\n        match slug {\n            Err(e) => Err(e),\n            Ok(slug) => get_post(slug)\n                .await\n                .map(|data| data.ok_or(PostError::PostNotFound))\n                .map_err(|e| PostError::ServerError(e.to_string())),\n        }\n    });\n\n    let post_view = move || {\n        Suspend::new(async move {\n            match post_resource.await {\n                Ok(Ok(post)) => {\n                    Ok(view! {\n                        <h1>{post.title.clone()}</h1>\n                        <p>{post.content.clone()}</p>\n\n                        // since we're using async rendering for this page,\n                        // this metadata should be included in the actual HTML <head>\n                        // when it's first served\n                        <Title text=post.title/>\n                        <Meta name=\"description\" content=post.content/>\n                    })\n                }\n                Ok(Err(e)) | Err(e) => {\n                    Err(PostError::ServerError(e.to_string()))\n                }\n            }\n        })\n    };\n\n    view! {\n        <em>\"The world's best content.\"</em>\n        <Suspense fallback=move || view! { <p>\"Loading post...\"</p> }>\n            <ErrorBoundary fallback=|errors| {\n                #[cfg(feature = \"ssr\")]\n                expect_context::<leptos_axum::ResponseOptions>()\n                    .set_status(http::StatusCode::NOT_FOUND);\n                view! {\n                    <div class=\"error\">\n                        <h1>\"Something went wrong.\"</h1>\n                        <ul>\n                            {move || {\n                                errors\n                                    .get()\n                                    .into_iter()\n                                    .map(|(_, error)| view! { <li>{error.to_string()}</li> })\n                                    .collect::<Vec<_>>()\n                            }}\n\n                        </ul>\n                    </div>\n                }\n            }>{post_view}</ErrorBoundary>\n        </Suspense>\n    }\n}\n\n#[derive(Error, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub enum PostError {\n    #[error(\"Invalid post ID.\")]\n    InvalidId,\n    #[error(\"Post not found.\")]\n    PostNotFound,\n    #[error(\"Server error: {0}.\")]\n    ServerError(String),\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct Post {\n    slug: String,\n    title: String,\n    content: String,\n}\n\n#[server]\npub async fn list_slugs() -> Result<Vec<String>, ServerFnError> {\n    use tokio::fs;\n    use tokio_stream::{wrappers::ReadDirStream, StreamExt};\n\n    let files = ReadDirStream::new(fs::read_dir(\"./posts\").await?);\n    Ok(files\n        .filter_map(|entry| {\n            let entry = entry.ok()?;\n            let path = entry.path();\n            if !path.is_file() {\n                return None;\n            }\n            let extension = path.extension()?;\n            if extension != \"md\" {\n                return None;\n            }\n\n            let slug = path\n                .file_name()\n                .and_then(|n| n.to_str())\n                .unwrap_or_default()\n                .replace(\".md\", \"\");\n            Some(slug)\n        })\n        .collect()\n        .await)\n}\n\n#[server]\npub async fn list_posts() -> Result<Vec<Post>, ServerFnError> {\n    println!(\"calling list_posts\");\n\n    use futures::TryStreamExt;\n    use tokio::fs;\n    use tokio_stream::wrappers::ReadDirStream;\n\n    let files = ReadDirStream::new(fs::read_dir(\"./posts\").await?);\n    files\n        .try_filter_map(|entry| async move {\n            let path = entry.path();\n            if !path.is_file() {\n                return Ok(None);\n            }\n            let Some(extension) = path.extension() else {\n                return Ok(None);\n            };\n            if extension != \"md\" {\n                return Ok(None);\n            }\n\n            let slug = path\n                .file_name()\n                .and_then(|n| n.to_str())\n                .unwrap_or_default()\n                .replace(\".md\", \"\");\n            let content = fs::read_to_string(path).await?;\n            // world's worst Markdown frontmatter parser\n            let title = content.lines().next().unwrap().replace(\"# \", \"\");\n\n            Ok(Some(Post {\n                slug,\n                title,\n                content,\n            }))\n        })\n        .try_collect()\n        .await\n        .map_err(ServerFnError::from)\n}\n\n#[server]\npub async fn get_post(slug: String) -> Result<Option<Post>, ServerFnError> {\n    println!(\"reading ./posts/{slug}.md\");\n    let content =\n        tokio::fs::read_to_string(&format!(\"./posts/{slug}.md\")).await?;\n    // world's worst Markdown frontmatter parser\n    let title = content.lines().next().unwrap().replace(\"# \", \"\");\n\n    Ok(Some(Post {\n        slug,\n        title,\n        content,\n    }))\n}\n\n#[allow(unused)] // path is not used in non-SSR\nfn watch_path(path: &Path) -> impl Stream<Item = ()> {\n    #[allow(unused)]\n    let (mut tx, rx) = mpsc::channel(0);\n\n    #[cfg(feature = \"ssr\")]\n    {\n        use notify::{RecursiveMode, Watcher};\n\n        let mut watcher =\n            notify::recommended_watcher(move |res: Result<_, _>| {\n                if res.is_ok() {\n                    // if this fails, it's because the buffer is full\n                    // this means we've already notified before it's regenerated,\n                    // so this page will be queued for regeneration already\n                    _ = tx.try_send(());\n                }\n            })\n            .expect(\"could not create watcher\");\n\n        // Add a path to be watched. All files and directories at that path and\n        // below will be monitored for changes.\n        watcher\n            .watch(path, RecursiveMode::NonRecursive)\n            .expect(\"could not watch path\");\n\n        // we want this to run as long as the server is alive\n        std::mem::forget(watcher);\n    }\n\n    rx\n}\n"
  },
  {
    "path": "examples/static_routing/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/static_routing/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use leptos::{logging::log, prelude::*};\n    use leptos_axum::{generate_route_list_with_ssg, LeptosRoutes};\n    use static_routing::app::*;\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let leptos_options = conf.leptos_options;\n    // Generate the list of routes in your Leptos App\n    let (routes, static_routes) = generate_route_list_with_ssg({\n        let leptos_options = leptos_options.clone();\n        move || shell(leptos_options.clone())\n    });\n\n    static_routes.generate(&leptos_options).await;\n\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    log!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "examples/static_routing/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n}"
  },
  {
    "path": "examples/stores/Cargo.toml",
    "content": "[package]\nname = \"stores\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\nopt-level = 'z'\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nreactive_stores = { path = \"../../reactive_stores\" }\nreactive_stores_macro = { path = \"../../reactive_stores_macro\" }\nconsole_error_panic_hook = \"0.1.7\"\nchrono = { version = \"0.4.38\", features = [\"serde\"] }\nserde = { version = \"1.0.210\", features = [\"derive\"] }\nserde_json = \"1.0.128\"\n\n[dev-dependencies]\nwasm-bindgen = \"0.2.93\"\nwasm-bindgen-test = \"0.3.42\"\nweb-sys = \"0.3.70\"\n"
  },
  {
    "path": "examples/stores/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n]\n"
  },
  {
    "path": "examples/stores/README.md",
    "content": "# Stores Example\n\nThis example shows how to use reactive stores, by building a client-side rendered TODO application.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/stores/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t\t<style>\n\t\t.hidden {\n\t\t\tdisplay: none;\n\t\t}\n\t\t</style>\n\t</head>\n\t<body></body>\n</html>\n"
  },
  {
    "path": "examples/stores/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/stores/src/lib.rs",
    "content": "use chrono::{Local, NaiveDate};\nuse leptos::{logging::warn, prelude::*};\nuse reactive_stores::{Field, KeyMap, Patch, PatchField, Store, StorePath};\nuse serde::{Deserialize, Serialize};\nuse std::{\n    collections::BTreeMap,\n    sync::{\n        atomic::{AtomicUsize, Ordering},\n        Arc,\n    },\n};\n\n// ID starts higher than 0 because we have a few starting todos by default\nstatic NEXT_ID: AtomicUsize = AtomicUsize::new(3);\n\n#[derive(Debug, Store, Patch, Serialize, Deserialize)]\nstruct Todos {\n    /// Current user.\n    user: User,\n    /// Vector storage of a collection of todo's.\n    #[store(key: usize = |todo| todo.id)]\n    todos: Vec<Todo>,\n    /// User names to todo IDs.\n    #[store(key: Arc<String> = |(name, _)| name.clone())]\n    completed: BTreeMap<Arc<String>, usize>,\n}\n\nimpl Todos {\n    fn data() -> Self {\n        Self {\n            user: User {\n                name: \"Bob\".to_string(),\n                email: \"lawblog@bobloblaw.com\".into(),\n            },\n            todos: vec![\n                Todo {\n                    id: 0,\n                    label: \"Create reactive store\".to_string(),\n                    status: Status::Pending,\n                },\n                Todo {\n                    id: 1,\n                    label: \"???\".to_string(),\n                    status: Status::Pending,\n                },\n                Todo {\n                    id: 2,\n                    label: \"Profit\".to_string(),\n                    status: Status::Pending,\n                },\n            ],\n            completed: Default::default(),\n        }\n    }\n}\n\n#[derive(Debug, Store, Patch, Serialize, Deserialize)]\nstruct User {\n    name: String,\n    email: String,\n}\n\n#[derive(Debug, Store, Patch, Serialize, Deserialize)]\nstruct Todo {\n    id: usize,\n    label: String,\n    status: Status,\n}\nimpl Todo {\n    pub fn new(label: impl ToString) -> Self {\n        Self {\n            id: NEXT_ID.fetch_add(1, Ordering::Relaxed),\n            label: label.to_string(),\n            status: Status::Pending,\n        }\n    }\n}\n\n#[derive(Debug, Default, Clone, Store, Serialize, Deserialize, PartialEq)]\nenum Status {\n    #[default]\n    Pending,\n    Scheduled,\n    ScheduledFor {\n        date: NaiveDate,\n    },\n    Done,\n}\nimpl Status {\n    pub fn next_step(&mut self) {\n        *self = match self {\n            Status::Pending => Status::ScheduledFor {\n                date: Local::now().naive_local().into(),\n            },\n            Status::Scheduled | Status::ScheduledFor { .. } => Status::Done,\n            Status::Done => Status::Done,\n        };\n    }\n}\n\nimpl PatchField for Status {\n    fn patch_field(\n        &mut self,\n        new: Self,\n        path: &StorePath,\n        notify: &mut dyn FnMut(&StorePath),\n        _keys: Option<&KeyMap>,\n    ) {\n        if *self != new {\n            *self = new;\n            notify(path);\n        }\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    let store = Store::new(Todos::data());\n\n    let input_ref = NodeRef::new();\n\n    view! {\n        <p>\"Hello, \" {move || store.user().name().get()}</p>\n        <UserForm user=store.user() />\n        <UserAchievements store />\n        <hr />\n        <form on:submit=move |ev| {\n            ev.prevent_default();\n            store.todos().write().push(Todo::new(input_ref.get().unwrap().value()));\n        }>\n            <label>\"Add a Todo\" <input type=\"text\" node_ref=input_ref /></label>\n            <input type=\"submit\" />\n        </form>\n        <ol>\n            // because `todos` is a keyed field, `store.todos()` returns a struct that\n            // directly implements IntoIterator, so we can use it in <For/> and\n            // it will manage reactivity for the store fields correctly\n            <For each=move || store.todos() key=|row| row.id().get() let:todo>\n                <TodoRow store todo />\n            </For>\n\n        </ol>\n        <pre>{move || serde_json::to_string_pretty(&*store.read())}</pre>\n    }\n}\n\n#[component]\nfn UserForm(#[prop(into)] user: Field<User>) -> impl IntoView {\n    let error = RwSignal::new(None);\n\n    view! {\n        {move || error.get().map(|n| view! { <p>{n}</p> })}\n        <form on:submit:target=move |ev| {\n            ev.prevent_default();\n            match User::from_event(&ev) {\n                Ok(new_user) => {\n                    error.set(None);\n                    user.patch(new_user);\n                }\n                Err(e) => error.set(Some(e.to_string())),\n            }\n        }>\n            <label>\n                \"Name\" <input type=\"text\" name=\"name\" prop:value=move || user.name().get() />\n            </label>\n            <label>\n                \"Email\" <input type=\"email\" name=\"email\" prop:value=move || user.email().get() />\n            </label>\n            <input type=\"submit\" />\n        </form>\n    }\n}\n\n#[component]\nfn UserAchievements(store: Store<Todos>) -> impl IntoView {\n    let completed = Memo::new(move |_| {\n        store\n            .completed()\n            .at_key(store.user().name().get().into())\n            .try_get()\n            .unwrap_or_default()\n            .to_string()\n    });\n    view! {\n        <div>\"You completed: \" {completed} \" tasks\"</div>\n        <ul>\n            <For each=move || store.completed() key=|row| row.key() let:achievement>\n                <div>{achievement.key().to_uppercase()}: {move || achievement.get()}</div>\n            </For>\n        </ul>\n    }\n}\n\n#[component]\nfn TodoRow(\n    store: Store<Todos>,\n    #[prop(into)] todo: Field<Todo>,\n) -> impl IntoView {\n    let status = todo.status();\n    let title = todo.label();\n\n    let editing = RwSignal::new(true);\n\n    view! {\n        <li style:text-decoration=move || {\n            if status.done() { \"line-through\" } else { Default::default() }\n        }>\n\n            <p\n                class:hidden=move || editing.get()\n                on:click=move |_| {\n                    editing.update(|n| *n = !*n);\n                }\n            >\n                {move || title.get()}\n            </p>\n            <input\n                class:hidden=move || !(editing.get())\n                type=\"text\"\n                prop:value=move || title.get()\n                on:change=move |ev| {\n                    title.set(event_target_value(&ev));\n                }\n            />\n\n            <button on:click=move |_| {\n                let was_undone = !status.done();\n                status.write().next_step();\n                if was_undone && status.done() {\n                    let name = store.user().name().get();\n                    store\n                        .completed()\n                        .update(|completed| *completed.entry(name.into()).or_default() += 1)\n                }\n            }>\n                {move || {\n                    if todo.status().done() {\n                        \"Done\"\n                    } else if status.scheduled() || status.scheduled_for() {\n                        \"Scheduled\"\n                    } else {\n                        \"Pending\"\n                    }\n                }}\n\n            </button>\n\n            <button on:click=move |_| {\n                let id = todo.id().get();\n                store.todos().write().retain(|todo| todo.id != id);\n            }>\"X\"</button>\n            <input\n                type=\"date\"\n                prop:value=move || {\n                    todo.status().scheduled_for_date().map(|n| n.get().to_string())\n                }\n\n                class:hidden=move || !todo.status().scheduled_for()\n                on:change:target=move |ev| {\n                    if let Some(date) = todo.status().scheduled_for_date() {\n                        let value = ev.target().value();\n                        match NaiveDate::parse_from_str(&value, \"%Y-%m-%d\") {\n                            Ok(new_date) => {\n                                date.set(new_date);\n                            }\n                            Err(e) => warn!(\"{e}\"),\n                        }\n                    }\n                }\n            />\n\n        </li>\n    }\n}\n"
  },
  {
    "path": "examples/stores/src/main.rs",
    "content": "use leptos::prelude::*;\nuse stores::App;\n\npub fn main() {\n    console_error_panic_hook::set_once();\n    mount_to_body(App)\n}\n"
  },
  {
    "path": "examples/subsecond_hot_patch/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target\n.DS_Store\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n"
  },
  {
    "path": "examples/subsecond_hot_patch/Cargo.toml",
    "content": "[package]\nname = \"subsecond_hot_patch\"\nversion = \"0.1.0\"\nauthors = [\"Greg Johnston <greg.johnston@gmail.com>\"]\nedition = \"2021\"\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\", \"subsecond\"] }\nleptos_router = { path = \"../../router\" }\n\n[features]\ndefault = [\"web\"]\nweb = []\n"
  },
  {
    "path": "examples/subsecond_hot_patch/Dioxus.toml",
    "content": "[application]\n\n[web.app]\n\n# HTML title tag content\ntitle = \"ltest\"\n\n# include `assets` in web platform\n[web.resource]\n\n# Additional CSS style files\nstyle = []\n\n# Additional JavaScript files\nscript = []\n\n[web.resource.dev]\n\n# Javascript code file\n# serve: [dev-server] only\nscript = []\n"
  },
  {
    "path": "examples/subsecond_hot_patch/Makefile.toml",
    "content": "extend = [{ path = \"../cargo-make/main.toml\" }]\n"
  },
  {
    "path": "examples/subsecond_hot_patch/README.md",
    "content": "# Hot Patching with `dx`\n\nThis is an experimental example exploring how to combine Leptos with the binary hot-patching provided by Dioxus's `subsecond` library and `dx` cli.\n\n### Serving Your App\n\nThis requires installing the Dioxus CLI version 0.7.0. At the time I'm writing this README, that does not yet have a stable release. Once `dioxus-cli` 0.7.0 has been released, you should use the latest stable release. Until then, I'd suggest installing from git:\n\n```sh\ncargo install dioxus-cli --git https://github.com/DioxusLabs/dioxus\n```\n\nThen you can run the example with `dx serve --hot-patch --platform web`.\n\n### Hot Patching\n\nChanges to the your application should be reflected in your app without a full rebuild and reload.\n\n### Limitatations\n\nCurrently we only support hot-patching for reactive view functions. You probably want to use `AnyView` (via `.into_any()`) on any views that will be hot-patched, so they can be rebuilt correctly despite their types changing when the structure of the view tree changes.\n\nIf you are using `leptos_router` this actually works quite well, as every route’s view is erased to `AnyView` and the router itself is a reactive view function: in other words, changes inside any route should be hot-patched in any case.\n\nNote that any hot-patch will cause all render effects to run again. This means that some client-side state (like the values of signals) will be wiped out.\n\n### Build Tooling\n\nThe preference of the Dioxus team is that all hot-patching work that uses their `subsecond` also use `dioxus-cli`. As this demo shows, it's completely possible to use `dioxus-cli` to build and run a Leptos project. We do not plan to build `subsecond` into our own build tooling at this time.\n\n**This is an experiment/POC. It is being published because members of the community have found it useful and have asked for the support to be merged in its current state. Further development and bugfixes are a relatively low priority at this time.**\n"
  },
  {
    "path": "examples/subsecond_hot_patch/assets/main.css",
    "content": "/* App-wide styling */\nbody {\n    background-color: #0f1116;\n    color: #ffffff;\n    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n    margin: 20px;\n}\n\n#hero {\n    margin: 0;\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n}\n\n#links {\n    width: 400px;\n    text-align: left;\n    font-size: x-large;\n    color: white;\n    display: flex;\n    flex-direction: column;\n}\n\n#links a {\n    color: white;\n    text-decoration: none;\n    margin-top: 20px;\n    margin: 10px 0px;\n    border: white 1px solid;\n    border-radius: 5px;\n    padding: 10px;\n}\n\n#links a:hover {\n    background-color: #1f1f1f;\n    cursor: pointer;\n}\n\n#header {\n    max-width: 1200px;\n}\n\n\n\n"
  },
  {
    "path": "examples/subsecond_hot_patch/src/main.rs",
    "content": "use leptos::{prelude::*, subsecond::connect_to_hot_patch_messages};\nuse leptos_router::{\n    components::{Route, Router, Routes},\n    path,\n};\n\nfn main() {\n    // connect to DX CLI and patch the WASM binary whenever we receive a message\n    connect_to_hot_patch_messages();\n\n    // wrapping App here in a closure so we can hot-reload it, because we only do that\n    // for reactive views right now. changing anything will re-run App and update the view\n    mount_to_body(|| App);\n}\n\n#[component]\nfn App() -> impl IntoView {\n    view! {\n        <nav>\n            <a href=\"/\">\"Home\"</a>\n            <a href=\"/about\">\"About\"</a>\n        </nav>\n        <Router>\n            <Routes fallback=|| \"Not found\">\n                <Route path=path!(\"/\") view=HomePage/>\n                <Route path=path!(\"/about\") view=About/>\n            </Routes>\n        </Router>\n    }\n}\n\n#[component]\nfn HomePage() -> impl IntoView {\n    view! {\n        <h1>\"Home Page\"</h1>\n    }\n}\n\n#[component]\nfn About() -> impl IntoView {\n    view! {\n        <h1>\"About\"</h1>\n    }\n}\n"
  },
  {
    "path": "examples/suspense_tests/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n"
  },
  {
    "path": "examples/suspense_tests/Cargo.toml",
    "content": "[package]\nname = \"suspense_tests\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nactix-files = { version = \"0.6.6\", optional = true }\nactix-web = { version = \"4.8\", optional = true, features = [\"macros\"] }\nconsole_error_panic_hook = \"0.1.7\"\njs-sys = { version = \"0.3.72\" }\nleptos = { path = \"../../leptos\" }\nleptos_actix = { path = \"../../integrations/actix\", optional = true }\nleptos_router = { path = \"../../router\" }\nlog = \"0.4.22\"\nwasm-bindgen = \"0.2.93\"\nserde = \"1.0\"\ntokio = { version = \"1.39\", features = [\"time\", \"rt\"], optional = true }\n\n[features]\nhydrate = [\n\n  \"leptos/hydrate\",\n]\nssr = [\n  \"dep:actix-files\",\n  \"dep:actix-web\",\n  \"dep:leptos_actix\",\n  \"leptos/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:tokio\",\n]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name   \noutput-name = \"suspense_tests\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\t\nsite-pkg-dir = \"pkg\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"cargo make test-ui\"\nend2end-dir = \"e2e\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/suspense_tests/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 henrik\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/suspense_tests/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos-webdriver-test.toml\" },\n]\n\n[env]\nCLIENT_PROCESS_NAME = \"suspense_tests\"\n\n[tasks.test-ui]\ncwd = \"./e2e\"\ncommand = \"cargo\"\nargs = [\"make\", \"test-ui\", \"${@}\"]\n"
  },
  {
    "path": "examples/suspense_tests/README.md",
    "content": "# Suspense Test Example\n\nThis example demonstrates the `<Suspense/>` behavior.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Test Strategy\n\nSee the [E2E README](./e2e/README.md) to learn about the web testing strategy for this project.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/suspense_tests/e2e/Cargo.toml",
    "content": "[package]\nname = \"suspense_tests_e2e\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\nanyhow = \"1.0\"\nasync-trait = \"0.1.81\"\ncucumber = \"0.21.1\"\nfantoccini = \"0.21.1\"\npretty_assertions = \"1.4\"\nserde_json = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\", \"time\"] }\nurl = \"2.5\"\n\n[[test]]\nname = \"app_suite\"\nharness = false    # Allow Cucumber to print output instead of libtest\n"
  },
  {
    "path": "examples/suspense_tests/e2e/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.test]\nenv = { RUN_AUTOMATICALLY = false }\ncondition = { env_true = [\"RUN_AUTOMATICALLY\"] }\n\n[tasks.ci]\n\n[tasks.test-ui]\ncommand = \"cargo\"\nargs = [\n  \"test\",\n  \"--test\",\n  \"app_suite\",\n  \"--\",\n  \"--retry\",\n  \"2\",\n  \"--retry-after\",\n  \"5sec\",\n  \"--fail-fast\",\n  \"${@}\",\n]\n"
  },
  {
    "path": "examples/suspense_tests/e2e/README.md",
    "content": "# E2E Testing\n\nThis example demonstrates e2e testing with Rust using executable requirements.\n\n## Testing Stack\n\n|    |      Role      |  Description |\n|---|---|---|\n| [Cucumber](https://github.com/cucumber-rs/cucumber/tree/main) | Test Runner | Run [Gherkin](https://cucumber.io/docs/gherkin/reference/) specifications as Rust tests |\n| [Fantoccini](https://github.com/jonhoo/fantoccini/tree/main) | Browser Client | Interact with web pages through WebDriver |\n| [Cargo Leptos](https://github.com/leptos-rs/cargo-leptos) | Build Tool |  Compile example and start the server and end-2-end tests |\n| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome |\n\n## Testing Organization\n\nTesting is organized around what a user can do and see/not see. Test scenarios are grouped by the **user action** and the **object** of that action. This makes it easier to locate and reason about requirements.\n\nHere is a brief overview of how things fit together.\n\n```bash\nfeatures\n└── {action}_{object}.feature   # Specify test scenarios\ntests\n├── fixtures\n│   ├── action.rs               # Perform a user action (click, type, etc.)\n│   ├── check.rs                # Assert what a user can see/not see\n│   ├── find.rs                 # Query page elements\n│   ├── mod.rs\n│   └── world\n│       ├── action_steps.rs     # Map Gherkin steps to user actions\n│       ├── check_steps.rs      # Map Gherkin steps to user expectations\n│       └── mod.rs\n└── app_suite.rs                # Test main \n```\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/check_aria_current.feature",
    "content": "@check_aria_current\nFeature: Check aria-current being applied to make links bolded\n\n    Background:\n\n        Given I see the app\n\n    Scenario: Should see the base case working\n        Then I see the Out-of-Order link being bolded\n        And I see the following links being bolded\n            | Out-of-Order |\n            | Nested       |\n        And I see the In-Order link not being bolded\n        And I see the following links not being bolded\n            | In-Order     |\n            | Single       |\n\n    Scenario: Should see client-side render the correct bolded links\n        When I select the link In-Order\n        And I select the link Single\n        Then I see the following links being bolded\n            | In-Order     |\n            | Single       |\n        And I see the following links not being bolded\n            | Out-of-Order |\n            | Nested       |\n\n    Scenario: Should see server-side render the correct bolded links\n        When I select the link In-Order\n        And I select the link Single\n        And I reload the page\n        Then I see the following links being bolded\n            | In-Order     |\n            | Single       |\n        And I see the following links not being bolded\n            | Out-of-Order |\n            | Nested       |\n\n    Scenario: Check that the base nested route links are working\n        When I select the link Instrumented\n        Then I see the Instrumented link being bolded\n        And I see the Item Listing link not being bolded\n\n    Scenario: Should see going deep down into nested routes bold links\n        When I select the link Instrumented\n        And I select the link Target 421\n        Then I see the following links being bolded\n            | Instrumented |\n            | Item Listing |\n            | Target 4##   |\n            | Target 42#   |\n            | Target 421   |\n            | field1       |\n\n    Scenario: Should see going deep down into nested routes in SSR bold links\n        When I select the link Instrumented\n        And I select the link Target 421\n        And I reload the page\n        Then I see the following links being bolded\n            | Instrumented |\n            | Item Listing |\n            | Target 4##   |\n            | Target 42#   |\n            | Target 421   |\n            | field1       |\n\n    Scenario: Going deep down navigate around nested links bold correctly\n        When I select the link Instrumented\n        And I select the link Target 421\n        And I select the link Inspect path2/field3\n        Then I see the following links being bolded\n            | Instrumented |\n            | Item Listing |\n            | Target 4##   |\n            | Target 42#   |\n            | field3       |\n        And I see the following links not being bolded\n            | Target 421   |\n            | field1       |\n\n    Scenario: Going deep down navigate around nested links bold correctly, SSR\n        When I select the link Instrumented\n        And I select the link Target 421\n        And I select the link Inspect path2/field3\n        And I reload the page\n        Then I see the following links being bolded\n            | Instrumented |\n            | Item Listing |\n            | Target 4##   |\n            | Target 42#   |\n            | field3       |\n        And I see the following links not being bolded\n            | Target 421   |\n            | field1       |\n\n    Scenario: Going deep down back out nested routes reset bolded states\n        When I select the link Instrumented\n        And I select the link Target 421\n        And I select the link Counters\n        Then I see the following links being bolded\n            | Instrumented |\n            | Counters     |\n        And I see the following links not being bolded\n            | Item Listing |\n            | Target 4##   |\n            | Target 42#   |\n            | Target 421   |\n\n    Scenario: Going deep down back out nested routes reset bolded states, SSR\n        When I select the link Instrumented\n        And I select the link Target 421\n        And I select the link Counters\n        And I reload the page\n        Then I see the following links being bolded\n            | Instrumented |\n            | Counters     |\n        And I see the following links not being bolded\n            | Item Listing |\n            | Target 4##   |\n            | Target 42#   |\n            | Target 421   |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/check_instrumented.feature",
    "content": "@check_instrumented\nFeature: Instrumented Counters showing the expected values\n\n    Scenario: I can fresh CSR instrumented counters\n        Given I see the app\n        When I access the instrumented counters via CSR\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 0 |\n            | item_overview      | 0 |\n            | item_inspect       | 0 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    Scenario: I should see counter going up after viewing Item Listing\n        Given I see the app\n        When I select the following links\n            | Instrumented |\n            | Item Listing |\n            | Counters     |\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 1 |\n            | item_overview      | 0 |\n            | item_inspect       | 0 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 1 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    # the reload has happened in Item Listing, it follows a suspend\n    # will be called as hydration happens.\n    Scenario: Refreshing Item Listing should have only suspend counters\n        Given I see the app\n        When I access the instrumented counters via SSR\n        And I select the component Item Listing\n        And I reload the page\n        And I select the component Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 1 |\n            | item_overview      | 0 |\n            | item_inspect       | 0 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    Scenario: Reset CSR Counters work as expected.\n        Given I see the app\n        When I access the instrumented counters via SSR\n        And I select the component Item Listing\n        And I click on Reset CSR Counters\n        And I select the component Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 0 |\n            | item_overview      | 0 |\n            | item_inspect       | 0 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    Scenario: Standard usage of the instruments traversing down\n        Given I see the app\n        When I select the following links\n            | Instrumented         |\n            | Item Listing         |\n            | Item 2               |\n            | Inspect path3        |\n            | Inspect path3/field1 |\n        And I access the instrumented counters via CSR\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 1 |\n            | item_overview      | 1 |\n            | item_inspect       | 2 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 1 |\n            | get_item           | 1 |\n            | inspect_item_root  | 1 |\n            | inspect_item_field | 1 |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/check_instrumented_issue_3719.feature",
    "content": "@check_instrumented_issue_3719\nFeature: Using instrumented counters to test regression from #3502.\n    Check that the suspend/suspense and the underlying resources are\n    called with the expected number of times.  If this was already in\n    place by #3502 (5c43c18) it should have caught this regression.\n    For a better minimum demonstration see #3719.\n\n    Background:\n\n        Given I see the app\n        And I select the mode Instrumented\n\n    Scenario: follow all paths via CSR avoids #3502\n        Given I select the following links\n            | Item Listing         |\n            | Item 1               |\n            | Inspect path2        |\n            | Inspect path2/field3 |\n        And I click on Reset CSR Counters\n        When I select the following links\n            | Inspect path2/field1 |\n            | Inspect path2/field2 |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 0 |\n            | item_overview      | 0 |\n            | item_inspect       | 2 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 2 |\n\n    # To show that starting directly from within a param will simply\n    # cause the problem.\n    Scenario: Quicker way to demonstrate regression caused by #3502\n        Given I select the link Target 123\n        # And I click on Reset CSR Counters\n        When I select the following links\n            | Inspect path2/field1 |\n            | Inspect path2/field2 |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 0 |\n            | item_overview      | 0 |\n            | item_inspect       | 3 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 1 |\n            | get_item           | 1 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 3 |\n\n    Scenario: Follow paths ordinarily down to a target\n        Given I select the following links\n            | Item Listing         |\n            | Item 1               |\n        And I click on Reset CSR Counters\n        When I select the following links\n            | Target 4## |\n            | Target 3## |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 0 |\n            | item_overview      | 2 |\n            | item_inspect       | 0 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 2 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    Scenario: Same as above, but add a refresh to test hydration\n        Given I select the following links\n            | Item Listing         |\n            | Item 1               |\n        And I refresh the page\n        And I click on Reset CSR Counters\n        When I select the following links\n            | Target 4## |\n            | Target 3## |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 0 |\n            | item_overview      | 2 |\n            | item_inspect       | 0 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 2 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/check_instrumented_suspense_resource.feature",
    "content": "@check_instrumented_suspense_resource\nFeature: Using instrumented counters for real\n    Check that the suspend/suspense and the underlying resources are\n    called with the expected number of times for CSR rendering.\n\n    Background:\n\n        Given I see the app\n        And I select the mode Instrumented\n\n    Scenario: Emulate steps 1 to 5 of issue #2961\n        Given I select the link Target 3##\n        And I refresh the page\n        When I select the following links\n            | Item Listing |\n            | Target 4##   |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 1 |\n            | item_overview      | 2 |\n            | item_inspect       | 0 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 1 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    Scenario: Emulate step 6 of issue #2961\n        Given I select the link Target 41#\n        And I refresh the page\n        When I select the following links\n            | Target 4##   |\n            | Target 42#   |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 0 |\n            | item_overview      | 1 |\n            | item_inspect       | 2 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 1 |\n            | inspect_item_field | 0 |\n\n    Scenario: Emulate step 7 of issue #2961\n        Given I select the link Target 42#\n        And I refresh the page\n        When I select the following links\n            | Target 4##   |\n            | Target 42#   |\n            | Target 41#   |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 0 |\n            | item_overview      | 1 |\n            | item_inspect       | 3 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 2 |\n            | inspect_item_field | 0 |\n\n    Scenario: Emulate step 8, \"not trigger double fetch\".\n        Given I select the link Target 3##\n        And I refresh the page\n        When I select the following links\n            | Item Listing |\n            | Target 4##   |\n            | Target 41#   |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 1 |\n            | item_overview      | 2 |\n            | item_inspect       | 1 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 1 |\n            | inspect_item_root  | 1 |\n            | inspect_item_field | 0 |\n\n    Scenario: Like above, for the \"double fetch\" which shouldn't happen\n        Given I select the link Target 3##\n        And I refresh the page\n        When I select the following links\n            | Item Listing |\n            | Target 4##   |\n            | Target 41#   |\n            | Target 3##   |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 1 |\n            | item_overview      | 3 |\n            | item_inspect       | 1 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 2 |\n            | inspect_item_root  | 1 |\n            | inspect_item_field | 0 |\n\n    Scenario: Like above, but using 4## instead\n        Given I select the link Target 3##\n        And I refresh the page\n        When I select the following links\n            | Item Listing |\n            | Target 4##   |\n            | Target 41#   |\n            | Target 4##   |\n        And I go check the Counters\n        Then I see the following counters under section\n            | Suspend Calls      |   |\n            | item_listing       | 1 |\n            | item_overview      | 3 |\n            | item_inspect       | 1 |\n        And the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 1 |\n            | inspect_item_root  | 1 |\n            | inspect_item_field | 0 |\n\n    # The following tests previously showed the clear difference between\n    # hydration and CSR, where hydration resulting in extra server API\n    # calls via the resource while CSR did not suffer from the issue.\n    # With #3182 merged the issue is corrected, going up to components\n    # specified by the parent route should no longer result in the\n    # superfluous fetches for resources needed by component about to be\n    # unmounted.\n    Scenario: Emulate part of step 8 of issue #2961\n        Given I select the link Target 3##\n        And I refresh the page\n        When I select the link Item Listing\n        And I go check the Counters\n        Then I see the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    Scenario: Emulate above, instead of refresh page, reset csr counters\n        Given I select the link Target 3##\n        And I click on Reset CSR Counters\n        When I select the link Item Listing\n        And I go check the Counters\n        Then I see the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    # Further two sets for good measure.\n    Scenario: Start with hydration from Target 41# and go up\n        Given I select the link Target 41#\n        And I refresh the page\n        When I select the link Target 4##\n        And I select the link Item Listing\n        And I go check the Counters\n        Then I see the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n\n    Scenario: Emulate the same csr counter reset, for Target 41#.\n        Given I select the link Target 41#\n        And I click on Reset CSR Counters\n        When I select the link Target 4##\n        And I select the link Item Listing\n        And I go check the Counters\n        Then I see the following counters under section\n            | Server Calls (CSR) |   |\n            | list_items         | 0 |\n            | get_item           | 0 |\n            | inspect_item_root  | 0 |\n            | inspect_item_field | 0 |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/click_inside_component_count.feature",
    "content": "@click_inside_component_count\nFeature: Click Inside Component Count\n\n    Background:\n\n        Given I see the app\n\n    Scenario Outline: Should increase the count\n\n        Given I select the mode <Mode>\n        And I select the component Inside Component\n        When I click the count 3 times\n        Then I see the count is 3\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |"
  },
  {
    "path": "examples/suspense_tests/e2e/features/click_nested_count.feature",
    "content": "@click_nested_count\nFeature: Click Nested Count\n\n    Background:\n\n        Given I see the app\n\n    Scenario Outline: Should increase the count\n\n        Given I select the mode <Mode>\n        And I select the component Nested\n        When I click the count 3 times\n        Then I see the count is 3\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/click_nested_inside_count.feature",
    "content": "@click_nested_inside_count\nFeature: Click Nested Inside Count\n\n    Background:\n\n        Given I see the app\n\n    Scenario Outline: Should increase the count\n\n        Given I select the mode <Mode>\n        And I select the component Nested (resource created inside)\n        When I click the count 3 times\n        Then I see the count is 3\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/click_no_resources_count_1.feature",
    "content": "@click_no_resources_counts\nFeature: Click No Resources Count (1)\n\n    Background:\n\n        Given I see the app\n\n    Scenario Outline: Should increase the first and second counts\n\n        Given I select the mode <Mode>\n        And I select the component No Resources\n        When I click the first count 3 times\n        Then I see the first count is 3\n        And I see the second count is 3\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/click_no_resources_count_2.feature",
    "content": "@click_no_resources_counts_2\nFeature: Click No Resources Count (2)\n\n    Background:\n\n        Given I see the app\n\n    Scenario Outline: Should increase the first and second counts\n\n        Given I select the mode <Mode>\n        And I select the component No Resources\n        When I click the second count 3 times\n        Then I see the first count is 3\n        And I see the second count is 3\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/click_parallel_count_1.feature",
    "content": "@click_parallel_counts_1\nFeature: Click Parallel Count (1)\n\n    Background:\n\n        Given I see the app\n\n    Scenario Outline: Should increase the first and second counts\n\n        Given I select the mode <Mode>\n        And I select the component Parallel\n        When I click the first count 3 times\n        Then I see the first count is 3\n        And I see the second count is 3\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/click_parallel_count_2.feature",
    "content": "@click_parallel_counts_2\nFeature: Click Parallel Count (2)\n\n    Background:\n\n        Given I see the app\n\n    Scenario Outline: Should increase the first and second counts\n\n        Given I select the mode <Mode>\n        And I select the component Parallel\n        When I click the second count 3 times\n        Then I see the first count is 3\n        And I see the second count is 3\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/click_single_count.feature",
    "content": "@click_single_count\nFeature: Click Single Count\n\n    Background:\n\n        Given I see the app\n\n    Scenario Outline: Should increase the count\n\n        Given I select the mode <Mode>\n        And I select the component Single\n        When I click the count 3 times\n        Then I see the count is 3\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/open_app.feature",
    "content": "@open_app\nFeature: Open App\n\n  @open_app-title\n  Scenario: Should see the initial page title\n    When I open the app\n    Then I see the page title is Out-of-Order"
  },
  {
    "path": "examples/suspense_tests/e2e/features/view_inside_component.feature",
    "content": "@view_inside_component\nFeature: View Inside Component\n\n    Background:\n\n        Given I see the app\n\n    @view_inside_component\n    Scenario Outline: Should see the page title\n        Given I select the mode <Mode>\n        When I select the component Inside Component\n        Then I see the page title is <Mode>\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_inside_component-one\n    Scenario Outline: Should see the one second message\n        Given I select the mode <Mode>\n        When I select the component Inside Component\n        Then I see the one second message is One Second: Loaded 1!\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_inside_component-inside\n    Scenario Outline: Should see the inside message\n        Given I select the mode <Mode>\n        When I select the component Inside Component\n        Then I see the inside message is Suspense inside another component should work.\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_inside_component-following\n    Scenario Outline: Should see the following message\n        Given I select the mode <Mode>\n        When I select the component Inside Component\n        Then I see the following message is Children following Suspense should hydrate properly.\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/view_nested.feature",
    "content": "@view_nested\nFeature: View Nested\n\n    Background:\n\n        Given I see the app\n\n    @view_nested-title\n    Scenario Outline: Should see the page title\n        Given I select the mode <Mode>\n        When I select the component Nested\n        Then I see the page title is <Mode>\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_nested-one\n    Scenario Outline: Should see the one second message\n\n        Given I select the mode <Mode>\n        When I select the component Nested\n        Then I see the one second message is One Second: Loaded 1!\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_nested-two\n    Scenario Outline: Should see the two second message\n\n        Given I select the mode <Mode>\n        When I select the component Nested\n        Then I see the two second message is Two Second: Loaded 2!\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/view_nested_inside.feature",
    "content": "@view_nested_inside\nFeature: View Nested Inside\n\n    Background:\n\n        Given I see the app\n\n    @view_nested_inside-title\n    Scenario Outline: Should see the page title\n        Given I select the mode <Mode>\n        When I select the component Nested (resource created inside)\n        Then I see the page title is <Mode>\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_nested_inside-one\n    Scenario Outline: Should see the one second message\n        Given I select the mode <Mode>\n        When I select the component Nested (resource created inside)\n        Then I see the one second message is One Second: Loaded 1!\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_nested_inside-two\n    Scenario Outline: Should see the two second message\n        Given I select the mode <Mode>\n        When I select the component Nested (resource created inside)\n        Then I see the two second message is Loaded 2 (created inside first suspense)!: Ok(())\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |"
  },
  {
    "path": "examples/suspense_tests/e2e/features/view_no_resources.feature",
    "content": "@view_no_resources\nFeature: view No Resources\n\n    Background:\n\n        Given I see the app\n\n    @view_no_resources-title\n    Scenario Outline: Should see the page title\n        Given I select the mode <Mode>\n        When I select the component No Resources\n        Then I see the page title is <Mode>\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_no_resources-another\n    Scenario Outline: Should see the inside message\n        Given I select the mode <Mode>\n        When I select the component No Resources\n        Then I see the inside message is Children inside Suspense should hydrate properly.\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_no_resources-following\n    Scenario Outline: Should see the following message\n        Given I select the mode <Mode>\n        When I select the component No Resources\n        Then I see the following message is Children following Suspense should hydrate properly.\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/features/view_parallel.feature",
    "content": "@view_parallel\nFeature: View Parallel\n\n    Background:\n\n        Given I see the app\n\n    @view_parallel-title\n    Scenario Outline: Should see the page title\n        Given I select the mode <Mode>\n        When I select the component Parallel\n        Then I see the page title is <Mode>\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_parallel-one\n    Scenario Outline: Should see the one second message\n        Given I select the mode <Mode>\n        When I select the component Parallel\n        Then I see the one second message is One Second: Loaded 1!\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_parallel-two\n    Scenario Outline: Should see the two second message\n        Given I select the mode <Mode>\n        When I select the component Parallel\n        Then I see the two second message is Two Second: Loaded 2!\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |"
  },
  {
    "path": "examples/suspense_tests/e2e/features/view_single.feature",
    "content": "@view_single\nFeature: View Single\n\n    Background:\n\n        Given I see the app\n\n    @view_single-title\n    Scenario Outline: Should see the page title\n        Given I select the mode <Mode>\n        When I select the component Single\n        Then I see the page title is <Mode>\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_single-one\n    Scenario Outline: Should see the one second message\n        Given I select the mode <Mode>\n        When I select the component Single\n        Then I see the one second message is One Second: Loaded 1!\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n\n    @view_single-following\n    Scenario Outline: Should see the following message\n        Given I select the mode <Mode>\n        When I select the component Single\n        Then I see the following message is Children following Suspense should hydrate properly.\n\n        Examples:\n            | Mode         |\n            | Out-of-Order |\n            | In-Order     |\n            | Async        |\n"
  },
  {
    "path": "examples/suspense_tests/e2e/tests/app_suite.rs",
    "content": "mod fixtures;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fixtures::world::AppWorld;\nuse std::{ffi::OsStr, fs::read_dir};\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    // Normally the below is done, but it's now gotten to the point of\n    // having a sufficient number of tests where the resource contention\n    // of the concurrently running browsers will cause failures on CI.\n    // AppWorld::cucumber()\n    //     .fail_on_skipped()\n    //     .run_and_exit(\"./features\")\n    //     .await;\n\n    // Mitigate the issue by manually stepping through each feature,\n    // rather than letting cucumber glob them and dispatch all at once.\n    for entry in read_dir(\"./features\")? {\n        let path = entry?.path();\n        if path.extension() == Some(OsStr::new(\"feature\")) {\n            AppWorld::cucumber()\n                .fail_on_skipped()\n                .run_and_exit(path)\n                .await;\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "examples/suspense_tests/e2e/tests/fixtures/action.rs",
    "content": "use super::{find, world::HOST};\nuse anyhow::Result;\nuse fantoccini::{Client, Locator};\nuse std::result::Result::Ok;\n\npub async fn goto_path(client: &Client, path: &str) -> Result<()> {\n    let url = format!(\"{}{}\", HOST, path);\n    client.goto(&url).await?;\n\n    Ok(())\n}\n\npub async fn click_link(client: &Client, text: &str) -> Result<()> {\n    let link = client\n        .wait()\n        .for_element(Locator::LinkText(text))\n        .await\n        .expect(format!(\"Link not found by `{}`\", text).as_str());\n\n    link.click().await?;\n\n    Ok(())\n}\n\npub async fn click_first_button(client: &Client) -> Result<()> {\n    let counter_button = find::first_button(client).await?;\n\n    counter_button.click().await?;\n\n    Ok(())\n}\n\npub async fn click_second_button(client: &Client) -> Result<()> {\n    let counter_button = find::second_button(client).await?;\n\n    counter_button.click().await?;\n\n    Ok(())\n}\n\npub async fn click_reset_counters_button(client: &Client) -> Result<()> {\n    let reset_counter = find::reset_counter(client).await?;\n\n    reset_counter.click().await?;\n\n    Ok(())\n}\n\npub async fn click_reset_csr_counters_button(client: &Client) -> Result<()> {\n    let reset_counter = find::reset_csr_counter(client).await?;\n\n    reset_counter.click().await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/suspense_tests/e2e/tests/fixtures/check.rs",
    "content": "use crate::fixtures::find;\nuse anyhow::{Ok, Result};\nuse fantoccini::Client;\nuse pretty_assertions::assert_eq;\n\npub async fn page_title_is(client: &Client, expected_text: &str) -> Result<()> {\n    let actual = find::page_title(client).await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n\npub async fn loaded_one_message_is(\n    client: &Client,\n    expected_text: &str,\n) -> Result<()> {\n    let actual = find::loaded_one_message(client).await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n\npub async fn loaded_two_message_is(\n    client: &Client,\n    expected_text: &str,\n) -> Result<()> {\n    let actual = find::loaded_two_message(client).await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n\npub async fn inside_message_is(\n    client: &Client,\n    expected_text: &str,\n) -> Result<()> {\n    let actual = find::inside_message(client).await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n\npub async fn following_message_is(\n    client: &Client,\n    expected_text: &str,\n) -> Result<()> {\n    let actual = find::following_message(client).await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n\npub async fn first_count_is(client: &Client, expected: u32) -> Result<()> {\n    let actual = find::first_count(client).await?;\n    assert_eq!(actual, expected);\n\n    Ok(())\n}\n\npub async fn second_count_is(client: &Client, expected: u32) -> Result<()> {\n    let actual = find::second_count(client).await?;\n    assert_eq!(actual, expected);\n\n    Ok(())\n}\n\npub async fn instrumented_counts(\n    client: &Client,\n    expected: &[(&str, u32)],\n) -> Result<()> {\n    let mut actual = Vec::<(&str, u32)>::new();\n\n    for (selector, _) in expected.iter() {\n        actual\n            .push((selector, find::instrumented_count(client, selector).await?))\n    }\n\n    assert_eq!(actual, expected);\n\n    Ok(())\n}\n\npub async fn link_text_is_aria_current(\n    client: &Client,\n    text: &str,\n) -> Result<()> {\n    let link = find::link_with_text(client, text).await?;\n\n    link.attr(\"aria-current\")\n        .await?\n        .expect(format!(\"aria-current missing for {text}\").as_str());\n\n    Ok(())\n}\n\npub async fn link_text_is_not_aria_current(\n    client: &Client,\n    text: &str,\n) -> Result<()> {\n    let link = find::link_with_text(client, text).await?;\n\n    link.attr(\"aria-current\")\n        .await?\n        .map(|_| anyhow::bail!(\"aria-current mistakenly set for {text}\"))\n        .unwrap_or(Ok(()))\n}\n"
  },
  {
    "path": "examples/suspense_tests/e2e/tests/fixtures/find.rs",
    "content": "use anyhow::{Ok, Result};\nuse fantoccini::{elements::Element, Client, Locator};\n\npub async fn page_title(client: &Client) -> Result<String> {\n    let selector = \"h1\";\n    let element = client\n        .wait()\n        .for_element(Locator::Css(selector))\n        .await\n        .expect(\n            format!(\"Page title not found by Css selector `{}`\", selector)\n                .as_str(),\n        );\n\n    let text = element.text().await?;\n\n    Ok(text)\n}\n\npub async fn loaded_one_message(client: &Client) -> Result<String> {\n    let text = component_message(client, \"loaded-1\").await?;\n\n    Ok(text)\n}\n\npub async fn loaded_two_message(client: &Client) -> Result<String> {\n    let text = component_message(client, \"loaded-2\").await?;\n\n    Ok(text)\n}\n\npub async fn following_message(client: &Client) -> Result<String> {\n    let text = component_message(client, \"following-message\").await?;\n\n    Ok(text)\n}\n\npub async fn inside_message(client: &Client) -> Result<String> {\n    let text = component_message(client, \"inside-message\").await?;\n\n    Ok(text)\n}\n\npub async fn first_count(client: &Client) -> Result<u32> {\n    let element = first_button(client).await?;\n    let text = element.text().await?;\n    let count = text.parse::<u32>().unwrap();\n\n    Ok(count)\n}\n\npub async fn first_button(client: &Client) -> Result<Element> {\n    let counter_button = client\n        .wait()\n        .for_element(Locator::Css(\"button\"))\n        .await\n        .expect(\"First button not found\");\n\n    Ok(counter_button)\n}\n\npub async fn second_count(client: &Client) -> Result<u32> {\n    let element = second_button(client).await?;\n    let text = element.text().await?;\n    let count = text.parse::<u32>().unwrap();\n\n    Ok(count)\n}\n\npub async fn second_button(client: &Client) -> Result<Element> {\n    let counter_button = client\n        .wait()\n        .for_element(Locator::Id(\"second-count\"))\n        .await\n        .expect(\"Second button not found\");\n\n    Ok(counter_button)\n}\n\npub async fn instrumented_count(\n    client: &Client,\n    selector: &str,\n) -> Result<u32> {\n    let element = client\n        .wait()\n        .for_element(Locator::Id(selector))\n        .await\n        .expect(format!(\"Element #{selector} not found.\").as_str());\n    let text = element.text().await?;\n    let count = text.parse::<u32>().expect(\n        format!(\"Element #{selector} does not contain a number.\").as_str(),\n    );\n    Ok(count)\n}\n\npub async fn reset_counter(client: &Client) -> Result<Element> {\n    let reset_button = client\n        .wait()\n        .for_element(Locator::Id(\"reset-counters\"))\n        .await\n        .expect(\"Reset counter input not found\");\n\n    Ok(reset_button)\n}\n\npub async fn reset_csr_counter(client: &Client) -> Result<Element> {\n    let reset_button = client\n        .wait()\n        .for_element(Locator::Id(\"reset-csr-counters\"))\n        .await\n        .expect(\"Reset CSR counter input not found\");\n\n    Ok(reset_button)\n}\n\nasync fn component_message(client: &Client, id: &str) -> Result<String> {\n    let element =\n        client.wait().for_element(Locator::Id(id)).await.expect(\n            format!(\"loaded message not found by id `{}`\", id).as_str(),\n        );\n\n    let text = element.text().await?;\n\n    Ok(text)\n}\n\npub async fn link_with_text(client: &Client, text: &str) -> Result<Element> {\n    let link = client\n        .wait()\n        .for_element(Locator::LinkText(text))\n        .await\n        .expect(format!(\"Link not found by `{}`\", text).as_str());\n    Ok(link)\n}\n"
  },
  {
    "path": "examples/suspense_tests/e2e/tests/fixtures/mod.rs",
    "content": "pub mod action;\npub mod check;\npub mod find;\npub mod world;\n"
  },
  {
    "path": "examples/suspense_tests/e2e/tests/fixtures/world/action_steps.rs",
    "content": "use crate::fixtures::{action, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::{gherkin::Step, given, when};\n\n#[given(\"I see the app\")]\n#[when(\"I open the app\")]\nasync fn i_open_the_app(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::goto_path(client, \"\").await?;\n\n    Ok(())\n}\n\n#[given(regex = r\"^I select the mode (.*)$\")]\n#[given(regex = r\"^I select the component (.*)$\")]\n#[when(regex = \"^I select the component (.*)$\")]\n#[given(regex = \"^I select the link (.*)$\")]\n#[when(regex = \"^I select the link (.*)$\")]\n#[when(regex = \"^I click on the link (.*)$\")]\n#[when(regex = \"^I go check the (.*)$\")]\nasync fn i_select_the_link(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::click_link(client, &text).await?;\n\n    Ok(())\n}\n\n#[when(expr = \"I click the first count {int} times\")]\n#[when(expr = \"I click the count {int} times\")]\nasync fn i_click_the_first_button_n_times(\n    world: &mut AppWorld,\n    times: u32,\n) -> Result<()> {\n    let client = &world.client;\n\n    for _ in 1..=times {\n        action::click_first_button(client).await?;\n    }\n\n    Ok(())\n}\n\n#[when(expr = \"I click the second count {int} times\")]\nasync fn i_click_the_second_button_n_times(\n    world: &mut AppWorld,\n    times: u32,\n) -> Result<()> {\n    let client = &world.client;\n\n    for _ in 1..=times {\n        action::click_second_button(client).await?;\n    }\n\n    Ok(())\n}\n\n#[given(regex = \"^I (refresh|reload) the (browser|page)$\")]\n#[when(regex = \"^I (refresh|reload) the (browser|page)$\")]\nasync fn i_refresh_the_browser(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    client.refresh().await?;\n\n    Ok(())\n}\n\n#[when(expr = \"I click on Reset Counters\")]\nasync fn i_click_on_reset_counters(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::click_reset_counters_button(client).await?;\n\n    Ok(())\n}\n\n#[given(expr = \"I click on Reset CSR Counters\")]\n#[when(expr = \"I click on Reset CSR Counters\")]\nasync fn i_click_on_reset_csr_counters(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::click_reset_csr_counters_button(client).await?;\n\n    Ok(())\n}\n\n#[when(expr = \"I access the instrumented counters via SSR\")]\nasync fn i_access_the_instrumented_counters_page_via_ssr(\n    world: &mut AppWorld,\n) -> Result<()> {\n    let client = &world.client;\n    action::click_link(client, \"Instrumented\").await?;\n    action::click_link(client, \"Counters\").await?;\n    client.refresh().await?;\n\n    Ok(())\n}\n\n#[when(expr = \"I access the instrumented counters via CSR\")]\nasync fn i_access_the_instrumented_counters_page_via_csr(\n    world: &mut AppWorld,\n) -> Result<()> {\n    let client = &world.client;\n    action::click_link(client, \"Instrumented\").await?;\n    action::click_link(client, \"Counters\").await?;\n\n    Ok(())\n}\n\n#[given(expr = \"I select the following links\")]\n#[when(expr = \"I select the following links\")]\nasync fn i_select_the_following_links(\n    world: &mut AppWorld,\n    step: &Step,\n) -> Result<()> {\n    let client = &world.client;\n\n    if let Some(table) = step.table.as_ref() {\n        for row in table.rows.iter() {\n            action::click_link(client, &row[0]).await?;\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/suspense_tests/e2e/tests/fixtures/world/check_steps.rs",
    "content": "use crate::fixtures::{check, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::{gherkin::Step, then};\n\n#[then(regex = r\"^I see the page title is (.*)$\")]\nasync fn i_see_the_page_title_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::page_title_is(client, &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = r\"^I see the one second message is (.*)$\")]\nasync fn i_see_the_one_second_message_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::loaded_one_message_is(client, &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = r\"^I see the two second message is (.*)$\")]\nasync fn i_see_the_two_second_message_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::loaded_two_message_is(client, &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = r\"^I see the following message is (.*)$\")]\nasync fn i_see_the_following_message_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::following_message_is(client, &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = r\"^I see the inside message is (.*)$\")]\nasync fn i_see_the_inside_message_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::inside_message_is(client, &text).await?;\n\n    Ok(())\n}\n\n#[then(expr = \"I see the first count is {int}\")]\n#[then(expr = \"I see the count is {int}\")]\nasync fn i_see_the_first_count_is(\n    world: &mut AppWorld,\n    expected: u32,\n) -> Result<()> {\n    let client = &world.client;\n    check::first_count_is(client, expected).await?;\n\n    Ok(())\n}\n\n#[then(expr = \"I see the second count is {int}\")]\nasync fn i_see_the_second_count_is(\n    world: &mut AppWorld,\n    expected: u32,\n) -> Result<()> {\n    let client = &world.client;\n    check::second_count_is(client, expected).await?;\n\n    Ok(())\n}\n\n#[then(regex = r\"^I see the (.*) link being bolded$\")]\nasync fn i_see_the_link_being_bolded(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::link_text_is_aria_current(client, &text).await?;\n\n    Ok(())\n}\n\n#[then(expr = \"I see the following links being bolded\")]\nasync fn i_see_the_following_links_being_bolded(\n    world: &mut AppWorld,\n    step: &Step,\n) -> Result<()> {\n    let client = &world.client;\n    if let Some(table) = step.table.as_ref() {\n        for row in table.rows.iter() {\n            check::link_text_is_aria_current(client, &row[0]).await?;\n        }\n    }\n\n    Ok(())\n}\n\n#[then(regex = r\"^I see the (.*) link not being bolded$\")]\nasync fn i_see_the_link_being_not_bolded(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::link_text_is_not_aria_current(client, &text).await?;\n\n    Ok(())\n}\n\n#[then(expr = \"I see the following links not being bolded\")]\nasync fn i_see_the_following_links_not_being_bolded(\n    world: &mut AppWorld,\n    step: &Step,\n) -> Result<()> {\n    let client = &world.client;\n    if let Some(table) = step.table.as_ref() {\n        for row in table.rows.iter() {\n            check::link_text_is_not_aria_current(client, &row[0]).await?;\n        }\n    }\n\n    Ok(())\n}\n\n#[then(expr = \"I see the following counters under section\")]\n#[then(expr = \"the following counters under section\")]\nasync fn i_see_the_following_counters_under_section(\n    world: &mut AppWorld,\n    step: &Step,\n) -> Result<()> {\n    // FIXME ideally check the mode; for now leave it because effort\n    let client = &world.client;\n    if let Some(table) = step.table.as_ref() {\n        let expected = table\n            .rows\n            .iter()\n            .skip(1)\n            .map(|row| (row[0].as_str(), row[1].parse::<u32>().unwrap()))\n            .collect::<Vec<_>>();\n        check::instrumented_counts(client, &expected).await?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/suspense_tests/e2e/tests/fixtures/world/mod.rs",
    "content": "pub mod action_steps;\npub mod check_steps;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fantoccini::{\n    error::NewSessionError, wd::Capabilities, Client, ClientBuilder,\n};\n\npub const HOST: &str = \"http://127.0.0.1:3000\";\n\n#[derive(Debug, World)]\n#[world(init = Self::new)]\npub struct AppWorld {\n    pub client: Client,\n}\n\nimpl AppWorld {\n    async fn new() -> Result<Self, anyhow::Error> {\n        let webdriver_client = build_client().await?;\n\n        Ok(Self {\n            client: webdriver_client,\n        })\n    }\n}\n\nasync fn build_client() -> Result<Client, NewSessionError> {\n    let mut cap = Capabilities::new();\n    let arg = serde_json::from_str(\"{\\\"args\\\": [\\\"-headless\\\"]}\").unwrap();\n    cap.insert(\"goog:chromeOptions\".to_string(), arg);\n\n    let client = ClientBuilder::native()\n        .capabilities(cap)\n        .connect(\"http://localhost:4444\")\n        .await?;\n\n    Ok(client)\n}\n"
  },
  {
    "path": "examples/suspense_tests/src/app.rs",
    "content": "use crate::instrumented::InstrumentedRoutes;\nuse leptos::prelude::*;\nuse leptos_router::{\n    components::{Outlet, ParentRoute, Redirect, Route, Router, Routes, A},\n    SsrMode, StaticSegment,\n};\n\nconst WAIT_ONE_SECOND: u64 = 1;\nconst WAIT_TWO_SECONDS: u64 = 2;\n\n#[server]\nasync fn first_wait_fn(seconds: u64) -> Result<(), ServerFnError> {\n    tokio::time::sleep(tokio::time::Duration::from_secs(seconds)).await;\n\n    Ok(())\n}\n\n#[server]\nasync fn second_wait_fn(seconds: u64) -> Result<(), ServerFnError> {\n    tokio::time::sleep(tokio::time::Duration::from_secs(seconds)).await;\n\n    Ok(())\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    let style = r\"\n        nav {\n            display: flex;\n            width: 100%;\n            justify-content: space-around;\n        }\n\n        [aria-current] {\n            font-weight: bold;\n        }\n    \";\n    view! {\n        <style>{style}</style>\n        <Router>\n            <nav>\n                <A href=\"/out-of-order\">\"Out-of-Order\"</A>\n                <A href=\"/in-order\">\"In-Order\"</A>\n                <A href=\"/async\">\"Async\"</A>\n                <A href=\"/instrumented/\">\"Instrumented\"</A>\n            </nav>\n            <main>\n                <Routes fallback=|| \"Page not found.\">\n                    <Route\n                        path=StaticSegment(\"\")\n                        view=|| view! { <Redirect path=\"/out-of-order\"/> }\n                    />\n                    // out-of-order\n                    <ParentRoute\n                        path=StaticSegment(\"out-of-order\")\n                        view=|| {\n                            view! {\n                                <SecondaryNav/>\n                                <h1>\"Out-of-Order\"</h1>\n                                <Outlet/>\n                            }\n                        }\n                    >\n\n                        <Route path=StaticSegment(\"\") view=Nested/>\n                        <Route path=StaticSegment(\"inside\") view=NestedResourceInside/>\n                        <Route path=StaticSegment(\"single\") view=Single/>\n                        <Route path=StaticSegment(\"parallel\") view=Parallel/>\n                        <Route path=StaticSegment(\"inside-component\") view=InsideComponent/>\n                        <Route path=StaticSegment(\"local\") view=LocalResource/>\n                        <Route path=StaticSegment(\"none\") view=None/>\n                    </ParentRoute>\n                    // in-order\n                    <ParentRoute\n                        path=StaticSegment(\"in-order\")\n                        ssr=SsrMode::InOrder\n                        view=|| {\n                            view! {\n                                <SecondaryNav/>\n                                <h1>\"In-Order\"</h1>\n                                <Outlet/>\n                            }\n                        }\n                    >\n\n                        <Route path=StaticSegment(\"\") view=Nested/>\n                        <Route path=StaticSegment(\"inside\") view=NestedResourceInside/>\n                        <Route path=StaticSegment(\"single\") view=Single/>\n                        <Route path=StaticSegment(\"parallel\") view=Parallel/>\n                        <Route path=StaticSegment(\"inside-component\") view=InsideComponent/>\n                        <Route path=StaticSegment(\"local\") view=LocalResource/>\n                        <Route path=StaticSegment(\"none\") view=None/>\n                    </ParentRoute>\n                    // async\n                    <ParentRoute\n                        path=StaticSegment(\"async\")\n                        ssr=SsrMode::Async\n                        view=|| {\n                            view! {\n                                <SecondaryNav/>\n                                <h1>\"Async\"</h1>\n                                <Outlet/>\n                            }\n                        }\n                    >\n\n                        <Route path=StaticSegment(\"\") view=Nested/>\n                        <Route path=StaticSegment(\"inside\") view=NestedResourceInside/>\n                        <Route path=StaticSegment(\"single\") view=Single/>\n                        <Route path=StaticSegment(\"parallel\") view=Parallel/>\n                        <Route path=StaticSegment(\"inside-component\") view=InsideComponent/>\n                        <Route path=StaticSegment(\"local\") view=LocalResource/>\n                        <Route path=StaticSegment(\"none\") view=None/>\n                    </ParentRoute>\n                    <InstrumentedRoutes/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n#[component]\nfn SecondaryNav() -> impl IntoView {\n    view! {\n        <nav>\n            <A href=\"\" exact=true>\n                \"Nested\"\n            </A>\n            <A href=\"inside\" exact=true>\n                \"Nested (resource created inside)\"\n            </A>\n            <A href=\"single\">\"Single\"</A>\n            <A href=\"parallel\">\"Parallel\"</A>\n            <A href=\"inside-component\">\"Inside Component\"</A>\n            <A href=\"local\">\"Local Resource\"</A>\n            <A href=\"none\">\"No Resources\"</A>\n        </nav>\n    }\n}\n\n#[component]\nfn Nested() -> impl IntoView {\n    let one_second = Resource::new(|| WAIT_ONE_SECOND, first_wait_fn);\n    let two_second = Resource::new(|| WAIT_TWO_SECONDS, second_wait_fn);\n    let (count, set_count) = signal(0);\n\n    view! {\n        <div>\n            <Suspense fallback=|| {\n                \"Loading 1...\"\n            }>\n                {move || {\n                    one_second.map(|_| view! { <p id=\"loaded-1\">\"One Second: Loaded 1!\"</p> })\n                }}\n                <Suspense fallback=|| {\n                    \"Loading 2...\"\n                }>\n                    {move || {\n                        two_second\n                            .map(|_| {\n                                view! {\n                                    <p id=\"loaded-2\">\"Two Second: Loaded 2!\"</p>\n                                    <button on:click=move |_| {\n                                        set_count.update(|n| *n += 1)\n                                    }>{count}</button>\n                                }\n                            })\n                    }}\n\n                </Suspense>\n            </Suspense>\n        </div>\n    }\n}\n\n#[component]\nfn NestedResourceInside() -> impl IntoView {\n    let one_second = Resource::new(|| WAIT_ONE_SECOND, first_wait_fn);\n    let (count, set_count) = signal(0);\n\n    view! {\n        <div>\n            <Suspense fallback=|| {\n                \"Loading 1...\"\n            }>\n                {Suspend::new(async move {\n                    _ = one_second.await;\n                    let two_second = Resource::new(\n                        || (),\n                        move |_| async move { second_wait_fn(WAIT_TWO_SECONDS).await },\n                    );\n                    view! {\n                        <p id=\"loaded-1\">\"One Second: Loaded 1!\"</p>\n                        <Suspense fallback=|| \"Loading 2...\">\n                            <span id=\"loaded-2\">\n                                \"Loaded 2 (created inside first suspense)!: \"\n                                {Suspend::new(async move { format!(\"{:?}\", two_second.await) })}\n                            </span>\n                            <button on:click=move |_| set_count.update(|n| *n += 1)>{count}</button>\n                        </Suspense>\n                    }\n                })}\n\n            </Suspense>\n        </div>\n    }\n}\n\n#[component]\nfn Parallel() -> impl IntoView {\n    let one_second = Resource::new(|| WAIT_ONE_SECOND, first_wait_fn);\n    let two_second = Resource::new(|| WAIT_TWO_SECONDS, second_wait_fn);\n    let (count, set_count) = signal(0);\n\n    view! {\n        <div>\n            <Suspense fallback=|| {\n                \"Loading 1...\"\n            }>\n                {move || {\n                    one_second\n                        .map(move |_| {\n                            view! {\n                                <p id=\"loaded-1\">\"One Second: Loaded 1!\"</p>\n                                <button on:click=move |_| {\n                                    set_count.update(|n| *n += 1)\n                                }>{count}</button>\n                            }\n                        })\n                }}\n\n            </Suspense>\n            <Suspense fallback=|| {\n                \"Loading 2...\"\n            }>\n                {move || {\n                    two_second\n                        .map(move |_| {\n                            view! {\n                                <p id=\"loaded-2\">\"Two Second: Loaded 2!\"</p>\n                                <button\n                                    id=\"second-count\"\n                                    on:click=move |_| set_count.update(|n| *n += 1)\n                                >\n                                    {count}\n                                </button>\n                            }\n                        })\n                }}\n\n            </Suspense>\n        </div>\n    }\n}\n\n#[component]\nfn Single() -> impl IntoView {\n    let one_second = Resource::new(|| WAIT_ONE_SECOND, first_wait_fn);\n    let (count, set_count) = signal(0);\n\n    view! {\n        <div>\n            <Suspense fallback=|| {\n                \"Loading 1...\"\n            }>\n                {move || {\n                    one_second.map(|_| view! { <p id=\"loaded-1\">\"One Second: Loaded 1!\"</p> })\n                }}\n\n            </Suspense>\n            <p id=\"following-message\">\"Children following Suspense should hydrate properly.\"</p>\n            <div>\n                <button on:click=move |_| set_count.update(|n| *n += 1)>{count}</button>\n            </div>\n        </div>\n    }\n}\n\n#[component]\nfn InsideComponent() -> impl IntoView {\n    let (count, set_count) = signal(0);\n\n    view! {\n        <div>\n            <p id=\"inside-message\">\"Suspense inside another component should work.\"</p>\n            <InsideComponentChild/>\n            <p id=\"following-message\">\"Children following Suspense should hydrate properly.\"</p>\n            <div>\n                <button on:click=move |_| set_count.update(|n| *n += 1)>{count}</button>\n            </div>\n        </div>\n    }\n}\n\n#[component]\nfn InsideComponentChild() -> impl IntoView {\n    let one_second = Resource::new(|| WAIT_ONE_SECOND, first_wait_fn);\n    view! {\n        <Suspense fallback=|| {\n            \"Loading 1...\"\n        }>\n            {move || {\n                one_second.map(|_| view! { <p id=\"loaded-1\">\"One Second: Loaded 1!\"</p> })\n            }}\n\n        </Suspense>\n    }\n}\n\n#[component]\nfn LocalResource() -> impl IntoView {\n    let one_second = Resource::new(|| WAIT_ONE_SECOND, first_wait_fn);\n    let local = LocalResource::new(|| first_wait_fn(WAIT_ONE_SECOND));\n    let (count, set_count) = signal(0);\n\n    view! {\n        <div>\n            <Suspense fallback=|| {\n                \"Loading 1...\"\n            }>\n                {move || {\n                    one_second.map(|_| view! { <p id=\"loaded-1\">\"One Second: Loaded 1!\"</p> })\n                }}\n                {move || {\n                    Suspend::new(async move {\n                        let value = local.await;\n                        view! { <p id=\"loaded-2\">\"One Second: Local Loaded \" {value} \"!\"</p> }\n                    })\n                }}\n\n            </Suspense>\n            <p id=\"following-message\">\"Children following Suspense should hydrate properly.\"</p>\n            <div>\n                <button on:click=move |_| set_count.update(|n| *n += 1)>{count}</button>\n            </div>\n        </div>\n    }\n}\n\n#[component]\nfn None() -> impl IntoView {\n    let (count, set_count) = signal(0);\n\n    view! {\n        <div>\n            <Suspense fallback=|| \"Loading 1...\">\n                <p id=\"inside-message\">\"Children inside Suspense should hydrate properly.\"</p>\n                <button on:click=move |_| set_count.update(|n| *n += 1)>{count}</button>\n            </Suspense>\n            <p id=\"following-message\">\"Children following Suspense should hydrate properly.\"</p>\n            <div>\n                <button id=\"second-count\" on:click=move |_| set_count.update(|n| *n += 1)>\n                    {count}\n                </button>\n            </div>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/suspense_tests/src/instrumented.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::{\n    components::{ParentRoute, Route, A},\n    hooks::use_params,\n    nested_router::Outlet,\n    params::Params,\n    ParamSegment, SsrMode, StaticSegment, WildcardSegment,\n};\n\n#[cfg(feature = \"ssr\")]\npub(super) mod counter {\n    use std::{\n        collections::HashMap,\n        sync::{\n            atomic::{AtomicU32, Ordering},\n            LazyLock, Mutex,\n        },\n    };\n\n    #[derive(Default)]\n    pub struct Counter(AtomicU32);\n\n    impl Counter {\n        #[allow(dead_code)]\n        pub const fn new() -> Self {\n            Self(AtomicU32::new(0))\n        }\n\n        pub fn get(&self) -> u32 {\n            self.0.load(Ordering::SeqCst)\n        }\n\n        pub fn inc(&self) -> u32 {\n            self.0.fetch_add(1, Ordering::SeqCst)\n        }\n\n        pub fn reset(&self) {\n            self.0.store(0, Ordering::SeqCst);\n        }\n    }\n\n    #[derive(Default)]\n    pub struct Counters {\n        pub list_items: Counter,\n        pub get_item: Counter,\n        pub inspect_item_root: Counter,\n        pub inspect_item_field: Counter,\n    }\n\n    impl From<&mut Counters> for super::Counters {\n        fn from(counter: &mut Counters) -> Self {\n            Self {\n                get_item: counter.get_item.get(),\n                inspect_item_root: counter.inspect_item_root.get(),\n                inspect_item_field: counter.inspect_item_field.get(),\n                list_items: counter.list_items.get(),\n            }\n        }\n    }\n\n    impl Counters {\n        pub fn reset(&self) {\n            self.get_item.reset();\n            self.inspect_item_root.reset();\n            self.inspect_item_field.reset();\n            self.list_items.reset();\n        }\n    }\n\n    pub static COUNTERS: LazyLock<Mutex<HashMap<u64, Counters>>> =\n        LazyLock::new(|| Mutex::new(HashMap::new()));\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]\npub struct Item {\n    id: i64,\n    name: Option<String>,\n    field: Option<String>,\n}\n\n#[server]\nasync fn list_items(ticket: u64) -> Result<Vec<i64>, ServerFnError> {\n    // emulate database query overhead\n    tokio::time::sleep(std::time::Duration::from_millis(25)).await;\n    (*counter::COUNTERS)\n        .lock()\n        .expect(\"somehow panicked elsewhere\")\n        .entry(ticket)\n        .or_default()\n        .list_items\n        .inc();\n    Ok(vec![1, 2, 3, 4])\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]\npub struct GetItemResult(pub Item, pub Vec<String>);\n\n#[server]\nasync fn get_item(\n    ticket: u64,\n    id: i64,\n) -> Result<GetItemResult, ServerFnError> {\n    // emulate database query overhead\n    tokio::time::sleep(std::time::Duration::from_millis(25)).await;\n    (*counter::COUNTERS)\n        .lock()\n        .expect(\"somehow panicked elsewhere\")\n        .entry(ticket)\n        .or_default()\n        .get_item\n        .inc();\n    let name = None::<String>;\n    let field = None::<String>;\n    Ok(GetItemResult(\n        Item { id, name, field },\n        [\"path1\", \"path2\", \"path3\"]\n            .into_iter()\n            .map(str::to_string)\n            .collect::<Vec<_>>(),\n    ))\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]\npub struct InspectItemResult(pub Item, pub String, pub Vec<String>);\n\n#[server]\nasync fn inspect_item(\n    ticket: u64,\n    id: i64,\n    path: String,\n) -> Result<InspectItemResult, ServerFnError> {\n    // emulate database query overhead\n    tokio::time::sleep(std::time::Duration::from_millis(25)).await;\n    let mut split = path.split('/');\n    let name = split.next().map(str::to_string);\n    let path = name\n        .clone()\n        .expect(\"name should have been defined at this point\");\n    let field = split\n        .next()\n        .and_then(|s| (!s.is_empty()).then(|| s.to_string()));\n    if field.is_none() {\n        (*counter::COUNTERS)\n            .lock()\n            .expect(\"somehow panicked elsewhere\")\n            .entry(ticket)\n            .or_default()\n            .inspect_item_root\n            .inc();\n    } else {\n        (*counter::COUNTERS)\n            .lock()\n            .expect(\"somehow panicked elsewhere\")\n            .entry(ticket)\n            .or_default()\n            .inspect_item_field\n            .inc();\n    }\n    Ok(InspectItemResult(\n        Item { id, name, field },\n        path,\n        [\"field1\", \"field2\", \"field3\"]\n            .into_iter()\n            .map(str::to_string)\n            .collect::<Vec<_>>(),\n    ))\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]\npub struct Counters {\n    pub get_item: u32,\n    pub inspect_item_root: u32,\n    pub inspect_item_field: u32,\n    pub list_items: u32,\n}\n\n#[server]\nasync fn get_counters(ticket: u64) -> Result<Counters, ServerFnError> {\n    Ok((*counter::COUNTERS)\n        .lock()\n        .expect(\"somehow panicked elsewhere\")\n        .entry(ticket)\n        .or_default()\n        .into())\n}\n\n#[server(ResetCounters)]\nasync fn reset_counters(ticket: u64) -> Result<(), ServerFnError> {\n    (*counter::COUNTERS)\n        .lock()\n        .expect(\"somehow panicked elsewhere\")\n        .entry(ticket)\n        .or_default()\n        .reset();\n    // leptos::logging::log!(\"counters for ticket {ticket} have been reset\");\n    Ok(())\n}\n\n#[derive(Clone, Default)]\npub struct SuspenseCounters {\n    item_overview: u32,\n    item_inspect: u32,\n    item_listing: u32,\n}\n\n#[component]\npub fn InstrumentedRoutes() -> impl leptos_router::MatchNestedRoutes + Clone {\n    // TODO should make this mode configurable via feature flag?\n    let ssr = SsrMode::Async;\n    view! {\n        <ParentRoute path=StaticSegment(\"instrumented\") view=InstrumentedRoot ssr>\n            <Route path=StaticSegment(\"/\") view=InstrumentedTop />\n            <ParentRoute path=StaticSegment(\"item\") view=ItemRoot>\n                <Route path=StaticSegment(\"/\") view=ItemListing />\n                <ParentRoute path=ParamSegment(\"id\") view=ItemTop>\n                    <Route path=StaticSegment(\"/\") view=ItemOverview />\n                    <Route path=WildcardSegment(\"path\") view=ItemInspect />\n                </ParentRoute>\n            </ParentRoute>\n            <Route path=StaticSegment(\"counters\") view=ShowCounters />\n        </ParentRoute>\n    }\n    .into_inner()\n}\n\n#[derive(Copy, Clone)]\npub struct Ticket(pub u64);\n\n#[derive(Copy, Clone)]\npub struct CSRTicket(pub u64);\n\n#[cfg(feature = \"ssr\")]\nfn inst_ticket() -> u64 {\n    // SSR will always use 0 for the ticket\n    0\n}\n\n#[cfg(not(feature = \"ssr\"))]\nfn inst_ticket() -> u64 {\n    // CSR will use a random number for the ticket\n    (js_sys::Math::random() * ((u64::MAX - 1) as f64) + 1f64) as u64\n}\n\n#[component]\nfn InstrumentedRoot() -> impl IntoView {\n    let counters = RwSignal::new(SuspenseCounters::default());\n    provide_context(counters);\n    provide_field_nav_portlet_context();\n\n    // Generate a ID directly on this component.  Rather than relying on\n    // additional server functions, doing it this way emulates more\n    // standard workflows better and to avoid having to add another\n    // thing to instrument/interfere with the typical use case.\n    // Downside is that randomness has a chance to conflict.\n    //\n    // Furthermore, this approach **will** result in unintuitive\n    // behavior when it isn't accounted for - specifically, the reason\n    // for this design is that when SSR it will guarantee usage of `0`\n    // as the ticket, while CSR it will be of some other value as the\n    // version it uses will be random.  However, when trying to get back\n    // the counters associated with the ticket, rendering using SSR will\n    // always produce the SSR version and this quirk will need to be\n    // accounted for.\n    let ticket = inst_ticket();\n    // leptos::logging::log!(\n    //     \"Ticket for this InstrumentedRoot instance: {ticket}\"\n    // );\n    provide_context(Ticket(ticket));\n\n    let csr_ticket = RwSignal::<Option<CSRTicket>>::new(None);\n\n    let reset_counters = ServerAction::<ResetCounters>::new();\n\n    Effect::new(move |_| {\n        let ticket = expect_context::<Ticket>().0;\n        csr_ticket.set(Some(CSRTicket(ticket)));\n    });\n\n    view! {\n        <section id=\"instrumented\">\n            <nav>\n                <a href=\"/\">\"Site Root\"</a>\n                <A href=\"./\" exact=true>\n                    \"Instrumented Root\"\n                </A>\n                <A href=\"item/\" strict_trailing_slash=true>\n                    \"Item Listing\"\n                </A>\n                <A href=\"counters\" strict_trailing_slash=true>\n                    \"Counters\"\n                </A>\n            </nav>\n            <FieldNavPortlet />\n            <Outlet />\n            <Suspense>\n                {move || Suspend::new(async move {\n                    let clear_suspense_counters = move |_| {\n                        counters.update(|c| *c = SuspenseCounters::default());\n                    };\n                    csr_ticket\n                        .get()\n                        .map(|ticket| {\n                            let ticket = ticket.0;\n                            view! {\n                                <ActionForm action=reset_counters>\n                                    <input type=\"hidden\" name=\"ticket\" value=format!(\"{ticket}\") />\n                                    <input\n                                        id=\"reset-csr-counters\"\n                                        type=\"submit\"\n                                        value=\"Reset CSR Counters\"\n                                        on:click=clear_suspense_counters\n                                    />\n                                </ActionForm>\n                            }\n                        })\n                })}\n            </Suspense>\n            <footer>\n                <nav>\n                    <A href=\"item/3/\">\"Target 3##\"</A>\n                    <A href=\"item/4/\">\"Target 4##\"</A>\n                    <A href=\"item/4/path1/\">\"Target 41#\"</A>\n                    <A href=\"item/4/path2/\">\"Target 42#\"</A>\n                    <A href=\"item/4/path2/field1\">\"Target 421\"</A>\n                    <A href=\"item/1/path2/field3\">\"Target 123\"</A>\n                </nav>\n            </footer>\n        </section>\n    }\n}\n\n#[component]\nfn InstrumentedTop() -> impl IntoView {\n    view! {\n        <h1>\"Instrumented Tests\"</h1>\n        <p>\n            \"These tests validates the number of invocations of server functions and suspenses per access.\"\n        </p>\n        <ul>\n            // not using `A` because currently some bugs with artix\n            <li>\n                <a href=\"item/\">\"Item Listing\"</a>\n            </li>\n            <li>\n                <a href=\"item/4/path1/\">\"Target 41#\"</a>\n            </li>\n        </ul>\n    }\n}\n\n#[component]\nfn ItemRoot() -> impl IntoView {\n    let ticket = expect_context::<Ticket>().0;\n    provide_context(Resource::new_blocking(\n        move || (),\n        move |_| async move { list_items(ticket).await },\n    ));\n\n    view! {\n        <h2>\"<ItemRoot/>\"</h2>\n        <Outlet />\n    }\n}\n\n#[component]\nfn ItemListing() -> impl IntoView {\n    let suspense_counters = expect_context::<RwSignal<SuspenseCounters>>();\n    let resource =\n        expect_context::<Resource<Result<Vec<i64>, ServerFnError>>>();\n    let item_listing = move || {\n        Suspend::new(async move {\n            let result = resource.await.map(|items| items\n            .into_iter()\n            .map(move |item|\n                // FIXME seems like relative link isn't working, it is currently\n                // adding an extra `/` in artix; manually construct `a` instead.\n                // <li><A href=format!(\"./{item}/\")>\"Item \"{item}</A></li>\n                view! {\n                    <li>\n                        <a href=format!(\"/instrumented/item/{item}/\")>\"Item \"{item}</a>\n                    </li>\n                }\n            )\n            .collect_view()\n        );\n            suspense_counters.update_untracked(|c| c.item_listing += 1);\n            result\n        })\n    };\n\n    view! {\n        <h3>\"<ItemListing/>\"</h3>\n        <ul>\n            <Suspense>{item_listing}</Suspense>\n        </ul>\n    }\n}\n\n#[derive(Params, PartialEq, Clone, Debug)]\nstruct ItemTopParams {\n    id: Option<i64>,\n}\n\n#[component]\nfn ItemTop() -> impl IntoView {\n    let ticket = expect_context::<Ticket>().0;\n    let params = use_params::<ItemTopParams>();\n    // map result to an option as the focus isn't error rendering\n    provide_context(Resource::new_blocking(\n        move || params.get().map(|p| p.id),\n        move |id| async move {\n            match id {\n                Err(_) => None,\n                Ok(Some(id)) => get_item(ticket, id).await.ok(),\n                _ => None,\n            }\n        },\n    ));\n    view! {\n        <h4>\"<ItemTop/>\"</h4>\n        <Outlet />\n    }\n}\n\n#[component]\nfn ItemOverview() -> impl IntoView {\n    let suspense_counters = expect_context::<RwSignal<SuspenseCounters>>();\n    let resource = expect_context::<Resource<Option<GetItemResult>>>();\n    let item_view = move || {\n        Suspend::new(async move {\n            let result = resource.await.map(|GetItemResult(item, names)| {\n                view! {\n                    <p>{format!(\"Viewing {item:?}\")}</p>\n                    <ul>\n                        {names\n                            .into_iter()\n                            .map(|name| {\n                                let id = item.id;\n                                // FIXME seems like relative link isn't working, it is currently\n                                // adding an extra `/` in artix; manually construct `a` instead.\n                                // <li><A href=format!(\"./{name}/\")>{format!(\"Inspect {name}\")}</A></li>\n                                view! {\n                                    <li>\n                                        <a href=format!(\n                                            \"/instrumented/item/{id}/{name}/\",\n                                        )>\"Inspect \"{name.clone()}</a>\n                                    </li>\n                                }\n                            })\n                            .collect_view()}\n                    </ul>\n                }\n            });\n            suspense_counters.update_untracked(|c| c.item_overview += 1);\n            result\n        })\n    };\n\n    view! {\n        <h5>\"<ItemOverview/>\"</h5>\n        <Suspense>{item_view}</Suspense>\n    }\n}\n\n#[derive(Params, PartialEq, Clone, Debug)]\nstruct ItemInspectParams {\n    path: Option<String>,\n}\n\n#[component]\nfn ItemInspect() -> impl IntoView {\n    let ticket = expect_context::<Ticket>().0;\n    let suspense_counters = expect_context::<RwSignal<SuspenseCounters>>();\n    let params = use_params::<ItemInspectParams>();\n    let res_overview = expect_context::<Resource<Option<GetItemResult>>>();\n    let res_inspect = Resource::new_blocking(\n        move || params.get().map(|p| p.path),\n        move |p| async move {\n            // leptos::logging::log!(\"res_inspect: res_overview.await\");\n            // Note: this resource is untracked here, though `params` changing\n            // will nonetheless results in the \"expected\" tracked updates.\n            let overview = res_overview.await;\n            // leptos::logging::log!(\"res_inspect: resolved res_overview.await\");\n            // let result =\n            match (overview, p) {\n                (Some(item), Ok(Some(path))) => {\n                    // leptos::logging::log!(\"res_inspect: inspect_item().await\");\n                    inspect_item(ticket, item.0.id, path.clone()).await.ok()\n                }\n                _ => None,\n            }\n            // ;\n            // leptos::logging::log!(\"res_inspect: resolved inspect_item().await\");\n            // result\n        },\n    );\n    let ws = use_context::<WriteSignal<Option<FieldNavCtx>>>();\n    on_cleanup(move || {\n        if let Some(c) = ws {\n            c.set(None);\n        }\n    });\n    let inspect_view = move || {\n        // leptos::logging::log!(\"inspect_view closure invoked\");\n        Suspend::new(async move {\n            // leptos::logging::log!(\"inspect_view Suspend::new() called\");\n            let result = res_inspect.await.map(|InspectItemResult(item, name, fields)| {\n                // leptos::logging::log!(\"inspect_view res_inspect awaited\");\n                let id = item.id;\n                expect_context::<WriteSignal<Option<FieldNavCtx>>>().set(Some(\n                    fields.iter()\n                        .map(|field| FieldNavItem {\n                            href: format!(\"/instrumented/item/{id}/{name}/{field}\"),\n                            text: field.to_string(),\n                        })\n                        .collect::<Vec<_>>()\n                        .into()\n                ));\n                view! {\n                    <p>{format!(\"Inspecting {item:?}\")}</p>\n                    <ul>\n                        {fields\n                            .iter()\n                            .map(|field| {\n                                // FIXME seems like relative link to root for a wildcard isn't\n                                // working as expected, so manually construct `a` instead.\n                                // let text = format!(\"Inspect {name}/{field}\");\n                                // view! {\n                                // <li><A href=format!(\"{field}\")>{text}</A></li>\n                                // }\n                                view! {\n                                    <li>\n                                        <a href=format!(\n                                            \"/instrumented/item/{id}/{name}/{field}\",\n                                        )>{format!(\"Inspect {name}/{field}\")}</a>\n                                    </li>\n                                }\n                            })\n                            .collect_view()}\n                    </ul>\n                }\n            });\n            suspense_counters.update_untracked(|c| c.item_inspect += 1);\n            // leptos::logging::log!(\n            //     \"returning result, result.is_some() = {}, count = {}\",\n            //     result.is_some(),\n            //     suspense_counters.get().item_inspect,\n            // );\n            result\n        })\n    };\n\n    view! {\n        <h5>\"<ItemInspect/>\"</h5>\n        <Suspense>{inspect_view}</Suspense>\n    }\n}\n\n#[component]\nfn ShowCounters() -> impl IntoView {\n    // There is _weirdness_ in this view.  The `Server Calls` counters\n    // will be acquired via the expected mode and be rendered as such.\n    //\n    // However, upon `Reset Counters`, the mode from which the reset\n    // was issued will result in the rendering be reflected as such, so\n    // if the initial state was SSR, resetting under CSR will result in\n    // the CSR counters be rendered after.  However for the intents and\n    // purpose for the testing only the CSR is cared for.\n    //\n    // At the end of the day, it is possible to have both these be\n    // separated out, but for the purpose of this test the focus is not\n    // on the SSR side of things (at least until further regression is\n    // discovered that affects SSR directly).\n    let ticket = expect_context::<Ticket>().0;\n    let suspense_counters = expect_context::<RwSignal<SuspenseCounters>>();\n    let reset_counters = ServerAction::<ResetCounters>::new();\n    let res_counter = Resource::new(\n        move || reset_counters.version().get(),\n        move |_| async move {\n            (\n                get_counters(ticket).await,\n                if ticket == 0 { \"SSR\" } else { \"CSR\" }.to_string(),\n                ticket,\n            )\n        },\n    );\n    let counter_view = move || {\n        Suspend::new(async move {\n            // ensure current mode and ticket are both updated\n            let (counters, mode, ticket) = res_counter.await;\n            counters.map(|counters| {\n                let clear_suspense_counters = move |_| {\n                    suspense_counters.update(|c| {\n                        // leptos::logging::log!(\"resetting suspense counters\");\n                        *c = SuspenseCounters::default();\n                    });\n                };\n                view! {\n                    <h3 id=\"server-calls\">\"Server Calls (\"{mode}\")\"</h3>\n                    <dl>\n                        <dt>\"list_items\"</dt>\n                        <dd id=\"list_items\">{counters.list_items}</dd>\n                        <dt>\"get_item\"</dt>\n                        <dd id=\"get_item\">{counters.get_item}</dd>\n                        <dt>\"inspect_item_root\"</dt>\n                        <dd id=\"inspect_item_root\">{counters.inspect_item_root}</dd>\n                        <dt>\"inspect_item_field\"</dt>\n                        <dd id=\"inspect_item_field\">{counters.inspect_item_field}</dd>\n                    </dl>\n                    <ActionForm action=reset_counters>\n                        <input type=\"hidden\" name=\"ticket\" value=format!(\"{ticket}\") />\n                        <input\n                            id=\"reset-counters\"\n                            type=\"submit\"\n                            value=\"Reset Counters\"\n                            on:click=clear_suspense_counters\n                        />\n                    </ActionForm>\n                }\n            })\n        })\n    };\n\n    view! {\n        <h2>\"Counters\"</h2>\n\n        <h3 id=\"suspend-calls\">\"Suspend Calls\"</h3>\n        {move || {\n            suspense_counters\n                .with(|c| {\n                    view! {\n                        <dl>\n                            <dt>\"item_listing\"</dt>\n                            <dd id=\"item_listing\">{c.item_listing}</dd>\n                            <dt>\"item_overview\"</dt>\n                            <dd id=\"item_overview\">{c.item_overview}</dd>\n                            <dt>\"item_inspect\"</dt>\n                            <dd id=\"item_inspect\">{c.item_inspect}</dd>\n                        </dl>\n                    }\n                })\n        }}\n\n        <Suspense>{counter_view}</Suspense>\n    }\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq)]\npub struct FieldNavItem {\n    pub href: String,\n    pub text: String,\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq)]\npub struct FieldNavCtx(pub Option<Vec<FieldNavItem>>);\n\nimpl From<Vec<FieldNavItem>> for FieldNavCtx {\n    fn from(item: Vec<FieldNavItem>) -> Self {\n        Self(Some(item))\n    }\n}\n\n#[component]\npub fn FieldNavPortlet() -> impl IntoView {\n    let ctx = expect_context::<ReadSignal<Option<FieldNavCtx>>>();\n\n    view! {\n        <ShowLet some=ctx let:ctx>\n            <div id=\"FieldNavPortlet\">\n                <span>\"FieldNavPortlet:\"</span>\n                <nav>\n                    {ctx\n                        .0\n                        .map(|ctx| {\n                            ctx.into_iter()\n                                .map(|FieldNavItem { href, text }| {\n                                    view! { <A href=href>{text}</A> }\n                                })\n                                .collect_view()\n                        })}\n                </nav>\n            </div>\n        </ShowLet>\n    }\n}\n\npub fn provide_field_nav_portlet_context() {\n    // wrapping the Ctx in an Option allows better ergonomics whenever it isn't needed\n    let (ctx, set_ctx) = signal(None::<FieldNavCtx>);\n    provide_context(ctx);\n    provide_context(set_ctx);\n}\n"
  },
  {
    "path": "examples/suspense_tests/src/lib.rs",
    "content": "pub mod app;\nmod instrumented;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n\n    console_error_panic_hook::set_once();\n\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/suspense_tests/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    use actix_files::Files;\n    use actix_web::*;\n    use leptos::prelude::*;\n    use leptos_actix::{generate_route_list, LeptosRoutes};\n    use suspense_tests::app::*;\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    println!(\"listening on http://{}\", &addr);\n\n    HttpServer::new(move || {\n        // Generate the list of routes in your Leptos App\n        let routes = generate_route_list(App);\n        let leptos_options = &conf.leptos_options;\n        let site_root = &leptos_options.site_root;\n\n        App::new()\n            .leptos_routes(routes, {\n                let leptos_options = leptos_options.clone();\n                move || {\n                    use leptos::prelude::*;\n\n                    view! {\n                        <!DOCTYPE html>\n                        <html lang=\"en\">\n                            <head>\n                                <meta charset=\"utf-8\"/>\n                                <meta\n                                    name=\"viewport\"\n                                    content=\"width=device-width, initial-scale=1\"\n                                />\n                                <AutoReload options=leptos_options.clone()/>\n                                <HydrationScripts options=leptos_options.clone()/>\n                            </head>\n                            <body>\n                                <App/>\n                            </body>\n                        </html>\n                    }\n            }})\n            .service(Files::new(\"/\", site_root.as_ref()))\n    })\n    .bind(addr)?\n    .workers(1)\n    .run()\n    .await\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "examples/tailwind_actix/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# Support playwright testing\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\npnpm-lock.yaml\n"
  },
  {
    "path": "examples/tailwind_actix/Cargo.toml",
    "content": "[package]\nname = \"tailwind\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nleptos = { path = \"../../leptos\" }\nleptos_actix = { path = \"../../integrations/actix\", optional = true }\nleptos_meta = { path = \"../../meta\" }\nleptos_router = { path = \"../../router\" }\n\n# dependencies for browser (enable when hydrate set)\nconsole_error_panic_hook = { version = \"0.1.7\", optional = true }\nwasm-bindgen = { version = \"0.2.93\", optional = true }\n\n# dependencies for server (enable when ssr set)\nactix-files = { version = \"0.6.6\", optional = true }\nactix-web = { version = \"4.8\", features = [\"macros\"], optional = true }\n\n[features]\nhydrate = [\"leptos/hydrate\", \"dep:wasm-bindgen\", \"dep:console_error_panic_hook\"]\nssr = [\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:leptos_actix\",\n  \"dep:actix-web\",\n  \"dep:actix-files\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\n  \"actix-files\",\n  \"actix-web\",\n  \"console_error_panic_hook\",\n  \"leptos_actix\",\n  \"wasm-bindgen\",\n]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[profile.release]\ncodegen-units = 1\nlto = true\nopt-level = 'z'\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name   \noutput-name = \"tailwind_actix\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\t\nsite-pkg-dir = \"pkg\"\n# The tailwind input file.\n#\n# Optional, Activates the tailwind build\ntailwind-input-file = \"input.css\"\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/tailwind_actix/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 henrik\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/tailwind_actix/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/playwright.toml\" },\n    { path = \"../cargo-make/cargo-leptos-test.toml\" },\n]\n\n[env]\nCLIENT_PROCESS_NAME = \"tailwind\"\n"
  },
  {
    "path": "examples/tailwind_actix/README.md",
    "content": "# Leptos Starter Template\n\nThis is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Tailwind\n\nYou can install Tailwind using `npm`:\n\n```bash\nnpm install -D tailwindcss\n```\n\nIf you'd rather not use `npm`, you can install the Tailwind binary [here](https://github.com/tailwindlabs/tailwindcss/releases).\n\n## Setting up with VS Code and Additional Tools\n\nIf you're using VS Code, add the following to your `settings.json`\n\n```json\n  \"emmet.includeLanguages\": {\n    \"rust\": \"html\",\n    \"*.rs\": \"html\"\n  },\n  \"tailwindCSS.includeLanguages\": {\n      \"rust\": \"html\",\n      \"*.rs\": \"html\"\n  },\n  \"files.associations\": {\n      \"*.rs\": \"rust\"\n  },\n  \"editor.quickSuggestions\": {\n    \"other\": \"on\",\n    \"comments\": \"on\",\n    \"strings\": true\n  },\n  \"css.validate\": false,\n```\n\nInstall [Tailwind CSS Intellisense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss).\n\nInstall [VS Browser](https://marketplace.visualstudio.com/items?itemName=Phu1237.vs-browser) extension (allows you to open a browser at the right window).\n\nAllow vscode Ports forward: 3000, 3001.\n\n### Attribution\n\nMany thanks to GreatGreg for putting together this guide. You can find the original, with added details, [here](https://github.com/leptos-rs/leptos/discussions/125).\n"
  },
  {
    "path": "examples/tailwind_actix/end2end/package.json",
    "content": "{\n  \"name\": \"end2end\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {},\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.28.0\"\n  }\n}\n"
  },
  {
    "path": "examples/tailwind_actix/end2end/playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from \"@playwright/test\";\nimport { devices } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://github.com/motdotla/dotenv\n */\n// require('dotenv').config();\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nconst config: PlaywrightTestConfig = {\n  testDir: \"./tests\",\n  /* Maximum time one test can run for. */\n  timeout: 30 * 1000,\n  expect: {\n    /**\n     * Maximum time expect() should wait for the condition to be met.\n     * For example in `await expect(locator).toHaveText();`\n     */\n    timeout: 5000,\n  },\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !!process.env.CI,\n  /* Retry on CI only */\n  retries: process.env.CI ? 2 : 0,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.CI ? 1 : undefined,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: \"html\",\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */\n    actionTimeout: 0,\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    // baseURL: 'http://localhost:3000',\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: \"on-first-retry\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"chromium\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n      },\n    },\n\n    {\n      name: \"firefox\",\n      use: {\n        ...devices[\"Desktop Firefox\"],\n      },\n    },\n\n    {\n      name: \"webkit\",\n      use: {\n        ...devices[\"Desktop Safari\"],\n      },\n    },\n\n    /* Test against mobile viewports. */\n    // {\n    //   name: 'Mobile Chrome',\n    //   use: {\n    //     ...devices['Pixel 5'],\n    //   },\n    // },\n    // {\n    //   name: 'Mobile Safari',\n    //   use: {\n    //     ...devices['iPhone 12'],\n    //   },\n    // },\n\n    /* Test against branded browsers. */\n    // {\n    //   name: 'Microsoft Edge',\n    //   use: {\n    //     channel: 'msedge',\n    //   },\n    // },\n    // {\n    //   name: 'Google Chrome',\n    //   use: {\n    //     channel: 'chrome',\n    //   },\n    // },\n  ],\n\n  /* Folder for test artifacts such as screenshots, videos, traces, etc. */\n  // outputDir: 'test-results/',\n\n  /* Run your local dev server before starting the tests */\n  // webServer: {\n  //   command: 'npm run start',\n  //   port: 3000,\n  // },\n};\n\nexport default config;\n"
  },
  {
    "path": "examples/tailwind_actix/end2end/tests/example.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest(\"homepage has title 'Leptos + Tailwindcss'\", async ({ page }) => {\n  await page.goto(\"http://localhost:3000/\");\n\n  await expect(page).toHaveTitle(\"Leptos + Tailwindcss\");\n});\n"
  },
  {
    "path": "examples/tailwind_actix/input.css",
    "content": "@import \"tailwindcss\";\n"
  },
  {
    "path": "examples/tailwind_actix/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/tailwind_actix/src/app.rs",
    "content": "use leptos::prelude::*;\nuse leptos_meta::*;\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router},\n    StaticSegment,\n};\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/tailwind_actix.css\"/>\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/favicon.ico\"/>\n        <Router>\n            <FlatRoutes fallback=|| \"Page not found.\">\n                <Route path=StaticSegment(\"\") view=Home/>\n            </FlatRoutes>\n        </Router>\n    }\n}\n\n#[component]\nfn Home() -> impl IntoView {\n    let (value, set_value) = signal(0);\n\n    // thanks to https://tailwindcomponents.com/component/blue-buttons-example for the showcase layout\n    view! {\n        <Title text=\"Leptos + Tailwindcss\"/>\n        <main>\n            <div class=\"bg-gradient-to-tl from-blue-800 to-blue-500 text-white font-mono flex flex-col min-h-screen\">\n                <div class=\"flex flex-row-reverse flex-wrap m-auto\">\n                    <button on:click=move |_| set_value.update(|value| *value += 1) class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-700 border-blue-800 text-white\">\n                        \"+\"\n                    </button>\n                    <button class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-800 border-blue-900 text-white\">\n                        {value}\n                    </button>\n                    <button\n                        on:click=move |_| set_value.update(|value| *value -= 1)\n                        class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-700 border-blue-800 text-white\"\n                        class:invisible=move || {value.get() < 1}\n                    >\n                        \"-\"\n                    </button>\n                </div>\n            </div>\n        </main>\n    }\n}\n"
  },
  {
    "path": "examples/tailwind_actix/src/lib.rs",
    "content": "mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::app::App;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/tailwind_actix/src/main.rs",
    "content": "mod app;\n\nuse crate::app::*;\nuse actix_files::Files;\nuse actix_web::*;\nuse leptos::prelude::*;\nuse leptos_actix::{generate_route_list, LeptosRoutes};\nuse leptos_meta::MetaTags;\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n\n    HttpServer::new(move || {\n        // Generate the list of routes in your Leptos App\n        let routes = generate_route_list(App);\n        let leptos_options = &conf.leptos_options;\n        let site_root = &leptos_options.site_root;\n\n        App::new()\n            .leptos_routes(routes, {\n                let leptos_options = leptos_options.clone();\n                move || {\n                    use leptos::prelude::*;\n\n                    view! {\n                        <!DOCTYPE html>\n                        <html lang=\"en\">\n                            <head>\n                                <meta charset=\"utf-8\"/>\n                                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                                <AutoReload options=leptos_options.clone() />\n                                <HydrationScripts options=leptos_options.clone()/>\n                                <MetaTags/>\n                            </head>\n                            <body>\n                                <App/>\n                            </body>\n                        </html>\n                    }\n            }})\n            .service(Files::new(\"/\", site_root.as_ref()))\n            .wrap(middleware::Compress::default())\n    })\n    .bind(&addr)?\n    .run()\n    .await\n}\n"
  },
  {
    "path": "examples/tailwind_axum/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# Support playwright testing\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\npnpm-lock.yaml\n"
  },
  {
    "path": "examples/tailwind_axum/Cargo.toml",
    "content": "[package]\nname = \"leptos-tailwind\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.8.1\", optional = true }\nconsole_error_panic_hook = \"0.1.7\"\nleptos = { path = \"../../leptos\" }\nleptos_meta = { path = \"../../meta\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../router\" }\ntokio = { version = \"1.39\", features = [\n  \"rt-multi-thread\",\n  \"macros\",\n], optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\nwasm-bindgen = \"0.2.93\"\nthiserror = \"2.0.12\"\ntracing = { version = \"0.1.40\", optional = true }\nhttp = \"1.1\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:leptos_axum\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:tracing\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tokio\", \"tower\", \"tower-http\", \"leptos_axum\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"leptos_tailwind\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# The tailwind input file.\n#\n# Optional, Activates the tailwind build\ntailwind-input-file = \"input.css\"\n\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/tailwind_axum/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"leptos-tailwind\"\n"
  },
  {
    "path": "examples/tailwind_axum/README.md",
    "content": "# Leptos with Axum + TailwindCSS Template\n\nThis is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/leptos-rs/leptos) web framework, Axum server, and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n\n# Tailwind Migration\n\nIf you're updating from Tailwind 3 to Tailwind 4, it would be informative to view the [tailwind migration document](https://tailwindcss.com/docs/installation/tailwind-cli) as things have changed.\n"
  },
  {
    "path": "examples/tailwind_axum/end2end/package.json",
    "content": "{\n  \"name\": \"end2end\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {},\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.28.0\"\n  }\n}\n"
  },
  {
    "path": "examples/tailwind_axum/end2end/playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from \"@playwright/test\";\nimport { devices } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://github.com/motdotla/dotenv\n */\n// require('dotenv').config();\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nconst config: PlaywrightTestConfig = {\n  testDir: \"./tests\",\n  /* Maximum time one test can run for. */\n  timeout: 30 * 1000,\n  expect: {\n    /**\n     * Maximum time expect() should wait for the condition to be met.\n     * For example in `await expect(locator).toHaveText();`\n     */\n    timeout: 5000,\n  },\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !!process.env.CI,\n  /* Retry on CI only */\n  retries: process.env.CI ? 2 : 0,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.CI ? 1 : undefined,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: \"html\",\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */\n    actionTimeout: 0,\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    // baseURL: 'http://localhost:3000',\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: \"on-first-retry\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"chromium\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n      },\n    },\n\n    {\n      name: \"firefox\",\n      use: {\n        ...devices[\"Desktop Firefox\"],\n      },\n    },\n\n    {\n      name: \"webkit\",\n      use: {\n        ...devices[\"Desktop Safari\"],\n      },\n    },\n\n    /* Test against mobile viewports. */\n    // {\n    //   name: 'Mobile Chrome',\n    //   use: {\n    //     ...devices['Pixel 5'],\n    //   },\n    // },\n    // {\n    //   name: 'Mobile Safari',\n    //   use: {\n    //     ...devices['iPhone 12'],\n    //   },\n    // },\n\n    /* Test against branded browsers. */\n    // {\n    //   name: 'Microsoft Edge',\n    //   use: {\n    //     channel: 'msedge',\n    //   },\n    // },\n    // {\n    //   name: 'Google Chrome',\n    //   use: {\n    //     channel: 'chrome',\n    //   },\n    // },\n  ],\n\n  /* Folder for test artifacts such as screenshots, videos, traces, etc. */\n  // outputDir: 'test-results/',\n\n  /* Run your local dev server before starting the tests */\n  // webServer: {\n  //   command: 'npm run start',\n  //   port: 3000,\n  // },\n};\n\nexport default config;\n"
  },
  {
    "path": "examples/tailwind_axum/end2end/tests/example.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest(\"homepage has title 'Leptos + Tailwindcss'\", async ({ page }) => {\n  await page.goto(\"http://localhost:3000/\");\n\n  await expect(page).toHaveTitle(\"Leptos + Tailwindcss\");\n});\n"
  },
  {
    "path": "examples/tailwind_axum/input.css",
    "content": "@import \"tailwindcss\";\n"
  },
  {
    "path": "examples/tailwind_axum/package.json",
    "content": "{\n  \"author\": \"\",\n  \"dependencies\": {\n    \"tailwindcss\": \"^4.0.17\"\n  },\n  \"description\": \"<picture>\\r     <source srcset=\\\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg\\\" media=\\\"(prefers-color-scheme: dark)\\\">\\r     <img src=\\\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg\\\" alt=\\\"Leptos Logo\\\">\\r </picture>\",\n  \"devDependencies\": {\n    \"@tailwindcss/cli\": \"^4.0.17\"\n  },\n  \"keywords\": [],\n  \"license\": \"ISC\",\n  \"main\": \"index.js\",\n  \"name\": \"leptos-tailwind\",\n  \"scripts\": {\n    \"build\": \"npx tailwindcss -i ./input.css -o ./style/output.css\",\n    \"watch\": \"npx tailwindcss -i ./input.css -o ./style/output.css --watch\"\n  },\n  \"version\": \"1.0.0\"\n}\n"
  },
  {
    "path": "examples/tailwind_axum/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/tailwind_axum/src/app.rs",
    "content": "use leptos::prelude::*;\nuse leptos_meta::*;\nuse leptos_router::{\n    components::{FlatRoutes, Route, Router},\n    StaticSegment,\n};\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options/>\n                <link rel=\"stylesheet\" id=\"leptos\" href=\"/pkg/leptos_tailwind.css\"/>\n                <link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\"/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n\n    view! {\n        <Router>\n            <FlatRoutes fallback=|| \"Page not found.\">\n                <Route path=StaticSegment(\"\") view=Home/>\n            </FlatRoutes>\n        </Router>\n    }\n}\n\n#[component]\nfn Home() -> impl IntoView {\n    let (value, set_value) = signal(0);\n\n    // thanks to https://tailwindcomponents.com/component/blue-buttons-example for the showcase layout\n    view! {\n        <Title text=\"Leptos + Tailwindcss\"/>\n        <main>\n            <div class=\"bg-gradient-to-tl from-blue-800 to-blue-500 text-white font-mono flex flex-col min-h-screen\">\n                <div class=\"flex flex-row-reverse flex-wrap m-auto\">\n                    <button on:click=move |_| set_value.update(|value| *value += 1) class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-700 border-blue-800 text-white\">\n                        \"+\"\n                    </button>\n                    <button class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-800 border-blue-900 text-white\">\n                        {value}\n                    </button>\n                    <button\n                        on:click=move |_| set_value.update(|value| *value -= 1)\n                        class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-700 border-blue-800 text-white\"\n                        class:invisible=move || {value.get() < 1}\n                    >\n                        \"-\"\n                    </button>\n                </div>\n            </div>\n        </main>\n    }\n}\n"
  },
  {
    "path": "examples/tailwind_axum/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::app::App;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/tailwind_axum/src/main.rs",
    "content": "use axum::Router;\nuse leptos::prelude::*;\nuse leptos_axum::{generate_route_list, LeptosRoutes};\nuse leptos_tailwind::app::{shell, App};\n\n#[tokio::main]\nasync fn main() {\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    println!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for a purely client-side app\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "examples/tailwind_csr/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# Support playwright testing\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\npnpm-lock.yaml\n"
  },
  {
    "path": "examples/tailwind_csr/Cargo.toml",
    "content": "[package]\nname = \"tailwind-csr-trunk\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nleptos_meta = { path = \"../../meta\" }\nleptos_router = { path = \"../../router\" }\ngloo-net = { version = \"0.6.0\", features = [\"http\"] }\nconsole_error_panic_hook = { version = \"0.1.7\" }\n"
  },
  {
    "path": "examples/tailwind_csr/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 henrik\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/tailwind_csr/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/tailwind_csr/README.md",
    "content": "# Leptos Starter Template\n\nThis is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [trunk](https://github.com/thedodd/trunk) tool.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Tailwind\n\n`Trunk.toml` is configured to build the CSS automatically.\n\nYou can install Tailwind using `npm`:\n\n```bash\nnpm install -D tailwindcss\n```\n\nIf you'd rather not use `npm`, you can install the Tailwind binary [here](https://github.com/tailwindlabs/tailwindcss/releases).\n\n## Setting up with VS Code and Additional Tools\n\nIf you're using VS Code, add the following to your `settings.json`\n\n```json\n  \"emmet.includeLanguages\": {\n    \"rust\": \"html\",\n    \"*.rs\": \"html\"\n  },\n  \"tailwindCSS.includeLanguages\": {\n      \"rust\": \"html\",\n      \"*.rs\": \"html\"\n  },\n  \"files.associations\": {\n      \"*.rs\": \"rust\"\n  },\n  \"editor.quickSuggestions\": {\n    \"other\": \"on\",\n    \"comments\": \"on\",\n    \"strings\": true\n  },\n  \"css.validate\": false,\n```\n\nInstall [Tailwind CSS Intellisense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss).\n\nInstall [VS Browser](https://marketplace.visualstudio.com/items?itemName=Phu1237.vs-browser) extension (allows you to open a browser at the right window).\n\nAllow vscode Ports forward: 3000, 3001.\n\n### Attribution\n\nMany thanks to GreatGreg for putting together this guide. You can find the original, with added details, [here](https://github.com/leptos-rs/leptos/discussions/125).\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/tailwind_csr/Trunk.toml",
    "content": "[tools]\ntailwindcss = \"4.1.13\"\n\n"
  },
  {
    "path": "examples/tailwind_csr/end2end/package.json",
    "content": "{\n  \"name\": \"end2end\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {},\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.28.0\"\n  }\n}\n"
  },
  {
    "path": "examples/tailwind_csr/end2end/playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from \"@playwright/test\";\nimport { devices } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://github.com/motdotla/dotenv\n */\n// require('dotenv').config();\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nconst config: PlaywrightTestConfig = {\n  testDir: \"./tests\",\n  /* Maximum time one test can run for. */\n  timeout: 30 * 1000,\n  expect: {\n    /**\n     * Maximum time expect() should wait for the condition to be met.\n     * For example in `await expect(locator).toHaveText();`\n     */\n    timeout: 5000,\n  },\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !!process.env.CI,\n  /* Retry on CI only */\n  retries: process.env.CI ? 2 : 0,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.CI ? 1 : undefined,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: \"html\",\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */\n    actionTimeout: 0,\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    // baseURL: 'http://localhost:3000',\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: \"on-first-retry\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"chromium\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n      },\n    },\n\n    {\n      name: \"firefox\",\n      use: {\n        ...devices[\"Desktop Firefox\"],\n      },\n    },\n\n    {\n      name: \"webkit\",\n      use: {\n        ...devices[\"Desktop Safari\"],\n      },\n    },\n\n    /* Test against mobile viewports. */\n    // {\n    //   name: 'Mobile Chrome',\n    //   use: {\n    //     ...devices['Pixel 5'],\n    //   },\n    // },\n    // {\n    //   name: 'Mobile Safari',\n    //   use: {\n    //     ...devices['iPhone 12'],\n    //   },\n    // },\n\n    /* Test against branded browsers. */\n    // {\n    //   name: 'Microsoft Edge',\n    //   use: {\n    //     channel: 'msedge',\n    //   },\n    // },\n    // {\n    //   name: 'Google Chrome',\n    //   use: {\n    //     channel: 'chrome',\n    //   },\n    // },\n  ],\n\n  /* Folder for test artifacts such as screenshots, videos, traces, etc. */\n  // outputDir: 'test-results/',\n\n  /* Run your local dev server before starting the tests */\n  // webServer: {\n  //   command: 'npm run start',\n  //   port: 3000,\n  // },\n};\n\nexport default config;\n"
  },
  {
    "path": "examples/tailwind_csr/end2end/tests/example.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest(\"homepage has title 'Leptos + Tailwindcss'\", async ({ page }) => {\n  await page.goto(\"http://localhost:8080/\");\n\n  await expect(page).toHaveTitle(\"Leptos + Tailwindcss\");\n});\n"
  },
  {
    "path": "examples/tailwind_csr/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link data-trunk rel=\"rust\" data-wasm-opt=\"z\" />\n    <link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\" />\n    <link data-trunk rel=\"tailwind-css\" href=\"input.css\" />\n    <title>Leptos • Counter with Tailwind</title>\n  </head>\n\n  <body></body>\n</html>\n"
  },
  {
    "path": "examples/tailwind_csr/input.css",
    "content": "@import \"tailwindcss\";"
  },
  {
    "path": "examples/tailwind_csr/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/tailwind_csr/src/app.rs",
    "content": "use leptos::prelude::*;\nuse leptos_meta::*;\nuse leptos_router::{\n    components::{Route, Router, Routes},\n    StaticSegment,\n};\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/style/output.css\"/>\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/favicon.ico\"/>\n        <Router>\n            <Routes fallback=|| \"Page not found.\">\n                <Route path=StaticSegment(\"\") view=Home/>\n            </Routes>\n        </Router>\n    }\n}\n\n#[component]\nfn Home() -> impl IntoView {\n    let (value, set_value) = signal(0);\n\n    // thanks to https://tailwindcomponents.com/component/blue-buttons-example for the showcase layout\n    view! {\n        <Title text=\"Leptos + Tailwindcss\"/>\n        <main>\n            <div class=\"bg-gradient-to-tl from-blue-800 to-blue-500 text-white font-mono flex flex-col min-h-screen\">\n                <div class=\"flex flex-row-reverse flex-wrap m-auto\">\n                    <button on:click=move |_| set_value.update(|value| *value += 1) class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-700 border-blue-800 text-white\">\n                        \"+\"\n                    </button>\n                    <button class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-800 border-blue-900 text-white\">\n                        {value}\n                    </button>\n                    <button\n                        on:click=move |_| set_value.update(|value| *value -= 1)\n                        class=\"rounded px-3 py-2 m-1 border-b-4 border-l-2 shadow-lg bg-blue-700 border-blue-800 text-white\"\n                        class:invisible=move || {value.get() < 1}\n                    >\n                        \"-\"\n                    </button>\n                </div>\n            </div>\n        </main>\n    }\n}\n"
  },
  {
    "path": "examples/tailwind_csr/src/main.rs",
    "content": "mod app;\n\nuse app::*;\nuse leptos::{logging, mount};\n\npub fn main() {\n    console_error_panic_hook::set_once();\n    logging::log!(\"csr mode - mounting to body\");\n    mount::mount_to_body(App);\n}\n"
  },
  {
    "path": "examples/timer/Cargo.toml",
    "content": "[package]\nname = \"timer\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_log = \"1.0\"\nlog = \"0.4.22\"\nconsole_error_panic_hook = \"0.1.7\"\nwasm-bindgen = \"0.2.93\"\n\n[dependencies.web-sys]\nversion = \"0.3.70\"\nfeatures = [\"Window\"]\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.42\"\n"
  },
  {
    "path": "examples/timer/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n"
  },
  {
    "path": "examples/timer/README.md",
    "content": "# Leptos Timer Example\n\nThis example creates a simple timer based on `setInterval` in a client side rendered app with Rust and WASM.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/timer/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/timer/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/timer/src/lib.rs",
    "content": "use leptos::prelude::*;\nuse std::time::Duration;\n\n/// Timer example, demonstrating the use of `use_interval`.\n#[component]\npub fn TimerDemo() -> impl IntoView {\n    // count_a updates with a fixed interval of 1000 ms, whereas count_b has a dynamic\n    // update interval.\n    let count_a = RwSignal::new(0_i32);\n    let count_b = RwSignal::new(0_i32);\n\n    let interval = RwSignal::new(1000);\n\n    use_interval(1000, move || {\n        count_a.update(|c| *c += 1);\n    });\n    use_interval(interval, move || {\n        count_b.update(|c| *c += 1);\n    });\n\n    view! {\n        <div>\n            <div>\"Count A (fixed interval of 1000 ms)\"</div>\n            <div>{count_a}</div>\n            <div>\"Count B (dynamic interval, currently \" {interval} \" ms)\"</div>\n            <div>{count_b}</div>\n            <input prop:value=interval on:input:target=move |ev| {\n                if let Ok(value) = ev.target().value().parse::<u64>() {\n                    interval.set(value);\n                }\n            }/>\n        </div>\n    }\n}\n\n/// Hook to wrap the underlying `setInterval` call and make it reactive w.r.t.\n/// possible changes of the timer interval.\npub fn use_interval<T, F>(interval_millis: T, f: F)\nwhere\n    F: Fn() + Clone + 'static,\n    T: Into<Signal<u64>> + 'static,\n{\n    let interval_millis = interval_millis.into();\n    Effect::new(move |prev_handle: Option<IntervalHandle>| {\n        // effects get their previous return value as an argument\n        // each time the effect runs, it will return the interval handle\n        // so if we have a previous one, we cancel it\n        if let Some(prev_handle) = prev_handle {\n            prev_handle.clear();\n        };\n\n        // here, we return the handle\n        set_interval_with_handle(\n            f.clone(),\n            // this is the only reactive access, so this effect will only\n            // re-run when the interval changes\n            Duration::from_millis(interval_millis.get()),\n        )\n        .expect(\"could not create interval\")\n    });\n}\n"
  },
  {
    "path": "examples/timer/src/main.rs",
    "content": "use leptos::prelude::*;\nuse timer::TimerDemo;\n\npub fn main() {\n    console_error_panic_hook::set_once();\n    mount_to_body(TimerDemo)\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/.gitignore",
    "content": ".leptos.kdl"
  },
  {
    "path": "examples/todo_app_sqlite/Cargo.toml",
    "content": "[package]\nname = \"todo_app_sqlite\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nactix-files = { version = \"0.6.6\", optional = true }\nactix-web = { version = \"4.8\", optional = true, features = [\"macros\"] }\nanyhow = \"1.0\"\nbroadcaster = \"1.0\"\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nfutures = \"0.3.30\"\nleptos = { path = \"../../leptos\" }\nleptos_actix = { path = \"../../integrations/actix\", optional = true }\nlog = \"0.4.22\"\nsimple_logger = \"5.0\"\ngloo = { git = \"https://github.com/rustwasm/gloo\" }\nsqlx = { version = \"0.8.6\", features = [\n  \"runtime-tokio-rustls\",\n  \"sqlite\",\n], optional = true }\nwasm-bindgen = \"0.2.93\"\ntokio = { version = \"1.39\", features = [\"rt\", \"time\"], optional = true }\nserver_fn = { path = \"../../server_fn\", features = [\"cbor\"] }\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:actix-files\",\n  \"dep:actix-web\",\n  \"dep:sqlx\",\n  \"leptos/ssr\",\n  \"leptos_actix\",\n  \"dep:tokio\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"actix-files\", \"actix-web\", \"leptos_actix\", \"sqlx\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"todo_app_sqlite\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"cargo make test-ui\"\nend2end-dir = \"e2e\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/todo_app_sqlite/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/todo_app_sqlite/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/cargo-leptos-webdriver-test.toml\" },\n]\n\n[env]\nCLIENT_PROCESS_NAME = \"todo_app_sqlite\"\n\n[tasks.test-ui]\ncwd = \"./e2e\"\ncommand = \"cargo\"\nargs = [\"make\", \"test-ui\", \"${@}\"]\n"
  },
  {
    "path": "examples/todo_app_sqlite/README.md",
    "content": "# Leptos Todo App Sqlite\n\nThis example creates a basic todo app with an Actix backend that uses Leptos' server functions to call sqlx from the client and seamlessly run it on the server.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## E2E Testing\n\nSee the [E2E README](./e2e/README.md) for more information about the testing strategy.\n\n## Rendering\n\nSee the [SSR Notes](../SSR_NOTES.md) for more information about Server Side Rendering.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/Cargo.toml",
    "content": "[package]\nname = \"todo_app_sqlite_e2e\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\nanyhow = \"1.0\"\nasync-trait = \"0.1.81\"\ncucumber = \"0.21.1\"\nfantoccini = \"0.21.1\"\npretty_assertions = \"1.4\"\nserde_json = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\", \"time\"] }\nurl = \"2.5\"\n\n[[test]]\nname = \"app_suite\"\nharness = false    # Allow Cucumber to print output instead of libtest\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.test]\nenv = { RUN_AUTOMATICALLY = false }\ncondition = { env_true = [\"RUN_AUTOMATICALLY\"] }\n\n[tasks.ci]\n\n[tasks.test-ui]\ncommand = \"cargo\"\nargs = [\n  \"test\",\n  \"--test\",\n  \"app_suite\",\n  \"--\",\n  \"--retry\",\n  \"2\",\n  \"--retry-after\",\n  \"5sec\",\n  \"--fail-fast\",\n  \"${@}\",\n]\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/README.md",
    "content": "# E2E Testing\n\nThis example demonstrates e2e testing with Rust using executable requirements.\n\n## Testing Stack\n\n|    |      Role      |  Description |\n|---|---|---|\n| [Cucumber](https://github.com/cucumber-rs/cucumber/tree/main) | Test Runner | Run [Gherkin](https://cucumber.io/docs/gherkin/reference/) specifications as Rust tests |\n| [Fantoccini](https://github.com/jonhoo/fantoccini/tree/main) | Browser Client | Interact with web pages through WebDriver |\n| [Cargo Leptos ](https://github.com/leptos-rs/cargo-leptos) | Build Tool |  Compile example and start the server and end-2-end tests |\n| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome\n\n## Testing Organization\n\nTesting is organized around what a user can do and see/not see. Test scenarios are grouped by the **user action** and the **object** of that action. This makes it easier to locate and reason about requirements.\n\nHere is a brief overview of how things fit together.\n\n```bash\nfeatures\n└── {action}_{object}.feature   # Specify test scenarios\ntests\n├── fixtures\n│   ├── action.rs               # Perform a user action (click, type, etc.)\n│   ├── check.rs                # Assert what a user can see/not see\n│   ├── find.rs                 # Query page elements\n│   ├── mod.rs\n│   └── world\n│       ├── action_steps.rs     # Map Gherkin steps to user actions\n│       ├── check_steps.rs      # Map Gherkin steps to user expectations\n│       └── mod.rs\n└── app_suite.rs                # Test main \n```\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/features/add_todo.feature",
    "content": "@add_todo\nFeature: Add Todo\n\n    Background:\n        Given I see the app\n\n    @add_todo-see\n    Scenario: Should see the todo\n        Given I set the todo as Buy Bread\n        When I click the Add button\n        Then I see the todo named Buy Bread\n\n    # @allow.skipped\n    @add_todo-style\n    Scenario: Should see the pending todo\n        When I add a todo as Buy Oranges\n        Then I see the pending todo\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/features/delete_todo.feature",
    "content": "@delete_todo\nFeature: Delete Todo\n\n    Background:\n        Given I see the app\n\n    @serial\n    @delete_todo-remove\n    Scenario: Should not see the deleted todo\n        Given I add a todo as Buy Yogurt\n        When I delete the todo named Buy Yogurt\n        Then I do not see the todo named Buy Yogurt\n\n    @serial\n    @delete_todo-message\n    Scenario: Should see the empty list message\n        When I empty the todo list\n        Then I see the empty list message is No tasks were found."
  },
  {
    "path": "examples/todo_app_sqlite/e2e/features/open_app.feature",
    "content": "@open_app\nFeature: Open App\n\n  @open_app-title\n  Scenario: Should see the home page title\n    When I open the app\n    Then I see the page title is My Tasks\n\n  @open_app-label\n  Scenario: Should see the input label\n    When I open the app\n    Then I see the label of the input is Add a Todo"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/tests/app_suite.rs",
    "content": "mod fixtures;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fixtures::world::AppWorld;\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    AppWorld::cucumber()\n        .fail_on_skipped()\n        .run_and_exit(\"./features\")\n        .await;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/tests/fixtures/action.rs",
    "content": "use super::{find, world::HOST};\nuse anyhow::Result;\nuse fantoccini::Client;\nuse std::result::Result::Ok;\nuse tokio::{self, time};\n\npub async fn goto_path(client: &Client, path: &str) -> Result<()> {\n    let url = format!(\"{}{}\", HOST, path);\n    client.goto(&url).await?;\n\n    Ok(())\n}\n\npub async fn add_todo(client: &Client, text: &str) -> Result<()> {\n    fill_todo(client, text).await?;\n    click_add_button(client).await?;\n    Ok(())\n}\n\npub async fn fill_todo(client: &Client, text: &str) -> Result<()> {\n    let textbox = find::todo_input(client).await;\n    textbox.send_keys(text).await?;\n\n    Ok(())\n}\n\npub async fn click_add_button(client: &Client) -> Result<()> {\n    let add_button = find::add_button(client).await;\n    add_button.click().await?;\n\n    Ok(())\n}\n\npub async fn empty_todo_list(client: &Client) -> Result<()> {\n    let todos = find::todos(client).await;\n\n    for _todo in todos {\n        let _ = delete_first_todo(client).await?;\n    }\n\n    Ok(())\n}\n\npub async fn delete_first_todo(client: &Client) -> Result<()> {\n    if let Some(element) = find::first_delete_button(client).await {\n        element.click().await.expect(\"Failed to delete todo\");\n        time::sleep(time::Duration::from_millis(250)).await;\n    }\n\n    Ok(())\n}\n\npub async fn delete_todo(client: &Client, text: &str) -> Result<()> {\n    if let Some(element) = find::delete_button(client, text).await {\n        element.click().await?;\n        time::sleep(time::Duration::from_millis(250)).await;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/tests/fixtures/check.rs",
    "content": "use super::find;\nuse anyhow::{Ok, Result};\nuse fantoccini::{Client, Locator};\nuse pretty_assertions::assert_eq;\n\npub async fn text_on_element(\n    client: &Client,\n    selector: &str,\n    expected_text: &str,\n) -> Result<()> {\n    let element = client\n        .wait()\n        .for_element(Locator::Css(selector))\n        .await\n        .expect(\n            format!(\"Element not found by Css selector `{}`\", selector)\n                .as_str(),\n        );\n\n    let actual = element.text().await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n\npub async fn todo_present(\n    client: &Client,\n    text: &str,\n    expected: bool,\n) -> Result<()> {\n    let todo_present = is_todo_present(client, text).await;\n\n    assert_eq!(todo_present, expected);\n\n    Ok(())\n}\n\nasync fn is_todo_present(client: &Client, text: &str) -> bool {\n    let todos = find::todos(client).await;\n\n    for todo in todos {\n        let todo_title = todo.text().await.expect(\"Todo title not found\");\n        if todo_title == text {\n            return true;\n        }\n    }\n\n    false\n}\n\npub async fn todo_is_pending(client: &Client) -> Result<()> {\n    if let None = find::pending_todo(client).await {\n        assert!(false, \"Pending todo not found\");\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/tests/fixtures/find.rs",
    "content": "use fantoccini::{elements::Element, Client, Locator};\n\npub async fn todo_input(client: &Client) -> Element {\n    let textbox = client\n        .wait()\n        .for_element(Locator::Css(\"input[name='title\"))\n        .await\n        .expect(\"Todo textbox not found\");\n\n    textbox\n}\n\npub async fn add_button(client: &Client) -> Element {\n    let button = client\n        .wait()\n        .for_element(Locator::Css(\"input[value='Add']\"))\n        .await\n        .expect(\"\");\n\n    button\n}\n\npub async fn first_delete_button(client: &Client) -> Option<Element> {\n    if let Ok(element) = client\n        .wait()\n        .for_element(Locator::Css(\"li:first-child input[value='X']\"))\n        .await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn delete_button(client: &Client, text: &str) -> Option<Element> {\n    let selector = format!(\"//*[text()='{text}']//input[@value='X']\");\n    if let Ok(element) =\n        client.wait().for_element(Locator::XPath(&selector)).await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn pending_todo(client: &Client) -> Option<Element> {\n    if let Ok(element) =\n        client.wait().for_element(Locator::Css(\".pending\")).await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn todos(client: &Client) -> Vec<Element> {\n    let todos = client\n        .find_all(Locator::Css(\"li\"))\n        .await\n        .expect(\"Todo List not found\");\n\n    todos\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/tests/fixtures/mod.rs",
    "content": "pub mod action;\npub mod check;\npub mod find;\npub mod world;\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/tests/fixtures/world/action_steps.rs",
    "content": "use crate::fixtures::{action, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::{given, when};\n\n#[given(\"I see the app\")]\n#[when(\"I open the app\")]\nasync fn i_open_the_app(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::goto_path(client, \"\").await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I add a todo as (.*)$\")]\n#[when(regex = \"^I add a todo as (.*)$\")]\nasync fn i_add_a_todo_titled(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::add_todo(client, text.as_str()).await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I set the todo as (.*)$\")]\nasync fn i_set_the_todo_as(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::fill_todo(client, &text).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"I click the Add button$\")]\nasync fn i_click_the_button(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::click_add_button(client).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"^I delete the todo named (.*)$\")]\nasync fn i_delete_the_todo_named(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    action::delete_todo(client, text.as_str()).await?;\n\n    Ok(())\n}\n\n#[given(\"the todo list is empty\")]\n#[when(\"I empty the todo list\")]\nasync fn i_empty_the_todo_list(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::empty_todo_list(client).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/tests/fixtures/world/check_steps.rs",
    "content": "use crate::fixtures::{check, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::then;\n\n#[then(regex = \"^I see the page title is (.*)$\")]\nasync fn i_see_the_page_title_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"h1\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the label of the input is (.*)$\")]\nasync fn i_see_the_label_of_the_input_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"label\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the todo named (.*)$\")]\nasync fn i_see_the_todo_is_present(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::todo_present(client, text.as_str(), true).await?;\n\n    Ok(())\n}\n\n#[then(\"I see the pending todo\")]\nasync fn i_see_the_pending_todo(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n\n    check::todo_is_pending(client).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the empty list message is (.*)$\")]\nasync fn i_see_the_empty_list_message_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"ul p\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I do not see the todo named (.*)$\")]\nasync fn i_do_not_see_the_todo_is_present(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::todo_present(client, text.as_str(), false).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/e2e/tests/fixtures/world/mod.rs",
    "content": "pub mod action_steps;\npub mod check_steps;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fantoccini::{\n    error::NewSessionError, wd::Capabilities, Client, ClientBuilder,\n};\n\npub const HOST: &str = \"http://127.0.0.1:3000\";\n\n#[derive(Debug, World)]\n#[world(init = Self::new)]\npub struct AppWorld {\n    pub client: Client,\n}\n\nimpl AppWorld {\n    async fn new() -> Result<Self, anyhow::Error> {\n        let webdriver_client = build_client().await?;\n\n        Ok(Self {\n            client: webdriver_client,\n        })\n    }\n}\n\nasync fn build_client() -> Result<Client, NewSessionError> {\n    let mut cap = Capabilities::new();\n    let arg = serde_json::from_str(\"{\\\"args\\\": [\\\"-headless\\\"]}\").unwrap();\n    cap.insert(\"goog:chromeOptions\".to_string(), arg);\n\n    let client = ClientBuilder::native()\n        .capabilities(cap)\n        .connect(\"http://localhost:4444\")\n        .await?;\n\n    Ok(client)\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/migrations/20221118172000_create_todo_table.sql",
    "content": "CREATE TABLE IF NOT EXISTS todos\n(\n  id          INTEGER NOT NULL PRIMARY KEY,\n  title       VARCHAR,\n  completed   BOOLEAN\n);"
  },
  {
    "path": "examples/todo_app_sqlite/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/todo_app_sqlite/src/lib.rs",
    "content": "pub mod todo;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::todo::*;\n    console_error_panic_hook::set_once();\n    _ = console_log::init_with_level(log::Level::Debug);\n\n    leptos::mount::hydrate_body(TodoApp);\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/src/main.rs",
    "content": "mod todo;\n\n#[cfg(feature = \"ssr\")]\nmod ssr {\n    pub use crate::todo::*;\n    pub use actix_files::Files;\n    pub use actix_web::*;\n    pub use leptos::prelude::*;\n    pub use leptos_actix::{generate_route_list, LeptosRoutes};\n\n    #[get(\"/style.css\")]\n    pub async fn css() -> impl Responder {\n        actix_files::NamedFile::open_async(\"./style.css\").await\n    }\n}\n\n#[cfg(feature = \"ssr\")]\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    use self::{ssr::*, todo::ssr::*};\n\n    let mut conn = db().await.expect(\"couldn't connect to DB\");\n    sqlx::migrate!()\n        .run(&mut conn)\n        .await\n        .expect(\"could not run SQLx migrations\");\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    println!(\"listening on http://{}\", &addr);\n\n    HttpServer::new(move || {\n        // Generate the list of routes in your Leptos App\n        let routes = generate_route_list(TodoApp);\n        let leptos_options = &conf.leptos_options;\n        let site_root = &leptos_options.site_root;\n\n        App::new()\n            .leptos_routes(routes, {\n                let leptos_options = leptos_options.clone();\n                move || {\n                    use leptos::prelude::*;\n\n                    view! {\n                        <!DOCTYPE html>\n                        <html lang=\"en\">\n                            <head>\n                                <meta charset=\"utf-8\"/>\n                                <meta\n                                    name=\"viewport\"\n                                    content=\"width=device-width, initial-scale=1\"\n                                />\n                                <AutoReload options=leptos_options.clone()/>\n                                <HydrationScripts options=leptos_options.clone()/>\n                            </head>\n                            <body>\n                                <TodoApp/>\n                            </body>\n                        </html>\n                    }\n            }})\n            .service(Files::new(\"/\", site_root.as_ref()))\n        //.wrap(middleware::Compress::default())\n    })\n    .bind(&addr)?\n    .run()\n    .await\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/src/todo.rs",
    "content": "use leptos::{either::Either, prelude::*};\nuse serde::{Deserialize, Serialize};\nuse server_fn::ServerFnError;\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\n#[cfg_attr(feature = \"ssr\", derive(sqlx::FromRow))]\npub struct Todo {\n    id: u16,\n    title: String,\n    completed: bool,\n}\n\n#[cfg(feature = \"ssr\")]\npub mod ssr {\n    // use http::{header::SET_COOKIE, HeaderMap, HeaderValue, StatusCode};\n    use leptos::server_fn::ServerFnError;\n    use sqlx::{Connection, SqliteConnection};\n\n    pub async fn db() -> Result<SqliteConnection, ServerFnError> {\n        Ok(SqliteConnection::connect(\"sqlite:Todos.db\").await?)\n    }\n}\n\n#[server]\npub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {\n    use self::ssr::*;\n\n    // this is just an example of how to access server context injected in the handlers\n    let req_parts = use_context::<leptos_actix::Request>();\n\n    if let Some(req_parts) = req_parts {\n        println!(\"Path = {:?}\", req_parts.path());\n    }\n\n    use futures::TryStreamExt;\n\n    let mut conn = db().await?;\n\n    let mut todos = Vec::new();\n    let mut rows =\n        sqlx::query_as::<_, Todo>(\"SELECT * FROM todos\").fetch(&mut conn);\n    while let Some(row) = rows.try_next().await? {\n        todos.push(row);\n    }\n\n    // Lines below show how to set status code and headers on the response\n    // let resp = expect_context::<ResponseOptions>();\n    // resp.set_status(StatusCode::IM_A_TEAPOT);\n    // resp.insert_header(SET_COOKIE, HeaderValue::from_str(\"fizz=buzz\").unwrap());\n\n    Ok(todos)\n}\n\n#[server]\npub async fn add_todo(title: String) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n    let mut conn = db().await?;\n\n    // fake API delay\n    std::thread::sleep(std::time::Duration::from_millis(250));\n\n    match sqlx::query(\"INSERT INTO todos (title, completed) VALUES ($1, false)\")\n        .bind(title)\n        .execute(&mut conn)\n        .await\n    {\n        Ok(_row) => Ok(()),\n        Err(e) => Err(ServerFnError::ServerError(e.to_string())),\n    }\n}\n\n#[server]\npub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n    let mut conn = db().await?;\n\n    Ok(sqlx::query(\"DELETE FROM todos WHERE id = $1\")\n        .bind(id)\n        .execute(&mut conn)\n        .await\n        .map(|_| ())?)\n}\n\n#[component]\npub fn TodoApp() -> impl IntoView {\n    view! {\n        <header>\n            <h1>\"My Tasks\"</h1>\n        </header>\n        <main>\n            <Todos/>\n        </main>\n    }\n}\n\n#[component]\npub fn Todos() -> impl IntoView {\n    let add_todo = ServerMultiAction::<AddTodo>::new();\n    let submissions = add_todo.submissions();\n    let delete_todo = ServerAction::<DeleteTodo>::new();\n\n    // list of todos is loaded from the server in reaction to changes\n    let todos = Resource::new(\n        move || {\n            (\n                delete_todo.version().get(),\n                add_todo.version().get(),\n                delete_todo.version().get(),\n            )\n        },\n        move |_| get_todos(),\n    );\n\n    let existing_todos = move || {\n        Suspend::new(async move {\n            todos\n                .await\n                .map(|todos| {\n                    if todos.is_empty() {\n                        Either::Left(view! { <p>\"No tasks were found.\"</p> })\n                    } else {\n                        Either::Right(\n                            todos\n                                .iter()\n                                .map(move |todo| {\n                                    let id = todo.id;\n                                    view! {\n                                        <li>\n                                            {todo.title.clone()} <ActionForm action=delete_todo>\n                                                <input type=\"hidden\" name=\"id\" value=id/>\n                                                <input type=\"submit\" value=\"X\"/>\n                                            </ActionForm>\n                                        </li>\n                                    }\n                                })\n                                .collect::<Vec<_>>(),\n                        )\n                    }\n                })\n        })\n    };\n\n    view! {\n        <MultiActionForm action=add_todo>\n            <label>\"Add a Todo\" <input type=\"text\" name=\"title\"/></label>\n            <input type=\"submit\" value=\"Add\"/>\n        </MultiActionForm>\n        <div>\n            <Transition fallback=move || view! { <p>\"Loading...\"</p> }>\n                // TODO: ErrorBoundary here seems to break Suspense in Actix\n                // <ErrorBoundary fallback=|errors| view! { <p>\"Error: \" {move || format!(\"{:?}\", errors.get())}</p> }>\n                <ul>\n                    {existing_todos}\n                    {move || {\n                        submissions\n                            .get()\n                            .into_iter()\n                            .filter(|submission| submission.pending().get())\n                            .map(|submission| {\n                                view! {\n                                    <li class=\"pending\">\n                                        {move || submission.input().get().map(|data| data.title)}\n                                    </li>\n                                }\n                            })\n                            .collect::<Vec<_>>()\n                    }}\n\n                </ul>\n            // </ErrorBoundary>\n            </Transition>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite/style.css",
    "content": ".pending {\n\tcolor: purple;\n}"
  },
  {
    "path": "examples/todo_app_sqlite_axum/Cargo.toml",
    "content": "[package]\nname = \"todo_app_sqlite_axum\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.30\"\nhttp = \"1.1\"\nleptos = { path = \"../../leptos\", features = [\"tracing\"] }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nlog = \"0.4.22\"\nsimple_logger = \"5.0\"\nserde = { version = \"1.0\", features = [\"derive\"] }\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nsqlx = { version = \"0.8.6\", features = [\n  \"runtime-tokio-rustls\",\n  \"sqlite\",\n], optional = true }\nthiserror = \"2.0.12\"\nwasm-bindgen = \"0.2.93\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"dep:sqlx\",\n  \"leptos/ssr\",\n  \"dep:leptos_axum\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"todo_app_sqlite_axum\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"cargo make test-ui\"\nend2end-dir = \"e2e\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos-webdriver-test.toml\" },\n]\n\n[env]\nCLIENT_PROCESS_NAME = \"todo_app_sqlite_axum\"\n\n[tasks.test-ui]\ncwd = \"./e2e\"\ncommand = \"cargo\"\nargs = [\"make\", \"test-ui\", \"${@}\"]\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/README.md",
    "content": "# Leptos Todo App Sqlite with Axum\n\nThis example creates a basic todo app with an Axum backend that uses Leptos' server functions to call sqlx from the client and seamlessly run it on the server.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## E2E Testing\n\nSee the [E2E README](./e2e/README.md) for more information about the testing strategy.\n\n## Rendering\n\nSee the [SSR Notes](../SSR_NOTES.md) for more information about Server Side Rendering.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/Cargo.toml",
    "content": "[package]\nname = \"todo_app_sqlite_axum_e2e\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\nanyhow = \"1.0\"\nasync-trait = \"0.1.81\"\ncucumber = \"0.21.1\"\nfantoccini = \"0.21.1\"\npretty_assertions = \"1.4\"\nserde_json = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\", \"time\"] }\nurl = \"2.5\"\n\n[[test]]\nname = \"app_suite\"\nharness = false    # Allow Cucumber to print output instead of libtest\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.test]\nenv = { RUN_AUTOMATICALLY = false }\ncondition = { env_true = [\"RUN_AUTOMATICALLY\"] }\n\n[tasks.ci]\n\n[tasks.test-ui]\ncommand = \"cargo\"\nargs = [\n  \"test\",\n  \"--test\",\n  \"app_suite\",\n  \"--\",\n  \"--retry\",\n  \"2\",\n  \"--retry-after\",\n  \"5sec\",\n  \"--fail-fast\",\n  \"${@}\",\n]\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/README.md",
    "content": "# E2E Testing\n\nThis example demonstrates e2e testing with Rust using executable requirements.\n\n## Testing Stack\n\n|    |      Role      |  Description |\n|---|---|---|\n| [Cucumber](https://github.com/cucumber-rs/cucumber/tree/main) | Test Runner | Run [Gherkin](https://cucumber.io/docs/gherkin/reference/) specifications as Rust tests |\n| [Fantoccini](https://github.com/jonhoo/fantoccini/tree/main) | Browser Client | Interact with web pages through WebDriver |\n| [Cargo Leptos ](https://github.com/leptos-rs/cargo-leptos) | Build Tool |  Compile example and start the server and end-2-end tests |\n| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome\n\n## Testing Organization\n\nTesting is organized around what a user can do and see/not see. Test scenarios are grouped by the **user action** and the **object** of that action. This makes it easier to locate and reason about requirements.\n\nHere is a brief overview of how things fit together.\n\n```bash\nfeatures\n└── {action}_{object}.feature   # Specify test scenarios\ntests\n├── fixtures\n│   ├── action.rs               # Perform a user action (click, type, etc.)\n│   ├── check.rs                # Assert what a user can see/not see\n│   ├── find.rs                 # Query page elements\n│   ├── mod.rs\n│   └── world\n│       ├── action_steps.rs     # Map Gherkin steps to user actions\n│       ├── check_steps.rs      # Map Gherkin steps to user expectations\n│       └── mod.rs\n└── app_suite.rs                # Test main \n```\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/features/add_todo.feature",
    "content": "@add_todo\nFeature: Add Todo\n\n    Background:\n        Given I see the app\n\n    @add_todo-see\n    Scenario: Should see the todo\n        Given I set the todo as Buy Bread\n        When I click the Add button\n        Then I see the todo named Buy Bread\n\n    @add_todo-style\n    Scenario: Should see the pending todo\n        When I add a todo as Buy Oranges\n        Then I see the pending todo\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/features/delete_todo.feature",
    "content": "@delete_todo\nFeature: Delete Todo\n\n    Background:\n        Given I see the app\n\n    @serial\n    @delete_todo-remove\n    Scenario: Should not see the deleted todo\n        Given I add a todo as Buy Yogurt\n        When I delete the todo named Buy Yogurt\n        Then I do not see the todo named Buy Yogurt\n\n    @serial\n    @delete_todo-message\n    Scenario: Should see the empty list message\n        When I empty the todo list\n        Then I see the empty list message is No tasks were found."
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/features/open_app.feature",
    "content": "@open_app\nFeature: Open App\n\n  @open_app-title\n  Scenario: Should see the home page title\n    When I open the app\n    Then I see the page title is My Tasks\n\n  @open_app-label\n  Scenario: Should see the input label\n    When I open the app\n    Then I see the label of the input is Add a Todo"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/tests/app_suite.rs",
    "content": "mod fixtures;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fixtures::world::AppWorld;\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    AppWorld::cucumber()\n        .fail_on_skipped()\n        .run_and_exit(\"./features\")\n        .await;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/tests/fixtures/action.rs",
    "content": "use super::{find, world::HOST};\nuse anyhow::Result;\nuse fantoccini::Client;\nuse std::result::Result::Ok;\nuse tokio::{self, time};\n\npub async fn goto_path(client: &Client, path: &str) -> Result<()> {\n    let url = format!(\"{}{}\", HOST, path);\n    client.goto(&url).await?;\n\n    Ok(())\n}\n\npub async fn add_todo(client: &Client, text: &str) -> Result<()> {\n    fill_todo(client, text).await?;\n    click_add_button(client).await?;\n    Ok(())\n}\n\npub async fn fill_todo(client: &Client, text: &str) -> Result<()> {\n    let textbox = find::todo_input(client).await;\n    textbox.send_keys(text).await?;\n\n    Ok(())\n}\n\npub async fn click_add_button(client: &Client) -> Result<()> {\n    let add_button = find::add_button(client).await;\n    add_button.click().await?;\n\n    Ok(())\n}\n\npub async fn empty_todo_list(client: &Client) -> Result<()> {\n    let todos = find::todos(client).await;\n\n    for _todo in todos {\n        let _ = delete_first_todo(client).await?;\n    }\n\n    Ok(())\n}\n\npub async fn delete_first_todo(client: &Client) -> Result<()> {\n    if let Some(element) = find::first_delete_button(client).await {\n        element.click().await.expect(\"Failed to delete todo\");\n        time::sleep(time::Duration::from_millis(250)).await;\n    }\n\n    Ok(())\n}\n\npub async fn delete_todo(client: &Client, text: &str) -> Result<()> {\n    if let Some(element) = find::delete_button(client, text).await {\n        element.click().await?;\n        time::sleep(time::Duration::from_millis(250)).await;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/tests/fixtures/check.rs",
    "content": "use super::find;\nuse anyhow::{Ok, Result};\nuse fantoccini::{Client, Locator};\nuse pretty_assertions::assert_eq;\n\npub async fn text_on_element(\n    client: &Client,\n    selector: &str,\n    expected_text: &str,\n) -> Result<()> {\n    let element = client\n        .wait()\n        .for_element(Locator::Css(selector))\n        .await\n        .expect(\n            format!(\"Element not found by Css selector `{}`\", selector)\n                .as_str(),\n        );\n\n    let actual = element.text().await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n\npub async fn todo_present(\n    client: &Client,\n    text: &str,\n    expected: bool,\n) -> Result<()> {\n    let todo_present = is_todo_present(client, text).await;\n\n    assert_eq!(todo_present, expected);\n\n    Ok(())\n}\n\nasync fn is_todo_present(client: &Client, text: &str) -> bool {\n    let todos = find::todos(client).await;\n\n    for todo in todos {\n        let todo_title = todo.text().await.expect(\"Todo title not found\");\n        if todo_title == text {\n            return true;\n        }\n    }\n\n    false\n}\n\npub async fn todo_is_pending(client: &Client) -> Result<()> {\n    if let None = find::pending_todo(client).await {\n        assert!(false, \"Pending todo not found\");\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/tests/fixtures/find.rs",
    "content": "use fantoccini::{elements::Element, Client, Locator};\n\npub async fn todo_input(client: &Client) -> Element {\n    let textbox = client\n        .wait()\n        .for_element(Locator::Css(\"input[name='title\"))\n        .await\n        .expect(\"Todo textbox not found\");\n\n    textbox\n}\n\npub async fn add_button(client: &Client) -> Element {\n    let button = client\n        .wait()\n        .for_element(Locator::Css(\"input[value='Add']\"))\n        .await\n        .expect(\"\");\n\n    button\n}\n\npub async fn first_delete_button(client: &Client) -> Option<Element> {\n    if let Ok(element) = client\n        .wait()\n        .for_element(Locator::Css(\"li:first-child input[value='X']\"))\n        .await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn delete_button(client: &Client, text: &str) -> Option<Element> {\n    let selector = format!(\"//*[text()='{text}']//input[@value='X']\");\n    if let Ok(element) =\n        client.wait().for_element(Locator::XPath(&selector)).await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn pending_todo(client: &Client) -> Option<Element> {\n    if let Ok(element) =\n        client.wait().for_element(Locator::Css(\".pending\")).await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn todos(client: &Client) -> Vec<Element> {\n    let todos = client\n        .find_all(Locator::Css(\"li\"))\n        .await\n        .expect(\"Todo List not found\");\n\n    todos\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/tests/fixtures/mod.rs",
    "content": "pub mod action;\npub mod check;\npub mod find;\npub mod world;\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/tests/fixtures/world/action_steps.rs",
    "content": "use crate::fixtures::{action, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::{given, when};\n\n#[given(\"I see the app\")]\n#[when(\"I open the app\")]\nasync fn i_open_the_app(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::goto_path(client, \"\").await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I add a todo as (.*)$\")]\n#[when(regex = \"^I add a todo as (.*)$\")]\nasync fn i_add_a_todo_titled(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::add_todo(client, text.as_str()).await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I set the todo as (.*)$\")]\nasync fn i_set_the_todo_as(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::fill_todo(client, &text).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"I click the Add button$\")]\nasync fn i_click_the_button(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::click_add_button(client).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"^I delete the todo named (.*)$\")]\nasync fn i_delete_the_todo_named(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    action::delete_todo(client, text.as_str()).await?;\n\n    Ok(())\n}\n\n#[given(\"the todo list is empty\")]\n#[when(\"I empty the todo list\")]\nasync fn i_empty_the_todo_list(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::empty_todo_list(client).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/tests/fixtures/world/check_steps.rs",
    "content": "use crate::fixtures::{check, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::then;\n\n#[then(regex = \"^I see the page title is (.*)$\")]\nasync fn i_see_the_page_title_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"h1\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the label of the input is (.*)$\")]\nasync fn i_see_the_label_of_the_input_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"label\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the todo named (.*)$\")]\nasync fn i_see_the_todo_is_present(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::todo_present(client, text.as_str(), true).await?;\n\n    Ok(())\n}\n\n#[then(\"I see the pending todo\")]\nasync fn i_see_the_pending_todo(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n\n    check::todo_is_pending(client).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the empty list message is (.*)$\")]\nasync fn i_see_the_empty_list_message_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"ul p\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I do not see the todo named (.*)$\")]\nasync fn i_do_not_see_the_todo_is_present(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::todo_present(client, text.as_str(), false).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/e2e/tests/fixtures/world/mod.rs",
    "content": "pub mod action_steps;\npub mod check_steps;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fantoccini::{\n    error::NewSessionError, wd::Capabilities, Client, ClientBuilder,\n};\n\npub const HOST: &str = \"http://127.0.0.1:3000\";\n\n#[derive(Debug, World)]\n#[world(init = Self::new)]\npub struct AppWorld {\n    pub client: Client,\n}\n\nimpl AppWorld {\n    async fn new() -> Result<Self, anyhow::Error> {\n        let webdriver_client = build_client().await?;\n\n        Ok(Self {\n            client: webdriver_client,\n        })\n    }\n}\n\nasync fn build_client() -> Result<Client, NewSessionError> {\n    let mut cap = Capabilities::new();\n    let arg = serde_json::from_str(\"{\\\"args\\\": [\\\"-headless\\\"]}\").unwrap();\n    cap.insert(\"goog:chromeOptions\".to_string(), arg);\n\n    let client = ClientBuilder::native()\n        .capabilities(cap)\n        .connect(\"http://localhost:4444\")\n        .await?;\n\n    Ok(client)\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/migrations/20221118172000_create_todo_table.sql",
    "content": "\nCREATE TABLE IF NOT EXISTS todos\n(\n  id          INTEGER NOT NULL PRIMARY KEY,\n  title       VARCHAR,\n  completed   BOOLEAN\n);"
  },
  {
    "path": "examples/todo_app_sqlite_axum/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/src/error_template.rs",
    "content": "use leptos::prelude::*;\n\n// A basic function to display errors served by the error boundaries. Feel free to do more complicated things\n// here than just displaying them\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional, into)] errors: Option<RwSignal<Errors>>,\n) -> impl IntoView {\n    let errors = match outside_errors {\n        Some(e) => RwSignal::new(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n\n    // Get Errors from Signal\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors =\n        move || errors.get().into_iter().map(|(_, v)| v).collect::<Vec<_>>();\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    /*#[cfg(feature = \"ssr\")]\n    {\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors[0].status_code());\n        }\n    }*/\n\n    view! {\n        <h1>\"Errors\"</h1>\n        {move || {\n            errors()\n                .into_iter()\n                .map(|error| {\n                    view! { <p>\"Error: \" {error.to_string()}</p> }\n                })\n                .collect::<Vec<_>>()\n        }}\n    }\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/src/errors.rs",
    "content": "use http::status::StatusCode;\nuse thiserror::Error;\n\n#[derive(Debug, Clone, Error)]\npub enum TodoAppError {\n    #[error(\"Not Found\")]\n    NotFound,\n    #[error(\"Internal Server Error\")]\n    InternalServerError,\n}\n\nimpl TodoAppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            TodoAppError::NotFound => StatusCode::NOT_FOUND,\n            TodoAppError::InternalServerError => {\n                StatusCode::INTERNAL_SERVER_ERROR\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/src/lib.rs",
    "content": "pub mod error_template;\npub mod errors;\npub mod todo;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::todo::TodoApp;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(TodoApp);\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\nuse axum::{\n    body::Body,\n    extract::Path,\n    http::Request,\n    response::{IntoResponse, Response},\n    routing::get,\n    Router,\n};\nuse leptos::prelude::*;\nuse todo_app_sqlite_axum::*;\n//Define a handler to test extractor with state\n#[cfg(feature = \"ssr\")]\nasync fn custom_handler(\n    Path(id): Path<String>,\n    req: Request<Body>,\n) -> Response {\n    let handler = leptos_axum::render_app_to_stream_with_context(\n        move || {\n            provide_context(id.clone());\n        },\n        todo::TodoApp,\n    );\n    handler(req).await.into_response()\n}\n\n#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use crate::todo::{ssr::db, *};\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n\n    simple_logger::init_with_level(log::Level::Error)\n        .expect(\"couldn't initialize logging\");\n\n    let mut conn = db().await.expect(\"couldn't connect to DB\");\n    if let Err(e) = sqlx::migrate!().run(&mut conn).await {\n        eprintln!(\"{e:?}\");\n    }\n\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(TodoApp);\n\n    // build our application with a route\n    let app = Router::new()\n        .route(\"/special/{id}\", get(custom_handler))\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    println!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    use leptos::mount::mount_to_body;\n\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(todo::TodoApp);\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/src/todo.rs",
    "content": "use crate::error_template::ErrorTemplate;\nuse leptos::{either::Either, prelude::*};\nuse serde::{Deserialize, Serialize};\nuse server_fn::ServerFnError;\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options/>\n                <link rel=\"stylesheet\" id=\"leptos\" href=\"/pkg/todo_app_sqlite_axum.css\"/>\n                <link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\"/>\n            </head>\n            <body>\n                <TodoApp/>\n            </body>\n        </html>\n    }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\n#[cfg_attr(feature = \"ssr\", derive(sqlx::FromRow))]\npub struct Todo {\n    id: u16,\n    title: String,\n    completed: bool,\n}\n\n#[cfg(feature = \"ssr\")]\npub mod ssr {\n    // use http::{header::SET_COOKIE, HeaderMap, HeaderValue, StatusCode};\n    use leptos::server_fn::ServerFnError;\n    use sqlx::{Connection, SqliteConnection};\n\n    pub async fn db() -> Result<SqliteConnection, ServerFnError> {\n        Ok(SqliteConnection::connect(\"sqlite:Todos.db\").await?)\n    }\n}\n\n#[server]\npub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {\n    use self::ssr::*;\n    use http::request::Parts;\n\n    // this is just an example of how to access server context injected in the handlers\n    let req_parts = use_context::<Parts>();\n\n    if let Some(req_parts) = req_parts {\n        println!(\"Uri = {:?}\", req_parts.uri);\n    }\n\n    use futures::TryStreamExt;\n\n    let mut conn = db().await?;\n\n    let mut todos = Vec::new();\n    let mut rows =\n        sqlx::query_as::<_, Todo>(\"SELECT * FROM todos\").fetch(&mut conn);\n    while let Some(row) = rows.try_next().await? {\n        todos.push(row);\n    }\n\n    // Lines below show how to set status code and headers on the response\n    // let resp = expect_context::<ResponseOptions>();\n    // resp.set_status(StatusCode::IM_A_TEAPOT);\n    // resp.insert_header(SET_COOKIE, HeaderValue::from_str(\"fizz=buzz\").unwrap());\n\n    Ok(todos)\n}\n\n#[server]\npub async fn add_todo(title: String) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n    let mut conn = db().await?;\n\n    // fake API delay\n    std::thread::sleep(std::time::Duration::from_millis(250));\n\n    match sqlx::query(\"INSERT INTO todos (title, completed) VALUES ($1, false)\")\n        .bind(title)\n        .execute(&mut conn)\n        .await\n    {\n        Ok(_row) => Ok(()),\n        Err(e) => Err(ServerFnError::ServerError(e.to_string())),\n    }\n}\n\n#[server]\npub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n    let mut conn = db().await?;\n\n    Ok(sqlx::query(\"DELETE FROM todos WHERE id = $1\")\n        .bind(id)\n        .execute(&mut conn)\n        .await\n        .map(|_| ())?)\n}\n\n#[component]\npub fn TodoApp() -> impl IntoView {\n    view! {\n        <header>\n            <h1>\"My Tasks\"</h1>\n        </header>\n        <main>\n            <Todos/>\n        </main>\n    }\n}\n\n#[component]\npub fn Todos() -> impl IntoView {\n    let add_todo = ServerMultiAction::<AddTodo>::new();\n    let submissions = add_todo.submissions();\n    let delete_todo = ServerAction::<DeleteTodo>::new();\n\n    // list of todos is loaded from the server in reaction to changes\n    let todos = Resource::new(\n        move || {\n            (\n                delete_todo.version().get(),\n                add_todo.version().get(),\n                delete_todo.version().get(),\n            )\n        },\n        move |_| get_todos(),\n    );\n\n    let existing_todos = move || {\n        Suspend::new(async move {\n            todos\n                .await\n                .map(|todos| {\n                    if todos.is_empty() {\n                        Either::Left(view! { <p>\"No tasks were found.\"</p> })\n                    } else {\n                        Either::Right(\n                            todos\n                                .iter()\n                                .map(move |todo| {\n                                    let id = todo.id;\n                                    view! {\n                                        <li>\n                                            {todo.title.clone()}\n                                            <ActionForm action=delete_todo>\n                                                <input type=\"hidden\" name=\"id\" value=id/>\n                                                <input type=\"submit\" value=\"X\"/>\n                                            </ActionForm>\n                                        </li>\n                                    }\n                                })\n                                .collect::<Vec<_>>(),\n                        )\n                    }\n                })\n        })\n    };\n\n    view! {\n        <MultiActionForm action=add_todo>\n            <label>\"Add a Todo\" <input type=\"text\" name=\"title\"/></label>\n            <input type=\"submit\" value=\"Add\"/>\n        </MultiActionForm>\n        <div>\n            <Transition fallback=move || view! { <p>\"Loading...\"</p> }>\n                <ErrorBoundary fallback=|errors| view! { <ErrorTemplate errors/> }>\n                    <ul>\n                        {existing_todos}\n                        {move || {\n                            submissions\n                                .get()\n                                .into_iter()\n                                .filter(|submission| submission.pending().get())\n                                .map(|submission| {\n                                    view! {\n                                        <li class=\"pending\">\n                                            {move || submission.input().get().map(|data| data.title)}\n                                        </li>\n                                    }\n                                })\n                                .collect::<Vec<_>>()\n                        }}\n\n                    </ul>\n                </ErrorBoundary>\n            </Transition>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_axum/style.css",
    "content": ".pending {\n\tcolor: purple;\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/Cargo.toml",
    "content": "[package]\nname = \"todo_app_sqlite_csr\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.30\"\nleptos = { path = \"../../leptos\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_meta = { path = \"../../meta\" }\nleptos_router = { path = \"../../router\" }\nleptos_integration_utils = { path = \"../../integrations/utils\", optional = true }\nserde = { version = \"1.0\", features = [\"derive\"] }\naxum = { version = \"0.8.1\", optional = true }\ntower = { version = \"0.5.1\", features = [\"util\"], optional = true }\ntower-http = { version = \"0.6.1\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nhttp = { version = \"1.1\" }\nsqlx = { version = \"0.8.6\", features = [\n  \"runtime-tokio-rustls\",\n  \"sqlite\",\n], optional = true }\nthiserror = \"2.0.12\"\nwasm-bindgen = \"0.2.93\"\n\n[features]\ncsr = [\"leptos/csr\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"dep:sqlx\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:leptos_axum\",\n  \"dep:leptos_integration_utils\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"todo_app_sqlite_csr\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"cargo make test-ui\"\nend2end-dir = \"e2e\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"csr\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos-webdriver-test.toml\" },\n]\n\n[env]\nCLIENT_PROCESS_NAME = \"todo_app_sqlite_csr\"\n\n[tasks.test-ui]\ncwd = \"./e2e\"\ncommand = \"cargo\"\nargs = [\"make\", \"test-ui\", \"${@}\"]\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/README.md",
    "content": "# Leptos Todo App Sqlite with CSR\n\nThis example shows how to combine client-side rendering with server functions, i.e., using server functions as a convenient way to create an ad hoc API, but without using server-side rendering and hydration.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## E2E Testing\n\nSee the [E2E README](./e2e/README.md) for more information about the testing strategy.\n\n## Rendering\n\nSee the [SSR Notes](../SSR_NOTES.md) for more information about Server Side Rendering.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/Cargo.toml",
    "content": "[package]\nname = \"todo_app_sqlite_csr_e2e\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\nanyhow = \"1.0\"\nasync-trait = \"0.1.81\"\ncucumber = \"0.21.1\"\nfantoccini = \"0.21.1\"\npretty_assertions = \"1.4\"\nserde_json = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\", \"time\"] }\nurl = \"2.5\"\n\n[[test]]\nname = \"app_suite\"\nharness = false    # Allow Cucumber to print output instead of libtest\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.test]\nenv = { RUN_AUTOMATICALLY = false }\ncondition = { env_true = [\"RUN_AUTOMATICALLY\"] }\n\n[tasks.ci]\n\n[tasks.test-ui]\ncommand = \"cargo\"\nargs = [\n  \"test\",\n  \"--test\",\n  \"app_suite\",\n  \"--\",\n  \"--retry\",\n  \"5\",\n  \"--retry-after\",\n  \"5sec\",\n  \"--fail-fast\",\n  \"${@}\",\n]\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/README.md",
    "content": "# E2E Testing\n\nThis example demonstrates e2e testing with Rust using executable requirements.\n\n## Testing Stack\n\n|    |      Role      |  Description |\n|---|---|---|\n| [Cucumber](https://github.com/cucumber-rs/cucumber/tree/main) | Test Runner | Run [Gherkin](https://cucumber.io/docs/gherkin/reference/) specifications as Rust tests |\n| [Fantoccini](https://github.com/jonhoo/fantoccini/tree/main) | Browser Client | Interact with web pages through WebDriver |\n| [Cargo Leptos ](https://github.com/leptos-rs/cargo-leptos) | Build Tool |  Compile example and start the server and end-2-end tests |\n| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome\n\n## Testing Organization\n\nTesting is organized around what a user can do and see/not see. Test scenarios are grouped by the **user action** and the **object** of that action. This makes it easier to locate and reason about requirements.\n\nHere is a brief overview of how things fit together.\n\n```bash\nfeatures\n└── {action}_{object}.feature   # Specify test scenarios\ntests\n├── fixtures\n│   ├── action.rs               # Perform a user action (click, type, etc.)\n│   ├── check.rs                # Assert what a user can see/not see\n│   ├── find.rs                 # Query page elements\n│   ├── mod.rs\n│   └── world\n│       ├── action_steps.rs     # Map Gherkin steps to user actions\n│       ├── check_steps.rs      # Map Gherkin steps to user expectations\n│       └── mod.rs\n└── app_suite.rs                # Test main \n```\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/features/add_todo.feature",
    "content": "@add_todo\nFeature: Add Todo\n\n    Background:\n        Given I see the app\n\n    @add_todo-see\n    Scenario: Should see the todo\n        Given I set the todo as Buy Bread\n        When I click the Add button\n        Then I see the todo named Buy Bread\n\n    @add_todo-style\n    Scenario: Should see the pending todo\n        When I add a todo as Buy Oranges\n        Then I see the pending todo\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/features/delete_todo.feature",
    "content": "@delete_todo\nFeature: Delete Todo\n\n    Background:\n        Given I see the app\n\n    @serial\n    @delete_todo-remove\n    Scenario: Should not see the deleted todo\n        Given I add a todo as Buy Yogurt\n        When I delete the todo named Buy Yogurt\n        Then I do not see the todo named Buy Yogurt\n\n    @serial\n    @delete_todo-message\n    Scenario: Should see the empty list message\n        When I empty the todo list\n        Then I see the empty list message is No tasks were found."
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/features/open_app.feature",
    "content": "@open_app\nFeature: Open App\n\n  @open_app-title\n  Scenario: Should see the home page title\n    When I open the app\n    Then I see the page title is My Tasks\n\n  @open_app-label\n  Scenario: Should see the input label\n    When I open the app\n    Then I see the label of the input is Add a Todo"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/tests/app_suite.rs",
    "content": "mod fixtures;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fixtures::world::AppWorld;\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    AppWorld::cucumber()\n        .fail_on_skipped()\n        .run_and_exit(\"./features\")\n        .await;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/tests/fixtures/action.rs",
    "content": "use super::{find, world::HOST};\nuse anyhow::Result;\nuse fantoccini::Client;\nuse std::result::Result::Ok;\nuse tokio::{self, time};\n\npub async fn goto_path(client: &Client, path: &str) -> Result<()> {\n    let url = format!(\"{}{}\", HOST, path);\n    client.goto(&url).await?;\n\n    Ok(())\n}\n\npub async fn add_todo(client: &Client, text: &str) -> Result<()> {\n    fill_todo(client, text).await?;\n    click_add_button(client).await?;\n    Ok(())\n}\n\npub async fn fill_todo(client: &Client, text: &str) -> Result<()> {\n    let textbox = find::todo_input(client).await;\n    textbox.send_keys(text).await?;\n\n    Ok(())\n}\n\npub async fn click_add_button(client: &Client) -> Result<()> {\n    let add_button = find::add_button(client).await;\n    add_button.click().await?;\n\n    Ok(())\n}\n\npub async fn empty_todo_list(client: &Client) -> Result<()> {\n    let todos = find::todos(client).await;\n\n    for _todo in todos {\n        let _ = delete_first_todo(client).await?;\n    }\n\n    Ok(())\n}\n\npub async fn delete_first_todo(client: &Client) -> Result<()> {\n    if let Some(element) = find::first_delete_button(client).await {\n        element.click().await.expect(\"Failed to delete todo\");\n        time::sleep(time::Duration::from_millis(250)).await;\n    }\n\n    Ok(())\n}\n\npub async fn delete_todo(client: &Client, text: &str) -> Result<()> {\n    if let Some(element) = find::delete_button(client, text).await {\n        element.click().await?;\n        time::sleep(time::Duration::from_millis(250)).await;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/tests/fixtures/check.rs",
    "content": "use super::find;\nuse anyhow::{Ok, Result};\nuse fantoccini::{Client, Locator};\nuse pretty_assertions::assert_eq;\n\npub async fn text_on_element(\n    client: &Client,\n    selector: &str,\n    expected_text: &str,\n) -> Result<()> {\n    let element = client\n        .wait()\n        .for_element(Locator::Css(selector))\n        .await\n        .expect(\n            format!(\"Element not found by Css selector `{}`\", selector)\n                .as_str(),\n        );\n\n    let actual = element.text().await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n\npub async fn todo_present(\n    client: &Client,\n    text: &str,\n    expected: bool,\n) -> Result<()> {\n    let todo_present = is_todo_present(client, text).await;\n\n    assert_eq!(todo_present, expected);\n\n    Ok(())\n}\n\nasync fn is_todo_present(client: &Client, text: &str) -> bool {\n    let todos = find::todos(client).await;\n\n    for todo in todos {\n        let todo_title = todo.text().await.expect(\"Todo title not found\");\n        if todo_title == text {\n            return true;\n        }\n    }\n\n    false\n}\n\npub async fn todo_is_pending(client: &Client) -> Result<()> {\n    if let None = find::pending_todo(client).await {\n        assert!(false, \"Pending todo not found\");\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/tests/fixtures/find.rs",
    "content": "use fantoccini::{elements::Element, Client, Locator};\n\npub async fn todo_input(client: &Client) -> Element {\n    let textbox = client\n        .wait()\n        .for_element(Locator::Css(\"input[name='title\"))\n        .await\n        .expect(\"Todo textbox not found\");\n\n    textbox\n}\n\npub async fn add_button(client: &Client) -> Element {\n    let button = client\n        .wait()\n        .for_element(Locator::Css(\"input[value='Add']\"))\n        .await\n        .expect(\"\");\n\n    button\n}\n\npub async fn first_delete_button(client: &Client) -> Option<Element> {\n    if let Ok(element) = client\n        .wait()\n        .for_element(Locator::Css(\"li:first-child input[value='X']\"))\n        .await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn delete_button(client: &Client, text: &str) -> Option<Element> {\n    let selector = format!(\"//*[text()='{text}']//input[@value='X']\");\n    if let Ok(element) =\n        client.wait().for_element(Locator::XPath(&selector)).await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn pending_todo(client: &Client) -> Option<Element> {\n    if let Ok(element) =\n        client.wait().for_element(Locator::Css(\".pending\")).await\n    {\n        return Some(element);\n    }\n\n    None\n}\n\npub async fn todos(client: &Client) -> Vec<Element> {\n    let todos = client\n        .find_all(Locator::Css(\"li\"))\n        .await\n        .expect(\"Todo List not found\");\n\n    todos\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/tests/fixtures/mod.rs",
    "content": "pub mod action;\npub mod check;\npub mod find;\npub mod world;\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/tests/fixtures/world/action_steps.rs",
    "content": "use crate::fixtures::{action, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::{given, when};\n\n#[given(\"I see the app\")]\n#[when(\"I open the app\")]\nasync fn i_open_the_app(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::goto_path(client, \"\").await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I add a todo as (.*)$\")]\n#[when(regex = \"^I add a todo as (.*)$\")]\nasync fn i_add_a_todo_titled(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::add_todo(client, text.as_str()).await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I set the todo as (.*)$\")]\nasync fn i_set_the_todo_as(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::fill_todo(client, &text).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"I click the Add button$\")]\nasync fn i_click_the_button(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::click_add_button(client).await?;\n\n    Ok(())\n}\n\n#[when(regex = \"^I delete the todo named (.*)$\")]\nasync fn i_delete_the_todo_named(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    action::delete_todo(client, text.as_str()).await?;\n\n    Ok(())\n}\n\n#[given(\"the todo list is empty\")]\n#[when(\"I empty the todo list\")]\nasync fn i_empty_the_todo_list(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::empty_todo_list(client).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/tests/fixtures/world/check_steps.rs",
    "content": "use crate::fixtures::{check, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::then;\n\n#[then(regex = \"^I see the page title is (.*)$\")]\nasync fn i_see_the_page_title_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"h1\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the label of the input is (.*)$\")]\nasync fn i_see_the_label_of_the_input_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"label\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the todo named (.*)$\")]\nasync fn i_see_the_todo_is_present(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::todo_present(client, text.as_str(), true).await?;\n\n    Ok(())\n}\n\n#[then(\"I see the pending todo\")]\nasync fn i_see_the_pending_todo(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n\n    check::todo_is_pending(client).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the empty list message is (.*)$\")]\nasync fn i_see_the_empty_list_message_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"ul p\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I do not see the todo named (.*)$\")]\nasync fn i_do_not_see_the_todo_is_present(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::todo_present(client, text.as_str(), false).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/e2e/tests/fixtures/world/mod.rs",
    "content": "pub mod action_steps;\npub mod check_steps;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fantoccini::{\n    error::NewSessionError, wd::Capabilities, Client, ClientBuilder,\n};\n\npub const HOST: &str = \"http://127.0.0.1:3000\";\n\n#[derive(Debug, World)]\n#[world(init = Self::new)]\npub struct AppWorld {\n    pub client: Client,\n}\n\nimpl AppWorld {\n    async fn new() -> Result<Self, anyhow::Error> {\n        let webdriver_client = build_client().await?;\n\n        Ok(Self {\n            client: webdriver_client,\n        })\n    }\n}\n\nasync fn build_client() -> Result<Client, NewSessionError> {\n    let mut cap = Capabilities::new();\n    let arg = serde_json::from_str(\"{\\\"args\\\": [\\\"-headless\\\"]}\").unwrap();\n    cap.insert(\"goog:chromeOptions\".to_string(), arg);\n\n    let client = ClientBuilder::native()\n        .capabilities(cap)\n        .connect(\"http://localhost:4444\")\n        .await?;\n\n    Ok(client)\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/migrations/20221118172000_create_todo_table.sql",
    "content": "\nCREATE TABLE IF NOT EXISTS todos\n(\n  id          INTEGER NOT NULL PRIMARY KEY,\n  title       VARCHAR,\n  completed   BOOLEAN\n);"
  },
  {
    "path": "examples/todo_app_sqlite_csr/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/src/error_template.rs",
    "content": "use crate::errors::TodoAppError;\nuse leptos::prelude::*;\n#[cfg(feature = \"ssr\")]\nuse leptos_axum::ResponseOptions;\n\n// A basic function to display errors served by the error boundaries. Feel free to do more complicated things\n// here than just displaying them\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional, into)] errors: Option<RwSignal<Errors>>,\n) -> impl IntoView {\n    let errors = match outside_errors {\n        Some(e) => RwSignal::new(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n\n    // Get Errors from Signal\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors: Vec<TodoAppError> = errors\n        .get()\n        .into_iter()\n        .filter_map(|(_, v)| v.downcast_ref::<TodoAppError>().cloned())\n        .collect();\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    #[cfg(feature = \"ssr\")]\n    {\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors[0].status_code());\n        }\n    }\n\n    view! {\n      <h1>\"Errors\"</h1>\n      <For\n        // a function that returns the items we're iterating over; a signal is fine\n        each= move || {errors.clone().into_iter().enumerate()}\n        // a unique key for each item as a reference\n        key=|(index, _error)| *index\n        // renders each item to a view\n        children=move |error| {\n        let error_string = error.1.to_string();\n        let error_code= error.1.status_code();\n          view! {\n\n            <h2>{error_code.to_string()}</h2>\n            <p>\"Error: \" {error_string}</p>\n          }\n        }\n      />\n    }\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/src/errors.rs",
    "content": "use http::status::StatusCode;\nuse thiserror::Error;\n\n#[derive(Debug, Clone, Error)]\npub enum TodoAppError {\n    #[error(\"Not Found\")]\n    NotFound,\n    #[error(\"Internal Server Error\")]\n    InternalServerError,\n}\n\nimpl TodoAppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            TodoAppError::NotFound => StatusCode::NOT_FOUND,\n            TodoAppError::InternalServerError => {\n                StatusCode::INTERNAL_SERVER_ERROR\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/src/fallback.rs",
    "content": "use axum::{\n    body::Body,\n    extract::State,\n    http::{Request, Response, StatusCode, Uri},\n    response::{Html, IntoResponse, Response as AxumResponse},\n};\nuse leptos::{\n    config::LeptosOptions,\n    hydration::{AutoReload, HydrationScripts},\n    prelude::*,\n};\nuse tower::util::ServiceExt;\nuse tower_http::services::ServeDir;\n\npub async fn file_or_index_handler(\n    uri: Uri,\n    State(options): State<LeptosOptions>,\n) -> AxumResponse {\n    let root = options.site_root.clone();\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        Html(view! {\n            <!DOCTYPE html>\n            <html lang=\"en\">\n                <head>\n                    <meta charset=\"utf-8\"/>\n                    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                    <AutoReload options=options.clone() />\n                    <HydrationScripts options=options.clone()/>\n                    <link rel=\"stylesheet\" id=\"leptos\" href=\"/pkg/todo_app_sqlite_csr.css\"/>\n                    <link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\"/>\n                </head>\n                <body></body>\n            </html>\n        }.to_html()).into_response()\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/src/lib.rs",
    "content": "pub mod error_template;\npub mod errors;\n#[cfg(feature = \"ssr\")]\npub mod fallback;\npub mod todo;\n\n#[cfg_attr(feature = \"csr\", wasm_bindgen::prelude::wasm_bindgen)]\npub fn hydrate() {\n    use crate::todo::*;\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(TodoApp);\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[allow(unused)]\nmod ssr_imports {\n    pub use axum::{\n        body::Body as AxumBody,\n        extract::{Path, State},\n        http::Request,\n        response::{Html, IntoResponse, Response},\n        routing::{get, post},\n        Router,\n    };\n    pub use leptos::prelude::*;\n    pub use leptos_axum::{generate_route_list, LeptosRoutes};\n    pub use todo_app_sqlite_csr::{\n        fallback::file_or_index_handler, todo::*, *,\n    };\n}\n\n#[cfg(feature = \"ssr\")]\n#[cfg_attr(feature = \"ssr\", tokio::main)]\nasync fn main() {\n    use ssr_imports::*;\n\n    let _conn = ssr::db().await.expect(\"couldn't connect to DB\");\n\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n\n    // build our application with a route\n    let app = Router::new()\n        // server function handlers are normally set up by .leptos_routes()\n        // here, we're not actually doing server side rendering, so we set up a manual\n        // handler for the server fns\n        // this should include a get() handler if you have any GetUrl-based server fns\n        .route(\"/api/{*fn_name}\", post(leptos_axum::handle_server_fns))\n        .fallback(file_or_index_handler)\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    leptos::logging::log!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr)\n        .await\n        .expect(\"couldn't bind to address\");\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // This example cannot be built as a trunk standalone CSR-only app.\n    // Only the server may directly connect to the database.\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/src/todo.rs",
    "content": "use crate::error_template::ErrorTemplate;\nuse leptos::{either::Either, prelude::*};\nuse serde::{Deserialize, Serialize};\nuse server_fn::ServerFnError;\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\n#[cfg_attr(feature = \"ssr\", derive(sqlx::FromRow))]\npub struct Todo {\n    id: u16,\n    title: String,\n    completed: bool,\n}\n\n#[cfg(feature = \"ssr\")]\npub mod ssr {\n    // use http::{header::SET_COOKIE, HeaderMap, HeaderValue, StatusCode};\n    use leptos::server_fn::ServerFnError;\n    use sqlx::{Connection, SqliteConnection};\n\n    pub async fn db() -> Result<SqliteConnection, ServerFnError> {\n        Ok(SqliteConnection::connect(\"sqlite:Todos.db\").await?)\n    }\n}\n\n#[server]\npub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {\n    use self::ssr::*;\n    use http::request::Parts;\n\n    // this is just an example of how to access server context injected in the handlers\n    let req_parts = use_context::<Parts>();\n\n    if let Some(req_parts) = req_parts {\n        println!(\"Uri = {:?}\", req_parts.uri);\n    }\n\n    use futures::TryStreamExt;\n\n    let mut conn = db().await?;\n\n    let mut todos = Vec::new();\n    let mut rows =\n        sqlx::query_as::<_, Todo>(\"SELECT * FROM todos\").fetch(&mut conn);\n    while let Some(row) = rows.try_next().await? {\n        todos.push(row);\n    }\n\n    // Lines below show how to set status code and headers on the response\n    // let resp = expect_context::<ResponseOptions>();\n    // resp.set_status(StatusCode::IM_A_TEAPOT);\n    // resp.insert_header(SET_COOKIE, HeaderValue::from_str(\"fizz=buzz\").unwrap());\n\n    Ok(todos)\n}\n\n#[server]\npub async fn add_todo(title: String) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n    let mut conn = db().await?;\n\n    // fake API delay\n    std::thread::sleep(std::time::Duration::from_millis(250));\n\n    match sqlx::query(\"INSERT INTO todos (title, completed) VALUES ($1, false)\")\n        .bind(title)\n        .execute(&mut conn)\n        .await\n    {\n        Ok(_row) => Ok(()),\n        Err(e) => Err(ServerFnError::ServerError(e.to_string())),\n    }\n}\n\n#[server]\npub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n    let mut conn = db().await?;\n\n    Ok(sqlx::query(\"DELETE FROM todos WHERE id = $1\")\n        .bind(id)\n        .execute(&mut conn)\n        .await\n        .map(|_| ())?)\n}\n\n#[component]\npub fn TodoApp() -> impl IntoView {\n    view! {\n        <header>\n            <h1>\"My Tasks\"</h1>\n        </header>\n        <main>\n            <Todos/>\n        </main>\n    }\n}\n\n#[component]\npub fn Todos() -> impl IntoView {\n    let add_todo = ServerMultiAction::<AddTodo>::new();\n    let submissions = add_todo.submissions();\n    let delete_todo = ServerAction::<DeleteTodo>::new();\n\n    // list of todos is loaded from the server in reaction to changes\n    let todos = Resource::new(\n        move || {\n            (\n                delete_todo.version().get(),\n                add_todo.version().get(),\n                delete_todo.version().get(),\n            )\n        },\n        move |_| get_todos(),\n    );\n\n    let existing_todos = move || {\n        Suspend::new(async move {\n            todos\n                .await\n                .map(|todos| {\n                    if todos.is_empty() {\n                        Either::Left(view! { <p>\"No tasks were found.\"</p> })\n                    } else {\n                        Either::Right(\n                            todos\n                                .iter()\n                                .map(move |todo| {\n                                    let id = todo.id;\n                                    view! {\n                                        <li>\n                                            {todo.title.clone()}\n                                            <ActionForm action=delete_todo>\n                                                <input type=\"hidden\" name=\"id\" value=id/>\n                                                <input type=\"submit\" value=\"X\"/>\n                                            </ActionForm>\n                                        </li>\n                                    }\n                                })\n                                .collect::<Vec<_>>(),\n                        )\n                    }\n                })\n        })\n    };\n\n    view! {\n        <MultiActionForm action=add_todo>\n            <label>\"Add a Todo\" <input type=\"text\" name=\"title\"/></label>\n            <input type=\"submit\" value=\"Add\"/>\n        </MultiActionForm>\n        <div>\n            <Transition fallback=move || view! { <p>\"Loading...\"</p> }>\n                <ErrorBoundary fallback=|errors| view! { <ErrorTemplate errors/> }>\n                    <ul>\n                        {existing_todos}\n                        {move || {\n                            submissions\n                                .get()\n                                .into_iter()\n                                .filter(|submission| submission.pending().get())\n                                .map(|submission| {\n                                    view! {\n                                        <li class=\"pending\">\n                                            {move || submission.input().get().map(|data| data.title)}\n                                        </li>\n                                    }\n                                })\n                                .collect::<Vec<_>>()\n                        }}\n\n                    </ul>\n                </ErrorBoundary>\n            </Transition>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/todo_app_sqlite_csr/style.css",
    "content": ".pending {\n\tcolor: purple;\n}\n"
  },
  {
    "path": "examples/todomvc/Cargo.toml",
    "content": "[package]\nname = \"todomvc\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_error_panic_hook = \"0.1.7\"\nuuid = { version = \"1.10\", features = [\"v4\", \"js\", \"serde\"] }\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\nweb-sys = { version = \"0.3.70\", features = [\"Storage\"] }\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.42\"\n\n[package.metadata.cargo-all-features]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n"
  },
  {
    "path": "examples/todomvc/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/trunk_server.toml\" },\n]\n\n[tasks.setup-node]\nenv = { SETUP_NODE = false }\ncondition = { env_true = [\"SETUP_NODE\"] }\n"
  },
  {
    "path": "examples/todomvc/README.md",
    "content": "# Leptos TodoMVC\n\nThis is a Leptos implementation of the TodoMVC example common to many frameworks. This is a relatively-simple application but shows off features like interaction between components and state management.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `trunk serve --open` to run this example.\n"
  },
  {
    "path": "examples/todomvc/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t\t<link data-trunk rel=\"css\" href=\"./node_modules/todomvc-common/base.css\">\n\t\t<link data-trunk rel=\"css\" href=\"./node_modules/todomvc-app-css/index.css\">\n\t\t<title>Leptos • TodoMVC</title>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "examples/todomvc/package.json",
    "content": "{\n  \"name\": \"todomvc\",\n  \"version\": \"0.1.0\",\n  \"description\": \"TodoMVC implemented in the Leptos framework for Rust\",\n  \"author\": \"Greg Johnston <greg.johnston@gmail.com>\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"todomvc-app-css\": \"^2.4.2\",\n    \"todomvc-common\": \"^1.0.5\"\n  }\n}\n"
  },
  {
    "path": "examples/todomvc/rust-toolchain.toml",
    "content": "[toolchain]\ntargets = [\"wasm32-unknown-unknown\"]\n"
  },
  {
    "path": "examples/todomvc/src/lib.rs",
    "content": "use leptos::{ev, html::Input, prelude::*};\nuse serde::{Deserialize, Serialize};\nuse uuid::Uuid;\nuse web_sys::KeyboardEvent;\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct Todos(pub Vec<Todo>);\n\nconst STORAGE_KEY: &str = \"todos-leptos\";\n\nimpl Default for Todos {\n    fn default() -> Self {\n        let starting_todos =\n            window()\n                .local_storage()\n                .ok()\n                .flatten()\n                .and_then(|storage| {\n                    storage.get_item(STORAGE_KEY).ok().flatten().and_then(\n                        |value| serde_json::from_str::<Vec<Todo>>(&value).ok(),\n                    )\n                })\n                .unwrap_or_default();\n        Self(starting_todos)\n    }\n}\n\n// Basic operations to manipulate the todo list: nothing really interesting here\nimpl Todos {\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    pub fn add(&mut self, todo: Todo) {\n        self.0.push(todo);\n    }\n\n    pub fn remove(&mut self, id: Uuid) {\n        self.retain(|todo| todo.id != id);\n    }\n\n    pub fn remaining(&self) -> usize {\n        // `todo.completed` is a signal, so we call .get() to access its value\n        self.0.iter().filter(|todo| !todo.completed.get()).count()\n    }\n\n    pub fn completed(&self) -> usize {\n        // `todo.completed` is a signal, so we call .get() to access its value\n        self.0.iter().filter(|todo| todo.completed.get()).count()\n    }\n\n    pub fn toggle_all(&self) {\n        // if all are complete, mark them all active\n        if self.remaining() == 0 {\n            for todo in &self.0 {\n                todo.completed.update(|completed| {\n                    if *completed {\n                        *completed = false\n                    }\n                });\n            }\n        }\n        // otherwise, mark them all complete\n        else {\n            for todo in &self.0 {\n                todo.completed.set(true);\n            }\n        }\n    }\n\n    fn clear_completed(&mut self) {\n        self.retain(|todo| !todo.completed.get());\n    }\n\n    fn retain(&mut self, mut f: impl FnMut(&Todo) -> bool) {\n        self.0.retain(|todo| {\n            let retain = f(todo);\n            // because these signals are created at the top level,\n            // they are owned by the <TodoMVC/> component and not\n            // by the individual <Todo/> components. This means\n            // that if they are not manually disposed when removed, they\n            // will be held onto until the <TodoMVC/> is unmounted.\n            if !retain {\n                todo.title.dispose();\n                todo.completed.dispose();\n            }\n            retain\n        })\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]\npub struct Todo {\n    pub id: Uuid,\n    pub title: RwSignal<String>,\n    pub completed: RwSignal<bool>,\n}\n\nimpl Todo {\n    pub fn new(id: Uuid, title: String) -> Self {\n        Self::new_with_completed(id, title, false)\n    }\n\n    pub fn new_with_completed(\n        id: Uuid,\n        title: String,\n        completed: bool,\n    ) -> Self {\n        // RwSignal combines the getter and setter in one struct, rather than separating\n        // the getter from the setter. This makes it more convenient in some cases, such\n        // as when we're putting the signals into a struct and passing it around.\n        let title = RwSignal::new(title);\n        let completed = RwSignal::new(completed);\n        Self {\n            id,\n            title,\n            completed,\n        }\n    }\n\n    pub fn toggle(&self) {\n        // A signal's `update()` function gives you a mutable reference to the current value\n        // You can use that to modify the value in place, which will notify any subscribers.\n        self.completed.update(|completed| *completed = !*completed);\n    }\n}\n\nconst ESCAPE_KEY: u32 = 27;\nconst ENTER_KEY: u32 = 13;\n\n#[component]\npub fn TodoMVC() -> impl IntoView {\n    // The `todos` are a signal, since we need to reactively update the list\n    let (todos, set_todos) = signal(Todos::default());\n\n    // We provide a context that each <Todo/> component can use to update the list\n    // Here, I'm just passing the `WriteSignal`; a <Todo/> doesn't need to read the whole list\n    // (and shouldn't try to, as that would cause each individual <Todo/> to re-render when\n    // a new todo is added! This kind of hygiene is why `signal` defaults to read-write\n    // segregation.)\n    provide_context(set_todos);\n\n    // Handle the three filter modes: All, Active, and Completed\n    let (mode, set_mode) = signal(Mode::All);\n\n    window_event_listener(ev::hashchange, move |_| {\n        let new_mode =\n            location_hash().map(|hash| route(&hash)).unwrap_or_default();\n        set_mode.set(new_mode);\n    });\n\n    // Callback to add a todo on pressing the `Enter` key, if the field isn't empty\n    let input_ref = NodeRef::<Input>::new();\n    let add_todo = move |ev: KeyboardEvent| {\n        let input = input_ref.get().unwrap();\n        ev.stop_propagation();\n        let key_code = ev.key_code();\n        if key_code == ENTER_KEY {\n            let title = input.value();\n            let title = title.trim();\n            if !title.is_empty() {\n                let new = Todo::new(Uuid::new_v4(), title.to_string());\n                set_todos.update(|t| t.add(new));\n                input.set_value(\"\");\n            }\n        }\n    };\n\n    // A derived signal that filters the list of the todos depending on the filter mode\n    // This doesn't need to be a `Memo`, because we're only reading it in one place\n    let filtered_todos = move || {\n        todos.with(|todos| match mode.get() {\n            Mode::All => todos.0.to_vec(),\n            Mode::Active => todos\n                .0\n                .iter()\n                .filter(|todo| !todo.completed.get())\n                .cloned()\n                .collect(),\n            Mode::Completed => todos\n                .0\n                .iter()\n                .filter(|todo| todo.completed.get())\n                .cloned()\n                .collect(),\n        })\n    };\n\n    // Serialization\n    //\n    // the effect reads the `todos` signal, and each `Todo`'s title and completed\n    // status,  so it will automatically re-run on any change to the list of tasks\n    //\n    // this is the main point of effects: to synchronize reactive state\n    // with something outside the reactive system (like localStorage)\n\n    Effect::new(move |_| {\n        if let Ok(Some(storage)) = window().local_storage() {\n            let json = serde_json::to_string(&todos)\n                .expect(\"couldn't serialize Todos\");\n            if storage.set_item(STORAGE_KEY, &json).is_err() {\n                leptos::logging::error!(\n                    \"error while trying to set item in localStorage\"\n                );\n            }\n        }\n    });\n\n    // focus the main input on load\n    Effect::new(move |_| {\n        if let Some(input) = input_ref.get() {\n            let _ = input.focus();\n        }\n    });\n\n    view! {\n        <main>\n            <section class=\"todoapp\">\n                <header class=\"header\">\n                    <h1>\"todos\"</h1>\n                    <input\n                        class=\"new-todo\"\n                        placeholder=\"What needs to be done?\"\n                        autofocus\n                        on:keydown=add_todo\n                        node_ref=input_ref\n                    />\n                </header>\n                <section class=\"main\" class:hidden=move || todos.with(|t| t.is_empty())>\n                    <input\n                        id=\"toggle-all\"\n                        class=\"toggle-all\"\n                        type=\"checkbox\"\n                        prop:checked=move || todos.with(|t| t.remaining() > 0)\n                        on:input=move |_| todos.with(|t| t.toggle_all())\n                    />\n                    <label for=\"toggle-all\">\"Mark all as complete\"</label>\n                    <ul class=\"todo-list\">\n                        <For each=filtered_todos key=|todo| todo.id let:todo>\n                            <Todo todo/>\n                        </For>\n                    </ul>\n                </section>\n                <footer class=\"footer\" class:hidden=move || todos.with(|t| t.is_empty())>\n                    <span class=\"todo-count\">\n                        <strong>{move || todos.with(|t| t.remaining().to_string())}</strong>\n                        {move || {\n                            if todos.with(|t| t.remaining()) == 1 { \" item\" } else { \" items\" }\n                        }}\n\n                        \" left\"\n                    </span>\n                    <ul class=\"filters\">\n                        <li>\n                            <a\n                                href=\"#/\"\n                                class=\"selected\"\n                                class:selected=move || mode.get() == Mode::All\n                            >\n                                \"All\"\n                            </a>\n                        </li>\n                        <li>\n                            <a href=\"#/active\" class:selected=move || mode.get() == Mode::Active>\n                                \"Active\"\n                            </a>\n                        </li>\n                        <li>\n                            <a\n                                href=\"#/completed\"\n                                class:selected=move || mode.get() == Mode::Completed\n                            >\n                                \"Completed\"\n                            </a>\n                        </li>\n                    </ul>\n                    <button\n                        class=\"clear-completed hidden\"\n                        class:hidden=move || todos.with(|t| t.completed() == 0)\n                        on:click=move |_| set_todos.update(|t| t.clear_completed())\n                    >\n                        \"Clear completed\"\n                    </button>\n                </footer>\n            </section>\n            <footer class=\"info\">\n                <p>\"Double-click to edit a todo\"</p>\n                <p>\"Created by \" <a href=\"http://todomvc.com\">\"Greg Johnston\"</a></p>\n                <p>\"Part of \" <a href=\"http://todomvc.com\">\"TodoMVC\"</a></p>\n            </footer>\n        </main>\n    }\n}\n\n#[component]\npub fn Todo(todo: Todo) -> impl IntoView {\n    let (editing, set_editing) = signal(false);\n    let set_todos = use_context::<WriteSignal<Todos>>().unwrap();\n\n    // this will be filled by node_ref=input below\n    let todo_input = NodeRef::<Input>::new();\n\n    let save = move |value: &str| {\n        let value = value.trim();\n        if value.is_empty() {\n            set_todos.update(|t| t.remove(todo.id));\n        } else {\n            todo.title.set(value.to_string());\n        }\n        set_editing.set(false);\n    };\n\n    view! {\n        <li class=\"todo\" class:editing=editing class:completed=move || todo.completed.get()>\n            <div class=\"view\">\n                <input\n                    node_ref=todo_input\n                    class=\"toggle\"\n                    type=\"checkbox\"\n                    bind:checked=todo.completed\n                />\n\n                <label on:dblclick=move |_| {\n                    set_editing.set(true);\n                    if let Some(input) = todo_input.get() {\n                        _ = input.focus();\n                    }\n                }>{move || todo.title.get()}</label>\n                <button\n                    class=\"destroy\"\n                    on:click=move |_| set_todos.update(|t| t.remove(todo.id))\n                ></button>\n            </div>\n            {move || {\n                editing\n                    .get()\n                    .then(|| {\n                        view! {\n                            <input\n                                class=\"edit\"\n                                class:hidden=move || !editing.get()\n                                prop:value=move || todo.title.get()\n                                on:focusout:target=move |ev| save(&ev.target().value())\n                                on:keyup:target=move |ev| {\n                                    let key_code = ev.key_code();\n                                    if key_code == ENTER_KEY {\n                                        save(&ev.target().value());\n                                    } else if key_code == ESCAPE_KEY {\n                                        set_editing.set(false);\n                                    }\n                                }\n                            />\n                        }\n                    })\n            }}\n\n        </li>\n    }\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum Mode {\n    Active,\n    Completed,\n    #[default]\n    All,\n}\n\npub fn route(hash: &str) -> Mode {\n    match hash {\n        \"/active\" => Mode::Active,\n        \"/completed\" => Mode::Completed,\n        _ => Mode::All,\n    }\n}\n"
  },
  {
    "path": "examples/todomvc/src/main.rs",
    "content": "pub use todomvc::*;\n\nfn main() {\n    console_error_panic_hook::set_once();\n    leptos::mount::mount_to_body(TodoMVC)\n}\n"
  },
  {
    "path": "examples/websocket/Cargo.toml",
    "content": "[package]\nname = \"websocket\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.30\"\nleptos = { path = \"../../leptos\", features = [\"tracing\"] }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nlog = \"0.4.22\"\nsimple_logger = \"5.0\"\nserde = { version = \"1.0\", features = [\"derive\"] }\naxum = { version = \"0.8.1\", optional = true }\ntokio = { version = \"1.39\", features = [\"full\"], optional = true }\nthiserror = \"2.0.12\"\nwasm-bindgen = \"0.2.100\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\"dep:axum\", \"dep:tokio\", \"leptos/ssr\", \"dep:leptos_axum\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tokio\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"websocket\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"cargo make test-ui\"\nend2end-dir = \"e2e\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "examples/websocket/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "examples/websocket/Makefile.toml",
    "content": "extend = [\n  { path = \"../cargo-make/main.toml\" },\n  { path = \"../cargo-make/cargo-leptos-webdriver-test.toml\" },\n]\n\n[env]\nCLIENT_PROCESS_NAME = \"websocket\"\n\n[tasks.test-ui]\ncwd = \"./e2e\"\ncommand = \"cargo\"\nargs = [\"make\", \"test-ui\", \"${@}\"]\n"
  },
  {
    "path": "examples/websocket/README.md",
    "content": "# Leptos WebSocket\n\nThis example creates a basic WebSocket echo app.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## E2E Testing\n\nSee the [E2E README](./e2e/README.md) for more information about the testing strategy.\n\n## Rendering\n\nSee the [SSR Notes](../SSR_NOTES.md) for more information about Server Side Rendering.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "examples/websocket/e2e/Cargo.toml",
    "content": "[package]\nname = \"websocket_e2e\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\nanyhow = \"1.0\"\nasync-trait = \"0.1.81\"\ncucumber = \"0.21.1\"\nfantoccini = \"0.21.1\"\npretty_assertions = \"1.4\"\nserde_json = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\", \"time\"] }\nurl = \"2.5\"\n\n[[test]]\nname = \"app_suite\"\nharness = false    # Allow Cucumber to print output instead of libtest\n"
  },
  {
    "path": "examples/websocket/e2e/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.test]\nenv = { RUN_AUTOMATICALLY = false }\ncondition = { env_true = [\"RUN_AUTOMATICALLY\"] }\n\n[tasks.ci]\n\n[tasks.test-ui]\ncommand = \"cargo\"\nargs = [\n  \"test\",\n  \"--test\",\n  \"app_suite\",\n  \"--\",\n  \"--retry\",\n  \"2\",\n  \"--retry-after\",\n  \"5sec\",\n  \"--fail-fast\",\n  \"${@}\",\n]\n"
  },
  {
    "path": "examples/websocket/e2e/README.md",
    "content": "# E2E Testing\n\nThis example demonstrates e2e testing with Rust using executable requirements.\n\n## Testing Stack\n\n|    |      Role      |  Description |\n|---|---|---|\n| [Cucumber](https://github.com/cucumber-rs/cucumber/tree/main) | Test Runner | Run [Gherkin](https://cucumber.io/docs/gherkin/reference/) specifications as Rust tests |\n| [Fantoccini](https://github.com/jonhoo/fantoccini/tree/main) | Browser Client | Interact with web pages through WebDriver |\n| [Cargo Leptos ](https://github.com/leptos-rs/cargo-leptos) | Build Tool |  Compile example and start the server and end-2-end tests |\n| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome\n\n## Testing Organization\n\nTesting is organized around what a user can do and see/not see. Test scenarios are grouped by the **user action** and the **object** of that action. This makes it easier to locate and reason about requirements.\n\nHere is a brief overview of how things fit together.\n\n```bash\nfeatures\n└── {action}_{object}.feature   # Specify test scenarios\ntests\n├── fixtures\n│   ├── action.rs               # Perform a user action (click, type, etc.)\n│   ├── check.rs                # Assert what a user can see/not see\n│   ├── find.rs                 # Query page elements\n│   ├── mod.rs\n│   └── world\n│       ├── action_steps.rs     # Map Gherkin steps to user actions\n│       ├── check_steps.rs      # Map Gherkin steps to user expectations\n│       └── mod.rs\n└── app_suite.rs                # Test main \n```\n"
  },
  {
    "path": "examples/websocket/e2e/features/echo_client_error.feature",
    "content": "@echo_client_error\nFeature: Echo Client Error\n\n    Background:\n        Given I see the app\n\n    @echo_client_error-see-fifth-input-error\n    Scenario: Should see the client error\n        Given I add a text as abcde\n        Then I see the label of the input is Error(ServerFnErrorWrapper(Registration(\"Error generated from client\")))\n"
  },
  {
    "path": "examples/websocket/e2e/features/echo_server_error.feature",
    "content": "@echo_server_error\nFeature: Echo Server Error\n\n    Background:\n        Given I see the app\n\n    @echo_server_error-see-third-input-error\n    Scenario: Should see the server error\n        Given I add a text as abc\n        Then I see the label of the input is Error(ServerFnErrorWrapper(Registration(\"Error generated from server\")))\n"
  },
  {
    "path": "examples/websocket/e2e/features/echo_text.feature",
    "content": "@echo_text\nFeature: Echo Text\n\n    Background:\n        Given I see the app\n\n    @echo_text-see-first-input\n    Scenario: Should see the label\n        Given I add a text as a\n        Then I see the label of the input is A\n\n    @add_text-see-second-input\n    Scenario: Should see the label\n        Given I add a text as ab\n        Then I see the label of the input is AB\n\n\n"
  },
  {
    "path": "examples/websocket/e2e/features/open_app.feature",
    "content": "@open_app\nFeature: Open App\n\n  @open_app-title\n  Scenario: Should see the home page title\n    When I open the app\n    Then I see the page title is Simple Echo WebSocket Communication\n"
  },
  {
    "path": "examples/websocket/e2e/tests/app_suite.rs",
    "content": "mod fixtures;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fixtures::world::AppWorld;\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    AppWorld::cucumber()\n        .fail_on_skipped()\n        .run_and_exit(\"./features\")\n        .await;\n    Ok(())\n}\n"
  },
  {
    "path": "examples/websocket/e2e/tests/fixtures/action.rs",
    "content": "use super::{find, world::HOST};\nuse anyhow::Result;\nuse fantoccini::Client;\nuse std::result::Result::Ok;\n\npub async fn goto_path(client: &Client, path: &str) -> Result<()> {\n    let url = format!(\"{}{}\", HOST, path);\n    client.goto(&url).await?;\n\n    Ok(())\n}\n\npub async fn fill_input(client: &Client, text: &str) -> Result<()> {\n    let textbox = find::input(client).await;\n    textbox.send_keys(text).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/websocket/e2e/tests/fixtures/check.rs",
    "content": "use anyhow::{Ok, Result};\nuse fantoccini::{Client, Locator};\nuse pretty_assertions::assert_eq;\n\npub async fn text_on_element(\n    client: &Client,\n    selector: &str,\n    expected_text: &str,\n) -> Result<()> {\n    let element = client\n        .wait()\n        .for_element(Locator::Css(selector))\n        .await\n        .unwrap_or_else(|_| {\n            panic!(\"Element not found by Css selector `{}`\", selector)\n        });\n\n    let actual = element.text().await?;\n    assert_eq!(&actual, expected_text);\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/websocket/e2e/tests/fixtures/find.rs",
    "content": "use fantoccini::{elements::Element, Client, Locator};\n\npub async fn input(client: &Client) -> Element {\n    let textbox = client\n        .wait()\n        .for_element(Locator::Css(\"input\"))\n        .await\n        .expect(\"websocket textbox not found\");\n\n    textbox\n}\n"
  },
  {
    "path": "examples/websocket/e2e/tests/fixtures/mod.rs",
    "content": "pub mod action;\npub mod check;\npub mod find;\npub mod world;\n"
  },
  {
    "path": "examples/websocket/e2e/tests/fixtures/world/action_steps.rs",
    "content": "use crate::fixtures::{action, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::{given, when};\n\n#[given(\"I see the app\")]\n#[when(\"I open the app\")]\nasync fn i_open_the_app(world: &mut AppWorld) -> Result<()> {\n    let client = &world.client;\n    action::goto_path(client, \"\").await?;\n\n    Ok(())\n}\n\n#[given(regex = \"^I add a text as (.*)$\")]\nasync fn i_add_a_text(world: &mut AppWorld, text: String) -> Result<()> {\n    let client = &world.client;\n    action::fill_input(client, text.as_str()).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/websocket/e2e/tests/fixtures/world/check_steps.rs",
    "content": "use crate::fixtures::{check, world::AppWorld};\nuse anyhow::{Ok, Result};\nuse cucumber::then;\nuse std::time::Duration;\nuse tokio::time::sleep;\n\n#[then(regex = \"^I see the page title is (.*)$\")]\nasync fn i_see_the_page_title_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    let client = &world.client;\n    check::text_on_element(client, \"h1\", &text).await?;\n\n    Ok(())\n}\n\n#[then(regex = \"^I see the label of the input is (.*)$\")]\nasync fn i_see_the_label_of_the_input_is(\n    world: &mut AppWorld,\n    text: String,\n) -> Result<()> {\n    sleep(Duration::from_millis(500)).await;\n    let client = &world.client;\n    check::text_on_element(client, \"p\", &text).await?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/websocket/e2e/tests/fixtures/world/mod.rs",
    "content": "pub mod action_steps;\npub mod check_steps;\n\nuse anyhow::Result;\nuse cucumber::World;\nuse fantoccini::{\n    error::NewSessionError, wd::Capabilities, Client, ClientBuilder,\n};\n\npub const HOST: &str = \"http://127.0.0.1:3000\";\n\n#[derive(Debug, World)]\n#[world(init = Self::new)]\npub struct AppWorld {\n    pub client: Client,\n}\n\nimpl AppWorld {\n    async fn new() -> Result<Self, anyhow::Error> {\n        let webdriver_client = build_client().await?;\n\n        Ok(Self {\n            client: webdriver_client,\n        })\n    }\n}\n\nasync fn build_client() -> Result<Client, NewSessionError> {\n    let mut cap = Capabilities::new();\n    let arg = serde_json::from_str(\"{\\\"args\\\": [\\\"-headless\\\"]}\").unwrap();\n    cap.insert(\"goog:chromeOptions\".to_string(), arg);\n\n    let client = ClientBuilder::native()\n        .capabilities(cap)\n        .connect(\"http://localhost:4444\")\n        .await?;\n\n    Ok(client)\n}\n"
  },
  {
    "path": "examples/websocket/src/lib.rs",
    "content": "pub mod websocket;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::websocket::App;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "examples/websocket/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use leptos::prelude::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use websocket::websocket::{shell, App};\n\n    simple_logger::init_with_level(log::Level::Error)\n        .expect(\"couldn't initialize logging\");\n\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, {\n            let leptos_options = leptos_options.clone();\n            move || shell(leptos_options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler(shell))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    println!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    use leptos::mount::mount_to_body;\n    use websocket::websocket::App;\n\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(App);\n}\n"
  },
  {
    "path": "examples/websocket/src/websocket.rs",
    "content": "use leptos::{prelude::*, task::spawn_local};\nuse server_fn::{codec::JsonEncoding, BoxedStream, ServerFnError, Websocket};\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\" />\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n                <AutoReload options=options.clone() />\n                <HydrationScripts options />\n                <link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\" />\n            </head>\n            <body>\n                <App />\n            </body>\n        </html>\n    }\n}\n\n// The websocket protocol can be used on any server function that accepts and returns a [`BoxedStream`]\n// with items that can be encoded by the input and output encoding generics.\n//\n// In this case, the input and output encodings are [`Json`] and [`Json`], respectively which requires\n// the items to implement [`Serialize`] and [`Deserialize`].\n#[server(protocol = Websocket<JsonEncoding, JsonEncoding>)]\nasync fn echo_websocket(\n    input: BoxedStream<String, ServerFnError>,\n) -> Result<BoxedStream<String, ServerFnError>, ServerFnError> {\n    use futures::{channel::mpsc, SinkExt, StreamExt};\n    let mut input = input; // FIXME :-) server fn fields should pass mut through to destructure\n\n    // create a channel of outgoing websocket messages\n    // we'll return rx, so sending a message to tx will send a message to the client via the websocket\n    let (mut tx, rx) = mpsc::channel(1);\n\n    // spawn a task to listen to the input stream of messages coming in over the websocket\n    tokio::spawn(async move {\n        let mut x = 0;\n        while let Some(msg) = input.next().await {\n            // do some work on each message, and then send our responses\n            x += 1;\n            println!(\"In server: {} {:?}\", x, msg);\n            if x % 3 == 0 {\n                let _ = tx\n                    .send(Err(ServerFnError::Registration(\n                        \"Error generated from server\".to_string(),\n                    )))\n                    .await;\n            } else {\n                let _ = tx.send(msg.map(|msg| msg.to_ascii_uppercase())).await;\n            }\n        }\n    });\n\n    Ok(rx.into())\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    use futures::{channel::mpsc, StreamExt};\n    let (mut tx, rx) = mpsc::channel(1);\n    let latest = RwSignal::new(Ok(\"\".into()));\n\n    // we'll only listen for websocket messages on the client\n    if cfg!(feature = \"hydrate\") {\n        spawn_local(async move {\n            match echo_websocket(rx.into()).await {\n                Ok(mut messages) => {\n                    while let Some(msg) = messages.next().await {\n                        leptos::logging::log!(\"{:?}\", msg);\n                        latest.set(msg);\n                    }\n                }\n                Err(e) => leptos::logging::warn!(\"{e}\"),\n            }\n        });\n    }\n\n    let mut x = 0;\n    view! {\n        <h1>Simple Echo WebSocket Communication</h1>\n        <input\n            type=\"text\"\n            on:input:target=move |ev| {\n                x += 1;\n                let msg = ev.target().value();\n                leptos::logging::log!(\"In client: {} {:?}\", x, msg);\n                if x % 5 == 0 {\n                    let _ = tx\n                        .try_send(\n                            Err(\n                                ServerFnError::Registration(\n                                    \"Error generated from client\".to_string(),\n                                ),\n                            ),\n                        );\n                } else {\n                    let _ = tx.try_send(Ok(msg));\n                }\n            }\n        />\n        <div>\n            <ErrorBoundary fallback=|errors| {\n                view! {\n                    <p>\n                        {move || {\n                            errors\n                                .get()\n                                .into_iter()\n                                .map(|(_, e)| format!(\"{e:?}\"))\n                                .collect::<Vec<String>>()\n                                .join(\" \")\n                        }}\n                    </p>\n                }\n            }>\n                <p>{latest}</p>\n            </ErrorBoundary>\n        </div>\n    }\n}\n"
  },
  {
    "path": "examples/websocket/style.css",
    "content": ""
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"A basic Rust devshell for NixOS users developing Leptos\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixos-unstable\";\n    rust-overlay.url = \"github:oxalica/rust-overlay\";\n    flake-utils.url = \"github:numtide/flake-utils\";\n  };\n\n  outputs =\n    {\n      nixpkgs,\n      rust-overlay,\n      flake-utils,\n      ...\n    }:\n    flake-utils.lib.eachDefaultSystem (\n      system:\n      let\n        overlays = [ (import rust-overlay) ];\n        pkgs = import nixpkgs {\n          inherit system overlays;\n        };\n      in\n      with pkgs;\n      {\n        devShells.default = mkShell {\n          buildInputs =\n            [\n              gcc\n              glib\n              openssl\n              pkg-config\n              cacert\n              cargo-make\n              trunk\n              (rust-bin.selectLatestNightlyWith (\n                toolchain:\n                toolchain.default.override {\n                  extensions = [\n                    \"rust-src\"\n                    \"rust-analyzer\"\n                  ];\n                  targets = [ \"wasm32-unknown-unknown\" ];\n                }\n              ))\n            ]\n            ++ pkgs.lib.optionals pkg.stdenv.isDarwin [\n              darwin.apple_sdk.frameworks.SystemConfiguration\n            ];\n\n          shellHook = '''';\n        };\n      }\n    );\n}\n"
  },
  {
    "path": "hydration_context/Cargo.toml",
    "content": "[package]\nname = \"hydration_context\"\nversion = \"0.3.0\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Utilities for sharing data between web servers and client-side web applications.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nthrow_error = { workspace = true }\nor_poisoned = { workspace = true }\nfutures = { workspace = true, default-features = true }\nserde = { features = [\"derive\"] , workspace = true, default-features = true }\nwasm-bindgen = { workspace = true, optional = true , default-features = true }\njs-sys = { optional = true , workspace = true, default-features = true }\npin-project-lite = { workspace = true, default-features = true }\n\n[features]\nbrowser = [\"dep:wasm-bindgen\", \"dep:js-sys\"]\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(leptos_debuginfo)'] }\n"
  },
  {
    "path": "hydration_context/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "hydration_context/README.md",
    "content": "Isomorphic web applications that run on the server to render HTML, then add interactivity in\nthe client, need to accomplish two tasks:\n\n1. Send HTML from the server, so that the client can \"hydrate\" it in the browser by adding\n   event listeners and setting up other interactivity.\n2. Send data that was loaded on the server to the client, so that the client \"hydrates\" with\n   the same data with which the server rendered HTML.\n\nThis crate helps with the second part of this process. It provides a [`SharedContext`] type\nthat allows you to store data on the server, and then extract the same data in the client.\n"
  },
  {
    "path": "hydration_context/src/csr.rs",
    "content": "use super::{SerializedDataId, SharedContext};\nuse crate::{PinnedFuture, PinnedStream};\n\n#[derive(Debug, Default)]\n/// The shared context that should be used in the browser while hydrating.\npub struct CsrSharedContext;\n\nimpl SharedContext for CsrSharedContext {\n    #[inline(always)]\n    fn is_browser(&self) -> bool {\n        true\n    }\n\n    #[inline(always)]\n    fn next_id(&self) -> SerializedDataId {\n        SerializedDataId(0)\n    }\n\n    #[inline(always)]\n    fn write_async(&self, _id: SerializedDataId, _fut: PinnedFuture<String>) {}\n\n    #[inline(always)]\n    fn read_data(&self, _id: &SerializedDataId) -> Option<String> {\n        None\n    }\n\n    #[inline(always)]\n    fn await_data(&self, _id: &SerializedDataId) -> Option<String> {\n        todo!()\n    }\n\n    #[inline(always)]\n    fn pending_data(&self) -> Option<PinnedStream<String>> {\n        None\n    }\n\n    #[inline(always)]\n    fn get_is_hydrating(&self) -> bool {\n        false\n    }\n\n    #[inline(always)]\n    fn set_is_hydrating(&self, _is_hydrating: bool) {}\n\n    #[inline(always)]\n    fn errors(\n        &self,\n        _boundary_id: &SerializedDataId,\n    ) -> Vec<(throw_error::ErrorId, throw_error::Error)> {\n        Vec::new()\n    }\n\n    #[inline(always)]\n    fn take_errors(\n        &self,\n    ) -> Vec<(SerializedDataId, throw_error::ErrorId, throw_error::Error)> {\n        Vec::new()\n    }\n\n    #[inline(always)]\n    fn register_error(\n        &self,\n        _error_boundary: SerializedDataId,\n        _error_id: throw_error::ErrorId,\n        _error: throw_error::Error,\n    ) {\n    }\n\n    #[inline(always)]\n    fn seal_errors(&self, _boundary_id: &SerializedDataId) {}\n\n    #[inline(always)]\n    fn during_hydration(&self) -> bool {\n        false\n    }\n\n    #[inline(always)]\n    fn hydration_complete(&self) {}\n\n    #[inline(always)]\n    fn defer_stream(&self, _wait_for: PinnedFuture<()>) {}\n\n    #[inline(always)]\n    fn await_deferred(&self) -> Option<PinnedFuture<()>> {\n        None\n    }\n\n    #[inline(always)]\n    fn set_incomplete_chunk(&self, _id: SerializedDataId) {}\n\n    #[inline(always)]\n    fn get_incomplete_chunk(&self, _id: &SerializedDataId) -> bool {\n        false\n    }\n}\n"
  },
  {
    "path": "hydration_context/src/hydrate.rs",
    "content": "// #[wasm_bindgen(thread_local)] is deprecated in wasm-bindgen 0.2.96\n// but the replacement is also only shipped in that version\n// as a result, we'll just allow deprecated for now\n#![allow(deprecated)]\n\nuse super::{SerializedDataId, SharedContext};\nuse crate::{PinnedFuture, PinnedStream};\nuse core::fmt::Debug;\nuse js_sys::Array;\nuse std::{\n    fmt::Display,\n    sync::{\n        atomic::{AtomicBool, AtomicUsize, Ordering},\n        LazyLock,\n    },\n};\nuse throw_error::{Error, ErrorId};\nuse wasm_bindgen::{prelude::wasm_bindgen, JsCast};\n\n#[wasm_bindgen]\nextern \"C\" {\n    #[wasm_bindgen(thread_local)]\n    static __RESOLVED_RESOURCES: Array;\n\n    #[wasm_bindgen(thread_local)]\n    static __SERIALIZED_ERRORS: Array;\n\n    #[wasm_bindgen(thread_local)]\n    static __INCOMPLETE_CHUNKS: Array;\n}\n\nfn serialized_errors() -> Vec<(SerializedDataId, ErrorId, Error)> {\n    __SERIALIZED_ERRORS.with(|s| {\n        s.iter()\n            .flat_map(|value| {\n                value.dyn_ref::<Array>().map(|value| {\n                    let error_boundary_id =\n                        value.get(0).as_f64().unwrap() as usize;\n                    let error_id = value.get(1).as_f64().unwrap() as usize;\n                    let value = value\n                        .get(2)\n                        .as_string()\n                        .expect(\"Expected a [number, string] tuple\");\n                    (\n                        SerializedDataId(error_boundary_id),\n                        ErrorId::from(error_id),\n                        Error::from(SerializedError(value)),\n                    )\n                })\n            })\n            .collect()\n    })\n}\n\nfn incomplete_chunks() -> Vec<SerializedDataId> {\n    __INCOMPLETE_CHUNKS.with(|i| {\n        i.iter()\n            .map(|value| {\n                let id = value.as_f64().unwrap() as usize;\n                SerializedDataId(id)\n            })\n            .collect()\n    })\n}\n\n/// An error that has been serialized across the network boundary.\n#[derive(Debug, Clone)]\nstruct SerializedError(String);\n\nimpl Display for SerializedError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&self.0, f)\n    }\n}\n\nimpl std::error::Error for SerializedError {}\n\n#[derive(Default)]\n/// The shared context that should be used in the browser while hydrating.\npub struct HydrateSharedContext {\n    id: AtomicUsize,\n    is_hydrating: AtomicBool,\n    during_hydration: AtomicBool,\n    errors: LazyLock<Vec<(SerializedDataId, ErrorId, Error)>>,\n    incomplete: LazyLock<Vec<SerializedDataId>>,\n}\n\nimpl HydrateSharedContext {\n    /// Creates a new shared context for hydration in the browser.\n    pub fn new() -> Self {\n        Self {\n            id: AtomicUsize::new(0),\n            is_hydrating: AtomicBool::new(true),\n            during_hydration: AtomicBool::new(true),\n            errors: LazyLock::new(serialized_errors),\n            incomplete: LazyLock::new(incomplete_chunks),\n        }\n    }\n\n    /// Creates a new shared context for hydration in the browser.\n    ///\n    /// This defaults to a mode in which the app is not hydrated, but allows you to opt into\n    /// hydration for certain portions using [`SharedContext::set_is_hydrating`].\n    pub fn new_islands() -> Self {\n        Self {\n            id: AtomicUsize::new(0),\n            is_hydrating: AtomicBool::new(false),\n            during_hydration: AtomicBool::new(true),\n            errors: LazyLock::new(serialized_errors),\n            incomplete: LazyLock::new(incomplete_chunks),\n        }\n    }\n}\n\nimpl Debug for HydrateSharedContext {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"HydrateSharedContext\").finish()\n    }\n}\n\nimpl SharedContext for HydrateSharedContext {\n    fn is_browser(&self) -> bool {\n        true\n    }\n\n    fn next_id(&self) -> SerializedDataId {\n        let id = self.id.fetch_add(1, Ordering::Relaxed);\n        SerializedDataId(id)\n    }\n\n    fn write_async(&self, _id: SerializedDataId, _fut: PinnedFuture<String>) {}\n\n    fn read_data(&self, id: &SerializedDataId) -> Option<String> {\n        __RESOLVED_RESOURCES.with(|r| r.get(id.0 as u32).as_string())\n    }\n\n    fn await_data(&self, _id: &SerializedDataId) -> Option<String> {\n        todo!()\n    }\n\n    fn pending_data(&self) -> Option<PinnedStream<String>> {\n        None\n    }\n\n    fn during_hydration(&self) -> bool {\n        self.during_hydration.load(Ordering::Relaxed)\n    }\n\n    fn hydration_complete(&self) {\n        self.during_hydration.store(false, Ordering::Relaxed)\n    }\n\n    fn get_is_hydrating(&self) -> bool {\n        self.is_hydrating.load(Ordering::Relaxed)\n    }\n\n    fn set_is_hydrating(&self, is_hydrating: bool) {\n        self.is_hydrating.store(is_hydrating, Ordering::Relaxed)\n    }\n\n    fn errors(&self, boundary_id: &SerializedDataId) -> Vec<(ErrorId, Error)> {\n        self.errors\n            .iter()\n            .filter_map(|(boundary, id, error)| {\n                if boundary == boundary_id {\n                    Some((id.clone(), error.clone()))\n                } else {\n                    None\n                }\n            })\n            .collect()\n    }\n\n    #[inline(always)]\n    fn register_error(\n        &self,\n        _error_boundary: SerializedDataId,\n        _error_id: ErrorId,\n        _error: Error,\n    ) {\n    }\n\n    #[inline(always)]\n    fn seal_errors(&self, _boundary_id: &SerializedDataId) {}\n\n    fn take_errors(&self) -> Vec<(SerializedDataId, ErrorId, Error)> {\n        self.errors.clone()\n    }\n\n    #[inline(always)]\n    fn defer_stream(&self, _wait_for: PinnedFuture<()>) {}\n\n    #[inline(always)]\n    fn await_deferred(&self) -> Option<PinnedFuture<()>> {\n        None\n    }\n\n    #[inline(always)]\n    fn set_incomplete_chunk(&self, _id: SerializedDataId) {}\n\n    fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool {\n        self.incomplete.iter().any(|entry| entry == id)\n    }\n}\n"
  },
  {
    "path": "hydration_context/src/lib.rs",
    "content": "//! Isomorphic web applications that run on the server to render HTML, then add interactivity in\n//! the client, need to accomplish two tasks:\n//! 1. Send HTML from the server, so that the client can \"hydrate\" it in the browser by adding\n//!    event listeners and setting up other interactivity.\n//! 2. Send data that was loaded on the server to the client, so that the client \"hydrates\" with\n//!    the same data with which the server rendered HTML.\n//!\n//! This crate helps with the second part of this process. It provides a [`SharedContext`] type\n//! that allows you to store data on the server, and then extract the same data in the client.\n\n#![deny(missing_docs)]\n#![forbid(unsafe_code)]\n#![cfg_attr(docsrs, feature(doc_cfg))]\n\n#[cfg(feature = \"browser\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"browser\")))]\nmod csr;\n#[cfg(feature = \"browser\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"browser\")))]\nmod hydrate;\nmod ssr;\n#[cfg(feature = \"browser\")]\npub use csr::*;\nuse futures::Stream;\n#[cfg(feature = \"browser\")]\npub use hydrate::*;\nuse serde::{Deserialize, Serialize};\npub use ssr::*;\nuse std::{fmt::Debug, future::Future, pin::Pin};\nuse throw_error::{Error, ErrorId};\n\n/// Type alias for a boxed [`Future`].\npub type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send + Sync>>;\n/// Type alias for a boxed [`Future`] that is `!Send`.\npub type PinnedLocalFuture<T> = Pin<Box<dyn Future<Output = T>>>;\n/// Type alias for a boxed [`Stream`].\npub type PinnedStream<T> = Pin<Box<dyn Stream<Item = T> + Send + Sync>>;\n\n#[derive(\n    Clone, Debug, PartialEq, Eq, Hash, Default, Deserialize, Serialize,\n)]\n#[serde(transparent)]\n/// A unique identifier for a piece of data that will be serialized\n/// from the server to the client.\npub struct SerializedDataId(usize);\n\nimpl SerializedDataId {\n    /// Create a new instance of [`SerializedDataId`].\n    pub fn new(id: usize) -> Self {\n        SerializedDataId(id)\n    }\n\n    /// Consume into the inner usize identifier.\n    pub fn into_inner(self) -> usize {\n        self.0\n    }\n}\n\nimpl From<SerializedDataId> for ErrorId {\n    fn from(value: SerializedDataId) -> Self {\n        value.0.into()\n    }\n}\n\n/// Information that will be shared between the server and the client.\npub trait SharedContext: Debug {\n    /// Whether the application is running in the browser.\n    fn is_browser(&self) -> bool;\n\n    /// Returns the next in a series of IDs that is unique to a particular request and response.\n    ///\n    /// This should not be used as a global unique ID mechanism. It is specific to the process\n    /// of serializing and deserializing data from the server to the browser as part of an HTTP\n    /// response.\n    fn next_id(&self) -> SerializedDataId;\n\n    /// The given [`Future`] should resolve with some data that can be serialized\n    /// from the server to the client. This will be polled as part of the process of\n    /// building the HTTP response, *not* when it is first created.\n    ///\n    /// In browser implementations, this should be a no-op.\n    fn write_async(&self, id: SerializedDataId, fut: PinnedFuture<String>);\n\n    /// Reads the current value of some data from the shared context, if it has been\n    /// sent from the server. This returns the serialized data as a `String` that should\n    /// be deserialized.\n    ///\n    /// On the server and in client-side rendered implementations, this should\n    /// always return [`None`].\n    fn read_data(&self, id: &SerializedDataId) -> Option<String>;\n\n    /// Returns a [`Future`] that resolves with a `String` that should\n    /// be deserialized once the given piece of server data has resolved.\n    ///\n    /// On the server and in client-side rendered implementations, this should\n    /// return a [`Future`] that is immediately ready with [`None`].\n    fn await_data(&self, id: &SerializedDataId) -> Option<String>;\n\n    /// Returns some [`Stream`] of HTML that contains JavaScript `<script>` tags defining\n    /// all values being serialized from the server to the client, with their serialized values\n    /// and any boilerplate needed to notify a running application that they exist; or `None`.\n    ///\n    /// In browser implementations, this return `None`.\n    fn pending_data(&self) -> Option<PinnedStream<String>>;\n\n    /// Whether the page is currently being hydrated.\n    ///\n    /// Should always be `false` on the server or when client-rendering, including after the\n    /// initial hydration in the client.\n    fn during_hydration(&self) -> bool;\n\n    /// Tells the shared context that the hydration process is complete.\n    fn hydration_complete(&self);\n\n    /// Returns `true` if you are currently in a part of the application tree that should be\n    /// hydrated.\n    ///\n    /// For example, in an app with \"islands,\" this should be `true` inside islands and\n    /// false elsewhere.\n    fn get_is_hydrating(&self) -> bool;\n\n    /// Sets whether you are currently in a part of the application tree that should be hydrated.\n    ///\n    /// For example, in an app with \"islands,\" this should be `true` inside islands and\n    /// false elsewhere.\n    fn set_is_hydrating(&self, is_hydrating: bool);\n\n    /// Returns all errors that have been registered, removing them from the list.\n    fn take_errors(&self) -> Vec<(SerializedDataId, ErrorId, Error)>;\n\n    /// Returns the set of errors that have been registered with a particular boundary.\n    fn errors(&self, boundary_id: &SerializedDataId) -> Vec<(ErrorId, Error)>;\n\n    /// \"Seals\" an error boundary, preventing further errors from being registered for it.\n    ///\n    /// This can be used in streaming SSR scenarios in which the final state of the error boundary\n    /// can only be known after the initial state is hydrated.\n    fn seal_errors(&self, boundary_id: &SerializedDataId);\n\n    /// Registers an error with the context to be shared from server to client.\n    fn register_error(\n        &self,\n        error_boundary: SerializedDataId,\n        error_id: ErrorId,\n        error: Error,\n    );\n\n    /// Adds a `Future` to the set of “blocking resources” that should prevent the server’s\n    /// response stream from beginning until all are resolved. The `Future` returned by\n    /// blocking resources will not resolve until every `Future` added by this method\n    /// has resolved.\n    ///\n    /// In browser implementations, this should be a no-op.\n    fn defer_stream(&self, wait_for: PinnedFuture<()>);\n\n    /// Returns a `Future` that will resolve when every `Future` added via\n    /// [`defer_stream`](Self::defer_stream) has resolved.\n    ///\n    /// In browser implementations, this should be a no-op.\n    fn await_deferred(&self) -> Option<PinnedFuture<()>>;\n\n    /// Tells the client that this chunk is being sent from the server before all its data have\n    /// loaded, and it may be in a fallback state.\n    fn set_incomplete_chunk(&self, id: SerializedDataId);\n\n    /// Checks whether this chunk is being sent from the server before all its data have loaded.\n    fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool;\n}\n"
  },
  {
    "path": "hydration_context/src/ssr.rs",
    "content": "use super::{SerializedDataId, SharedContext};\nuse crate::{PinnedFuture, PinnedStream};\nuse futures::{\n    future::join_all,\n    stream::{self, once},\n    Stream, StreamExt,\n};\nuse or_poisoned::OrPoisoned;\nuse std::{\n    collections::HashSet,\n    fmt::{Debug, Write},\n    mem,\n    pin::Pin,\n    sync::{\n        atomic::{AtomicBool, AtomicUsize, Ordering},\n        Arc, Mutex, RwLock,\n    },\n    task::{Context, Poll},\n};\nuse throw_error::{Error, ErrorId};\n\ntype AsyncDataBuf = Arc<RwLock<Vec<(SerializedDataId, PinnedFuture<String>)>>>;\ntype ErrorBuf = Arc<RwLock<Vec<(SerializedDataId, ErrorId, Error)>>>;\ntype SealedErrors = Arc<RwLock<HashSet<SerializedDataId>>>;\n\n#[derive(Default)]\n/// The shared context that should be used on the server side.\npub struct SsrSharedContext {\n    id: AtomicUsize,\n    non_hydration_id: AtomicUsize,\n    is_hydrating: AtomicBool,\n    sync_buf: RwLock<Vec<ResolvedData>>,\n    async_buf: AsyncDataBuf,\n    errors: ErrorBuf,\n    sealed_error_boundaries: SealedErrors,\n    deferred: Mutex<Vec<PinnedFuture<()>>>,\n    incomplete: Arc<Mutex<Vec<SerializedDataId>>>,\n}\n\nimpl SsrSharedContext {\n    /// Creates a new shared context for rendering HTML on the server.\n    pub fn new() -> Self {\n        Self {\n            is_hydrating: AtomicBool::new(true),\n            non_hydration_id: AtomicUsize::new(usize::MAX),\n            ..Default::default()\n        }\n    }\n\n    /// Creates a new shared context for rendering HTML on the server in \"islands\" mode.\n    ///\n    /// This defaults to a mode in which the app is not hydrated, but allows you to opt into\n    /// hydration for certain portions using [`SharedContext::set_is_hydrating`].\n    pub fn new_islands() -> Self {\n        Self {\n            is_hydrating: AtomicBool::new(false),\n            non_hydration_id: AtomicUsize::new(usize::MAX),\n            ..Default::default()\n        }\n    }\n\n    /// Consume the data buffers, awaiting all async resources,\n    /// returning both sync and async buffers.\n    /// Useful to implement custom hydration contexts.\n    ///\n    /// WARNING: this will clear the internal buffers, it should only be called once.\n    /// A second call would return an empty `vec![]`.\n    pub async fn consume_buffers(&self) -> Vec<(SerializedDataId, String)> {\n        let sync_data = mem::take(&mut *self.sync_buf.write().or_poisoned());\n        let async_data = mem::take(&mut *self.async_buf.write().or_poisoned());\n\n        let mut all_data = Vec::new();\n        for resolved in sync_data {\n            all_data.push((resolved.0, resolved.1));\n        }\n        for (id, fut) in async_data {\n            let data = fut.await;\n            all_data.push((id, data));\n        }\n        all_data\n    }\n}\n\nimpl Debug for SsrSharedContext {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SsrSharedContext\")\n            .field(\"id\", &self.id)\n            .field(\"is_hydrating\", &self.is_hydrating)\n            .field(\"sync_buf\", &self.sync_buf)\n            .field(\"async_buf\", &self.async_buf.read().or_poisoned().len())\n            .finish()\n    }\n}\n\nimpl SharedContext for SsrSharedContext {\n    fn is_browser(&self) -> bool {\n        false\n    }\n\n    #[track_caller]\n    fn next_id(&self) -> SerializedDataId {\n        let id = if self.get_is_hydrating() {\n            self.id.fetch_add(1, Ordering::Relaxed)\n        } else {\n            self.non_hydration_id.fetch_sub(1, Ordering::Relaxed)\n        };\n        SerializedDataId(id)\n    }\n\n    fn write_async(&self, id: SerializedDataId, fut: PinnedFuture<String>) {\n        self.async_buf.write().or_poisoned().push((id, fut))\n    }\n\n    fn read_data(&self, _id: &SerializedDataId) -> Option<String> {\n        None\n    }\n\n    fn await_data(&self, _id: &SerializedDataId) -> Option<String> {\n        None\n    }\n\n    fn get_is_hydrating(&self) -> bool {\n        self.is_hydrating.load(Ordering::SeqCst)\n    }\n\n    fn set_is_hydrating(&self, is_hydrating: bool) {\n        self.is_hydrating.store(is_hydrating, Ordering::SeqCst)\n    }\n\n    fn errors(&self, boundary_id: &SerializedDataId) -> Vec<(ErrorId, Error)> {\n        self.errors\n            .read()\n            .or_poisoned()\n            .iter()\n            .filter_map(|(boundary, id, error)| {\n                if boundary == boundary_id {\n                    Some((id.clone(), error.clone()))\n                } else {\n                    None\n                }\n            })\n            .collect()\n    }\n\n    fn register_error(\n        &self,\n        error_boundary_id: SerializedDataId,\n        error_id: ErrorId,\n        error: Error,\n    ) {\n        self.errors.write().or_poisoned().push((\n            error_boundary_id,\n            error_id,\n            error,\n        ));\n    }\n\n    fn take_errors(&self) -> Vec<(SerializedDataId, ErrorId, Error)> {\n        mem::take(&mut *self.errors.write().or_poisoned())\n    }\n\n    fn seal_errors(&self, boundary_id: &SerializedDataId) {\n        self.sealed_error_boundaries\n            .write()\n            .or_poisoned()\n            .insert(boundary_id.clone());\n    }\n\n    fn pending_data(&self) -> Option<PinnedStream<String>> {\n        let sync_data = mem::take(&mut *self.sync_buf.write().or_poisoned());\n        let async_data = self.async_buf.read().or_poisoned();\n\n        // 1) initial, synchronous setup chunk\n        let mut initial_chunk = String::new();\n        // resolved synchronous resources and errors\n        initial_chunk.push_str(\"__RESOLVED_RESOURCES=[\");\n        for resolved in sync_data {\n            resolved.write_to_buf(&mut initial_chunk);\n            initial_chunk.push(',');\n        }\n        initial_chunk.push_str(\"];\");\n\n        initial_chunk.push_str(\"__SERIALIZED_ERRORS=[\");\n        for error in mem::take(&mut *self.errors.write().or_poisoned()) {\n            _ = write!(\n                initial_chunk,\n                \"[{}, {}, {:?}],\",\n                error.0 .0,\n                error.1,\n                error.2.to_string()\n            );\n        }\n        initial_chunk.push_str(\"];\");\n\n        // pending async resources\n        initial_chunk.push_str(\"__PENDING_RESOURCES=[\");\n        for (id, _) in async_data.iter() {\n            _ = write!(&mut initial_chunk, \"{},\", id.0);\n        }\n        initial_chunk.push_str(\"];\");\n\n        // resolvers\n        initial_chunk.push_str(\"__RESOURCE_RESOLVERS=[];\");\n\n        let async_data = AsyncDataStream {\n            async_buf: Arc::clone(&self.async_buf),\n            errors: Arc::clone(&self.errors),\n            sealed_error_boundaries: Arc::clone(&self.sealed_error_boundaries),\n        };\n\n        let incomplete = Arc::clone(&self.incomplete);\n\n        let stream = stream::once(async move { initial_chunk })\n            .chain(async_data)\n            .chain(once(async move {\n                let mut script = String::new();\n                script.push_str(\"__INCOMPLETE_CHUNKS=[\");\n                for chunk in mem::take(&mut *incomplete.lock().or_poisoned()) {\n                    _ = write!(script, \"{},\", chunk.0);\n                }\n                script.push_str(\"];\");\n                script\n            }));\n        Some(Box::pin(stream))\n    }\n\n    fn during_hydration(&self) -> bool {\n        false\n    }\n\n    fn hydration_complete(&self) {}\n\n    fn defer_stream(&self, wait_for: PinnedFuture<()>) {\n        self.deferred.lock().or_poisoned().push(wait_for);\n    }\n\n    fn await_deferred(&self) -> Option<PinnedFuture<()>> {\n        let deferred = mem::take(&mut *self.deferred.lock().or_poisoned());\n        if deferred.is_empty() {\n            None\n        } else {\n            Some(Box::pin(async move {\n                join_all(deferred).await;\n            }))\n        }\n    }\n\n    fn set_incomplete_chunk(&self, id: SerializedDataId) {\n        self.incomplete.lock().or_poisoned().push(id);\n    }\n\n    fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool {\n        self.incomplete\n            .lock()\n            .or_poisoned()\n            .iter()\n            .any(|entry| entry == id)\n    }\n}\n\nstruct AsyncDataStream {\n    async_buf: AsyncDataBuf,\n    errors: ErrorBuf,\n    sealed_error_boundaries: SealedErrors,\n}\n\nimpl Stream for AsyncDataStream {\n    type Item = String;\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Self::Item>> {\n        let mut resolved = String::new();\n        let mut async_buf = self.async_buf.write().or_poisoned();\n        let data = mem::take(&mut *async_buf);\n        for (id, mut fut) in data {\n            match fut.as_mut().poll(cx) {\n                // if it's not ready, put it back into the queue\n                Poll::Pending => {\n                    async_buf.push((id, fut));\n                }\n                Poll::Ready(data) => {\n                    let data = data.replace('<', \"\\\\u003c\");\n                    _ = write!(\n                        resolved,\n                        \"__RESOLVED_RESOURCES[{}] = {:?};\",\n                        id.0, data\n                    );\n                }\n            }\n        }\n        let sealed = self.sealed_error_boundaries.read().or_poisoned();\n        for error in mem::take(&mut *self.errors.write().or_poisoned()) {\n            if !sealed.contains(&error.0) {\n                _ = write!(\n                    resolved,\n                    \"__SERIALIZED_ERRORS.push([{}, {}, {:?}]);\",\n                    error.0 .0,\n                    error.1,\n                    error.2.to_string()\n                );\n            }\n        }\n\n        if async_buf.is_empty() && resolved.is_empty() {\n            return Poll::Ready(None);\n        }\n        if resolved.is_empty() {\n            return Poll::Pending;\n        }\n\n        Poll::Ready(Some(resolved))\n    }\n}\n\n#[derive(Debug)]\nstruct ResolvedData(SerializedDataId, String);\n\nimpl ResolvedData {\n    pub fn write_to_buf(&self, buf: &mut String) {\n        let ResolvedData(id, ser) = self;\n        // escapes < to prevent it being interpreted as another opening HTML tag\n        let ser = ser.replace('<', \"\\\\u003c\");\n        write!(buf, \"{}: {:?}\", id.0, ser).unwrap();\n    }\n}\n"
  },
  {
    "path": "integrations/actix/Cargo.toml",
    "content": "[package]\nname = \"leptos_actix\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Actix integrations for the Leptos web framework.\"\nversion = \"0.8.7\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nactix-http = { workspace = true, default-features = true }\nactix-files = { workspace = true, default-features = true }\nactix-web = { workspace = true, default-features = false }\nfutures = { workspace = true, default-features = true }\nany_spawner = { workspace = true, features = [\"tokio\"] }\nhydration_context = { workspace = true }\nleptos = { workspace = true, features = [\"nonce\", \"ssr\"] }\nleptos_integration_utils = { workspace = true }\nleptos_macro = { workspace = true, features = [\"actix\"] }\nleptos_meta = { workspace = true, features = [\"nonce\"] }\nleptos_router = { workspace = true, features = [\"ssr\"] }\nserver_fn = { workspace = true, features = [\"actix-no-default\"] }\ntachys = { workspace = true }\nserde_json = { workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\ntokio = { features = [\"rt\", \"fs\"], workspace = true, default-features = true }\nsend_wrapper = { workspace = true, default-features = true }\nor_poisoned = { workspace = true, default-features = true }\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[features]\ndefault = [\"actix-default\"]\nactix-default = [\"actix-web/default\"]\nislands-router = [\"tachys/islands\"]\ntracing = [\"dep:tracing\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\"]\nmax_combination_size = 2\n"
  },
  {
    "path": "integrations/actix/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../../\" }\n"
  },
  {
    "path": "integrations/actix/src/lib.rs",
    "content": "#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\n//! Provides functions to easily integrate Leptos with Actix.\n//!\n//! For more details on how to use the integrations, see the\n//! [`examples`](https://github.com/leptos-rs/leptos/tree/main/examples)\n//! directory in the Leptos repository.\n\nuse actix_files::NamedFile;\nuse actix_http::header::{HeaderName, HeaderValue, ACCEPT, LOCATION, REFERER};\nuse actix_web::{\n    dev::{ServiceFactory, ServiceRequest},\n    http::header,\n    test,\n    web::{Data, Payload, ServiceConfig},\n    *,\n};\nuse futures::{stream::once, Stream, StreamExt};\nuse http::StatusCode;\nuse hydration_context::SsrSharedContext;\nuse leptos::{\n    config::LeptosOptions,\n    context::{provide_context, use_context},\n    hydration::IslandsRouterNavigation,\n    prelude::expect_context,\n    reactive::{computed::ScopedFuture, owner::Owner},\n    IntoView,\n};\nuse leptos_integration_utils::{\n    BoxedFnOnce, ExtendResponse, PinnedFuture, PinnedStream,\n};\nuse leptos_meta::ServerMetaContext;\nuse leptos_router::{\n    components::provide_server_redirect,\n    location::RequestUrl,\n    static_routes::{RegenerationFn, ResolvedStaticPath},\n    ExpandOptionals, Method, PathSegment, RouteList, RouteListing, SsrMode,\n};\nuse or_poisoned::OrPoisoned;\nuse send_wrapper::SendWrapper;\nuse server_fn::{\n    error::ServerFnErrorErr, redirect::REDIRECT_HEADER,\n    request::actix::ActixRequest,\n};\nuse std::{\n    collections::{HashMap, HashSet},\n    fmt::{Debug, Display},\n    future::Future,\n    ops::{Deref, DerefMut},\n    path::Path,\n    sync::{Arc, LazyLock, RwLock},\n};\n\n/// This struct lets you define headers and override the status of the Response from an Element or a Server Function\n/// Typically contained inside of a ResponseOptions. Setting this is useful for cookies and custom responses.\n#[derive(Debug, Clone, Default)]\npub struct ResponseParts {\n    /// If provided, this will overwrite any other status code for this response.\n    pub status: Option<StatusCode>,\n    /// The map of headers that should be added to the response.\n    pub headers: header::HeaderMap,\n}\n\nimpl ResponseParts {\n    /// Insert a header, overwriting any previous value with the same key\n    pub fn insert_header(\n        &mut self,\n        key: header::HeaderName,\n        value: header::HeaderValue,\n    ) {\n        self.headers.insert(key, value);\n    }\n\n    /// Append a header, leaving any header with the same key intact\n    pub fn append_header(\n        &mut self,\n        key: header::HeaderName,\n        value: header::HeaderValue,\n    ) {\n        self.headers.append(key, value);\n    }\n}\n\n/// A wrapper for an Actix [`HttpRequest`] that allows it to be used in an\n/// `Send`/`Sync` setting like Leptos's Context API.\n#[derive(Debug, Clone)]\npub struct Request(SendWrapper<HttpRequest>);\n\nimpl Request {\n    /// Wraps an existing Actix request.\n    pub fn new(req: &HttpRequest) -> Self {\n        Self(SendWrapper::new(req.clone()))\n    }\n\n    /// Consumes the wrapper and returns the inner Actix request.\n    pub fn into_inner(self) -> HttpRequest {\n        self.0.take()\n    }\n}\n\nimpl Deref for Request {\n    type Target = HttpRequest;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl DerefMut for Request {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// Allows you to override details of the HTTP response like the status code and add Headers/Cookies.\n#[derive(Debug, Clone, Default)]\npub struct ResponseOptions(pub Arc<RwLock<ResponseParts>>);\n\nimpl ResponseOptions {\n    /// A simpler way to overwrite the contents of `ResponseOptions` with a new `ResponseParts`.\n    pub fn overwrite(&self, parts: ResponseParts) {\n        let mut writable = self.0.write().or_poisoned();\n        *writable = parts\n    }\n    /// Set the status of the returned Response.\n    pub fn set_status(&self, status: StatusCode) {\n        let mut writeable = self.0.write().or_poisoned();\n        let res_parts = &mut *writeable;\n        res_parts.status = Some(status);\n    }\n    /// Insert a header, overwriting any previous value with the same key.\n    pub fn insert_header(\n        &self,\n        key: header::HeaderName,\n        value: header::HeaderValue,\n    ) {\n        let mut writeable = self.0.write().or_poisoned();\n        let res_parts = &mut *writeable;\n        res_parts.headers.insert(key, value);\n    }\n    /// Append a header, leaving any header with the same key intact.\n    pub fn append_header(\n        &self,\n        key: header::HeaderName,\n        value: header::HeaderValue,\n    ) {\n        let mut writeable = self.0.write().or_poisoned();\n        let res_parts = &mut *writeable;\n        res_parts.headers.append(key, value);\n    }\n}\n\nstruct ActixResponse(HttpResponse);\n\nimpl ExtendResponse for ActixResponse {\n    type ResponseOptions = ResponseOptions;\n\n    fn from_stream(\n        stream: impl Stream<Item = String> + Send + 'static,\n    ) -> Self {\n        ActixResponse(\n            HttpResponse::Ok()\n                .content_type(\"text/html\")\n                .streaming(stream.map(|chunk| {\n                    Ok(web::Bytes::from(chunk)) as Result<web::Bytes>\n                })),\n        )\n    }\n\n    fn extend_response(&mut self, res_options: &Self::ResponseOptions) {\n        let mut res_options = res_options.0.write().or_poisoned();\n\n        let headers = self.0.headers_mut();\n        for (key, value) in std::mem::take(&mut res_options.headers) {\n            headers.append(key, value);\n        }\n\n        // Set status to what is returned in the function\n        if let Some(status) = res_options.status {\n            *self.0.status_mut() = status;\n        }\n    }\n\n    fn set_default_content_type(&mut self, content_type: &str) {\n        let headers = self.0.headers_mut();\n        if !headers.contains_key(header::CONTENT_TYPE) {\n            // Set the Content Type headers on all responses. This makes Firefox show the page source\n            // without complaining\n            headers.insert(\n                header::CONTENT_TYPE,\n                HeaderValue::from_str(content_type).unwrap(),\n            );\n        }\n    }\n}\n\n/// Provides an easy way to redirect the user from within a server function.\n///\n/// Calling `redirect` in a server function will redirect the browser in three\n/// situations:\n/// 1. A server function that is calling in a [blocking\n///    resource](leptos::server::Resource::new_blocking).\n/// 2. A server function that is called from WASM running in the client (e.g., a dispatched action\n///    or a spawned `Future`).\n/// 3. A `<form>` submitted to the server function endpoint using default browser APIs (often due\n///    to using [`ActionForm`](leptos::form::ActionForm) without JS/WASM present.)\n///\n/// Using it with a non-blocking [`Resource`](leptos::server::Resource) will not work if you are using streaming rendering,\n/// as the response's headers will already have been sent by the time the server function calls `redirect()`.\n///\n/// ### Implementation\n///\n/// This sets the `Location` header to the URL given.\n///\n/// If the route or server function in which this is called is being accessed\n/// by an ordinary `GET` request or an HTML `<form>` without any enhancement, it also sets a\n/// status code of `302` for a temporary redirect. (This is determined by whether the `Accept`\n/// header contains `text/html` as it does for an ordinary navigation.)\n///\n/// Otherwise, it sets a custom header that indicates to the client that it should redirect,\n/// without actually setting the status code. This means that the client will not follow the\n/// redirect, and can therefore return the value of the server function and then handle\n/// the redirect with client-side routing.\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn redirect(path: &str) {\n    if let (Some(req), Some(res)) =\n        (use_context::<Request>(), use_context::<ResponseOptions>())\n    {\n        // insert the Location header in any case\n        res.insert_header(\n            header::LOCATION,\n            header::HeaderValue::from_str(path)\n                .expect(\"Failed to create HeaderValue\"),\n        );\n\n        let accepts_html = req\n            .headers()\n            .get(ACCEPT)\n            .and_then(|v| v.to_str().ok())\n            .map(|v| v.contains(\"text/html\"))\n            .unwrap_or(false);\n        if accepts_html {\n            // if the request accepts text/html, it's a plain form request and needs\n            // to have the 302 code set\n            res.set_status(StatusCode::FOUND);\n        } else {\n            // otherwise, we sent it from the server fn client and actually don't want\n            // to set a real redirect, as this will break the ability to return data\n            // instead, set the REDIRECT_HEADER to indicate that the client should redirect\n            res.insert_header(\n                HeaderName::from_static(REDIRECT_HEADER),\n                HeaderValue::from_str(\"\").unwrap(),\n            );\n        }\n    } else {\n        let msg = \"Couldn't retrieve either Parts or ResponseOptions while \\\n                   trying to redirect().\";\n\n        #[cfg(feature = \"tracing\")]\n        tracing::warn!(\"{}\", &msg);\n\n        #[cfg(not(feature = \"tracing\"))]\n        eprintln!(\"{}\", &msg);\n    }\n}\n\n/// An Actix [struct@Route](actix_web::Route) that listens for a `POST` request with\n/// Leptos server function arguments in the body, runs the server function if found,\n/// and returns the resulting [HttpResponse].\n///\n/// This can then be set up at an appropriate route in your application:\n///\n/// ```no_run\n/// use actix_web::*;\n///\n/// fn register_server_functions() {\n///   // call ServerFn::register() for each of the server functions you've defined\n/// }\n///\n/// # #[cfg(feature = \"default\")]\n/// #[actix_web::main]\n/// async fn main() -> std::io::Result<()> {\n///     // make sure you actually register your server functions\n///     register_server_functions();\n///\n///     HttpServer::new(|| {\n///         App::new()\n///             // \"/api\" should match the prefix, if any, declared when defining server functions\n///             // {tail:.*} passes the remainder of the URL as the server function name\n///             .route(\"/api/{tail:.*}\", leptos_actix::handle_server_fns())\n///     })\n///     .bind((\"127.0.0.1\", 8080))?\n///     .run()\n///     .await\n/// }\n/// # #[cfg(not(feature = \"default\"))]\n/// # fn main() {}\n/// ```\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn handle_server_fns() -> Route {\n    handle_server_fns_with_context(|| {})\n}\n\n/// An Actix [struct@Route](actix_web::Route) that listens for `GET` or `POST` requests with\n/// Leptos server function arguments in the URL (`GET`) or body (`POST`),\n/// runs the server function if found, and returns the resulting [HttpResponse].\n///\n/// This can then be set up at an appropriate route in your application:\n///\n/// This version allows you to pass in a closure that adds additional route data to the\n/// context, allowing you to pass in info about the route or user from Actix, or other info.\n///\n/// **NOTE**: If your server functions expect a context, make sure to provide it both in\n/// [`handle_server_fns_with_context`] **and** in [`LeptosRoutes::leptos_routes_with_context`] (or whatever\n/// rendering method you are using). During SSR, server functions are called by the rendering\n/// method, while subsequent calls from the client are handled by the server function handler.\n/// The same context needs to be provided to both handlers.\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn handle_server_fns_with_context(\n    additional_context: impl Fn() + 'static + Clone + Send,\n) -> Route {\n    web::to(move |req: HttpRequest, payload: Payload| {\n        let additional_context = additional_context.clone();\n        async move {\n            let additional_context = additional_context.clone();\n\n            let path = req.path();\n            let method = req.method();\n            if let Some(mut service) =\n                server_fn::actix::get_server_fn_service(path, method)\n            {\n                let owner = Owner::new();\n                owner\n                    .with(|| {\n                        ScopedFuture::new(async move {\n                            provide_context(Request::new(&req));\n                            let res_options = ResponseOptions::default();\n                            provide_context(res_options.clone());\n                            additional_context();\n\n                            // store Accepts and Referer in case we need them for redirect (below)\n                            let accepts_html = req\n                                .headers()\n                                .get(ACCEPT)\n                                .and_then(|v| v.to_str().ok())\n                                .map(|v| v.contains(\"text/html\"))\n                                .unwrap_or(false);\n                            let referrer = req.headers().get(REFERER).cloned();\n\n                            // actually run the server fn\n                            let mut res = ActixResponse(\n                                service\n                                    .run(ActixRequest::from((req, payload)))\n                                    .await\n                                    .take(),\n                            );\n\n                            // if it accepts text/html (i.e., is a plain form post) and doesn't already have a\n                            // Location set, then redirect to the Referer\n                            if accepts_html {\n                                if let Some(referrer) = referrer {\n                                    let has_location =\n                                        res.0.headers().get(LOCATION).is_some();\n                                    if !has_location {\n                                        *res.0.status_mut() = StatusCode::FOUND;\n                                        res.0\n                                            .headers_mut()\n                                            .insert(LOCATION, referrer);\n                                    }\n                                }\n                            }\n\n                            // the Location header may have been set to Referer, so any redirection by the\n                            // user must overwrite it\n                            {\n                                let mut res_options =\n                                    res_options.0.write().or_poisoned();\n                                let headers = res.0.headers_mut();\n\n                                for location in\n                                    res_options.headers.remove(header::LOCATION)\n                                {\n                                    headers.insert(header::LOCATION, location);\n                                }\n                            }\n\n                            // apply status code and headers if user changed them\n                            res.extend_response(&res_options);\n                            res.0\n                        })\n                    })\n                    .await\n            } else {\n                HttpResponse::BadRequest().body(format!(\n                    \"Could not find a server function at the route {:?}. \\\n                     \\n\\nIt's likely that either\n                         1. The API prefix you specify in the `#[server]` \\\n                     macro doesn't match the prefix at which your server \\\n                     function handler is mounted, or \\n2. You are on a \\\n                     platform that doesn't support automatic server function \\\n                     registration and you need to call \\\n                     ServerFn::register_explicit() on the server function \\\n                     type, somewhere in your `main` function.\",\n                    req.path()\n                ))\n            }\n        }\n    })\n}\n\n/// Returns an Actix [struct@Route](actix_web::Route) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an HTML stream of your application. The stream\n/// will include fallback content for any `<Suspense/>` nodes, and be immediately interactive,\n/// but requires some client-side JavaScript.\n///\n/// This can then be set up at an appropriate route in your application:\n/// ```no_run\n/// use actix_web::{App, HttpServer};\n/// use leptos::prelude::*;\n/// use leptos_router::Method;\n/// use std::{env, net::SocketAddr};\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     view! { <main>\"Hello, world!\"</main> }\n/// }\n///\n/// # #[cfg(feature = \"default\")]\n/// #[actix_web::main]\n/// async fn main() -> std::io::Result<()> {\n///     let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n///     let addr = conf.leptos_options.site_addr.clone();\n///     HttpServer::new(move || {\n///         let leptos_options = &conf.leptos_options;\n///\n///         App::new()\n///             // {tail:.*} passes the remainder of the URL as the route\n///             // the actual routing will be handled by `leptos_router`\n///             .route(\n///                 \"/{tail:.*}\",\n///                 leptos_actix::render_app_to_stream(MyApp, Method::Get),\n///             )\n///     })\n///     .bind(&addr)?\n///     .run()\n///     .await\n/// }\n/// # #[cfg(not(feature = \"default\"))]\n/// # fn main() {}\n/// ```\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n/// - [MetaContext](leptos_meta::MetaContext)\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream<IV>(\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    method: Method,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    render_app_to_stream_with_context(|| {}, app_fn, method)\n}\n\n/// Returns an Actix [struct@Route](actix_web::Route) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an in-order HTML stream of your application.\n/// This stream will pause at each `<Suspense/>` node and wait for it to resolve before\n/// sending down its HTML. The app will become interactive once it has fully loaded.\n///\n/// This can then be set up at an appropriate route in your application:\n/// ```no_run\n/// use actix_web::{App, HttpServer};\n/// use leptos::prelude::*;\n/// use leptos_router::Method;\n/// use std::{env, net::SocketAddr};\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     view! { <main>\"Hello, world!\"</main> }\n/// }\n///\n/// # #[cfg(feature = \"default\")]\n/// #[actix_web::main]\n/// async fn main() -> std::io::Result<()> {\n///     let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n///     let addr = conf.leptos_options.site_addr.clone();\n///     HttpServer::new(move || {\n///         let leptos_options = &conf.leptos_options;\n///\n///         App::new()\n///             // {tail:.*} passes the remainder of the URL as the route\n///             // the actual routing will be handled by `leptos_router`\n///             .route(\n///                 \"/{tail:.*}\",\n///                 leptos_actix::render_app_to_stream_in_order(\n///                     MyApp,\n///                     Method::Get,\n///                 ),\n///             )\n///     })\n///     .bind(&addr)?\n///     .run()\n///     .await\n/// }\n///\n/// # #[cfg(not(feature = \"default\"))]\n/// # fn main() {}\n/// ```\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream_in_order<IV>(\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    method: Method,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    render_app_to_stream_in_order_with_context(|| {}, app_fn, method)\n}\n\n/// Returns an Actix [struct@Route](actix_web::Route) that listens for a `GET` request and tries\n/// to route it using [leptos_router], asynchronously rendering an HTML page after all\n/// `async` resources have loaded.\n///\n/// This can then be set up at an appropriate route in your application:\n/// ```no_run\n/// use actix_web::{App, HttpServer};\n/// use leptos::prelude::*;\n/// use leptos_router::Method;\n/// use std::{env, net::SocketAddr};\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     view! { <main>\"Hello, world!\"</main> }\n/// }\n///\n/// # #[cfg(feature = \"default\")]\n/// #[actix_web::main]\n/// async fn main() -> std::io::Result<()> {\n///     let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n///     let addr = conf.leptos_options.site_addr.clone();\n///     HttpServer::new(move || {\n///         let leptos_options = &conf.leptos_options;\n///\n///         App::new()\n///             // {tail:.*} passes the remainder of the URL as the route\n///             // the actual routing will be handled by `leptos_router`\n///             .route(\n///                 \"/{tail:.*}\",\n///                 leptos_actix::render_app_async(MyApp, Method::Get),\n///             )\n///     })\n///     .bind(&addr)?\n///     .run()\n///     .await\n/// }\n/// # #[cfg(not(feature = \"default\"))]\n/// # fn main() {}\n/// ```\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_async<IV>(\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    method: Method,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    render_app_async_with_context(|| {}, app_fn, method)\n}\n\n/// Returns an Actix [struct@Route] that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an HTML stream of your application.\n///\n/// This function allows you to provide additional information to Leptos for your route.\n/// It could be used to pass in Path Info, Connection Info, or anything your heart desires.\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream_with_context<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    method: Method,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    render_app_to_stream_with_context_and_replace_blocks(\n        additional_context,\n        app_fn,\n        method,\n        false,\n    )\n}\n\n/// Returns an Actix [struct@Route](actix_web::Route) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an HTML stream of your application.\n///\n/// This function allows you to provide additional information to Leptos for your route.\n/// It could be used to pass in Path Info, Connection Info, or anything your heart desires.\n///\n/// `replace_blocks` additionally lets you specify whether `<Suspense/>` fragments that read\n/// from blocking resources should be retrojected into the HTML that's initially served, rather\n/// than dynamically inserting them with JavaScript on the client. This means you will have\n/// better support if JavaScript is not enabled, in exchange for a marginally slower response time.\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream_with_context_and_replace_blocks<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    method: Method,\n    replace_blocks: bool,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    _ = replace_blocks; // TODO\n    handle_response(\n        method,\n        additional_context,\n        app_fn,\n        |app, chunks, supports_ooo| {\n            Box::pin(async move {\n                let app = if cfg!(feature = \"islands-router\") {\n                    if supports_ooo {\n                        app.to_html_stream_out_of_order_branching()\n                    } else {\n                        app.to_html_stream_in_order_branching()\n                    }\n                } else if supports_ooo {\n                    app.to_html_stream_out_of_order()\n                } else {\n                    app.to_html_stream_in_order()\n                };\n                Box::pin(app.chain(chunks())) as PinnedStream<String>\n            })\n        },\n    )\n}\n\n/// Returns an Actix [struct@Route](actix_web::Route) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an in-order HTML stream of your application.\n///\n/// This function allows you to provide additional information to Leptos for your route.\n/// It could be used to pass in Path Info, Connection Info, or anything your heart desires.\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n/// - [MetaContext](leptos_meta::MetaContext)\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream_in_order_with_context<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    method: Method,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    handle_response(\n        method,\n        additional_context,\n        app_fn,\n        |app, chunks, _supports_ooo| {\n            Box::pin(async move {\n                let app = if cfg!(feature = \"islands-router\") {\n                    app.to_html_stream_in_order_branching()\n                } else {\n                    app.to_html_stream_in_order()\n                };\n                Box::pin(app.chain(chunks())) as PinnedStream<String>\n            })\n        },\n    )\n}\n\n/// Returns an Actix [struct@Route](actix_web::Route) that listens for a `GET` request and tries\n/// to route it using [leptos_router], asynchronously serving the page once all `async`\n/// resources have loaded.\n///\n/// This function allows you to provide additional information to Leptos for your route.\n/// It could be used to pass in Path Info, Connection Info, or anything your heart desires.\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [ResponseOptions]\n/// - [Request]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_async_with_context<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    method: Method,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    handle_response(method, additional_context, app_fn, async_stream_builder)\n}\n\nfn async_stream_builder<IV>(\n    app: IV,\n    chunks: BoxedFnOnce<PinnedStream<String>>,\n    _supports_ooo: bool,\n) -> PinnedFuture<PinnedStream<String>>\nwhere\n    IV: IntoView + 'static,\n{\n    Box::pin(async move {\n        let app = if cfg!(feature = \"islands-router\") {\n            app.to_html_stream_in_order_branching()\n        } else {\n            app.to_html_stream_in_order()\n        };\n        let app = app.collect::<String>().await;\n        let chunks = chunks();\n        Box::pin(once(async move { app }).chain(chunks)) as PinnedStream<String>\n    })\n}\n\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\nfn provide_contexts(\n    req: Request,\n    meta_context: &ServerMetaContext,\n    res_options: &ResponseOptions,\n) {\n    let path = leptos_corrected_path(&req);\n\n    provide_context(RequestUrl::new(&path));\n    provide_context(meta_context.clone());\n    provide_context(res_options.clone());\n    provide_context(req);\n    provide_server_redirect(redirect);\n    leptos::nonce::provide_nonce();\n}\n\nfn leptos_corrected_path(req: &HttpRequest) -> String {\n    let path = req.path();\n    let query = req.query_string();\n    if query.is_empty() {\n        \"http://leptos\".to_string() + path\n    } else {\n        \"http://leptos\".to_string() + path + \"?\" + query\n    }\n}\n\n#[allow(clippy::type_complexity)]\nfn handle_response<IV>(\n    method: Method,\n    additional_context: impl Fn() + 'static + Clone + Send,\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    stream_builder: fn(\n        IV,\n        BoxedFnOnce<PinnedStream<String>>,\n        bool,\n    ) -> PinnedFuture<PinnedStream<String>>,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    let handler = move |req: HttpRequest| {\n        let app_fn = app_fn.clone();\n        let add_context = additional_context.clone();\n\n        async move {\n            let is_island_router_navigation = cfg!(feature = \"islands-router\")\n                && req.headers().get(\"Islands-Router\").is_some();\n\n            let res_options = ResponseOptions::default();\n            let (meta_context, meta_output) = ServerMetaContext::new();\n\n            let additional_context = {\n                let meta_context = meta_context.clone();\n                let res_options = res_options.clone();\n                let req = Request::new(&req);\n                move || {\n                    provide_contexts(req, &meta_context, &res_options);\n                    add_context();\n\n                    if is_island_router_navigation {\n                        provide_context(IslandsRouterNavigation);\n                    }\n                }\n            };\n\n            let res = ActixResponse::from_app(\n                app_fn,\n                meta_output,\n                additional_context,\n                res_options,\n                stream_builder,\n                !is_island_router_navigation,\n            )\n            .await;\n\n            res.0\n        }\n    };\n    match method {\n        Method::Get => web::get().to(handler),\n        Method::Post => web::post().to(handler),\n        Method::Put => web::put().to(handler),\n        Method::Delete => web::delete().to(handler),\n        Method::Patch => web::patch().to(handler),\n    }\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Actix's App without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generated Actix compatible paths.\npub fn generate_route_list<IV>(\n    app_fn: impl Fn() -> IV + 'static + Send + Clone,\n) -> Vec<ActixRouteListing>\nwhere\n    IV: IntoView + 'static,\n{\n    generate_route_list_with_exclusions_and_ssg(app_fn, None).0\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Actix's App without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generated Actix compatible paths.\npub fn generate_route_list_with_ssg<IV>(\n    app_fn: impl Fn() -> IV + 'static + Send + Clone,\n) -> (Vec<ActixRouteListing>, StaticRouteGenerator)\nwhere\n    IV: IntoView + 'static,\n{\n    generate_route_list_with_exclusions_and_ssg(app_fn, None)\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Actix's App without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generated Actix compatible paths. Adding excluded_routes\n/// to this function will stop `.leptos_routes()` from generating a route for it, allowing a custom handler. These need to be in Actix path format\npub fn generate_route_list_with_exclusions<IV>(\n    app_fn: impl Fn() -> IV + 'static + Send + Clone,\n    excluded_routes: Option<Vec<String>>,\n) -> Vec<ActixRouteListing>\nwhere\n    IV: IntoView + 'static,\n{\n    generate_route_list_with_exclusions_and_ssg(app_fn, excluded_routes).0\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Actix's App without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generated Actix compatible paths. Adding excluded_routes\n/// to this function will stop `.leptos_routes()` from generating a route for it, allowing a custom handler. These need to be in Actix path format\npub fn generate_route_list_with_exclusions_and_ssg<IV>(\n    app_fn: impl Fn() -> IV + 'static + Send + Clone,\n    excluded_routes: Option<Vec<String>>,\n) -> (Vec<ActixRouteListing>, StaticRouteGenerator)\nwhere\n    IV: IntoView + 'static,\n{\n    generate_route_list_with_exclusions_and_ssg_and_context(\n        app_fn,\n        excluded_routes,\n        || {},\n    )\n}\n\ntrait ActixPath {\n    fn to_actix_path(&self) -> String;\n}\n\nimpl ActixPath for Vec<PathSegment> {\n    fn to_actix_path(&self) -> String {\n        let mut path = String::new();\n        for segment in self.iter() {\n            // TODO trailing slash handling\n            let raw = segment.as_raw_str();\n            if !raw.is_empty() && !raw.starts_with('/') {\n                path.push('/');\n            }\n            match segment {\n                PathSegment::Static(s) => path.push_str(s),\n                PathSegment::Param(s) => {\n                    path.push('{');\n                    path.push_str(s);\n                    path.push('}');\n                }\n                PathSegment::Splat(s) => {\n                    path.push('{');\n                    path.push_str(s);\n                    path.push_str(\":.*}\");\n                }\n                PathSegment::Unit => {}\n                PathSegment::OptionalParam(_) => {\n                    #[cfg(feature = \"tracing\")]\n                    tracing::error!(\n                        \"to_axum_path should only be called on expanded \\\n                         paths, which do not have OptionalParam any longer\"\n                    );\n                    Default::default()\n                }\n            }\n        }\n        path\n    }\n}\n\n#[derive(Clone, Debug, Default)]\n/// A route that this application can serve.\npub struct ActixRouteListing {\n    path: String,\n    mode: SsrMode,\n    methods: Vec<leptos_router::Method>,\n    regenerate: Vec<RegenerationFn>,\n    exclude: bool,\n}\n\ntrait IntoRouteListing: Sized {\n    fn into_route_listing(self) -> Vec<ActixRouteListing>;\n}\n\nimpl IntoRouteListing for RouteListing {\n    fn into_route_listing(self) -> Vec<ActixRouteListing> {\n        self.path()\n            .to_vec()\n            .expand_optionals()\n            .into_iter()\n            .map(|path| {\n                let path = path.to_actix_path();\n                let path = if path.is_empty() {\n                    \"/\".to_string()\n                } else {\n                    path\n                };\n                let mode = self.mode();\n                let methods = self.methods().collect();\n                let regenerate = self.regenerate().into();\n                ActixRouteListing {\n                    path,\n                    mode: mode.clone(),\n                    methods,\n                    regenerate,\n                    exclude: false,\n                }\n            })\n            .collect()\n    }\n}\n\nimpl ActixRouteListing {\n    /// Create a route listing from its parts.\n    pub fn new(\n        path: String,\n        mode: SsrMode,\n        methods: impl IntoIterator<Item = leptos_router::Method>,\n        regenerate: impl Into<Vec<RegenerationFn>>,\n    ) -> Self {\n        Self {\n            path,\n            mode,\n            methods: methods.into_iter().collect(),\n            regenerate: regenerate.into(),\n            exclude: false,\n        }\n    }\n\n    /// The path this route handles.\n    pub fn path(&self) -> &str {\n        &self.path\n    }\n\n    /// The rendering mode for this path.\n    pub fn mode(&self) -> SsrMode {\n        self.mode.clone()\n    }\n\n    /// The HTTP request methods this path can handle.\n    pub fn methods(&self) -> impl Iterator<Item = leptos_router::Method> + '_ {\n        self.methods.iter().copied()\n    }\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Actix's App without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generated Actix compatible paths. Adding excluded_routes\n/// to this function will stop `.leptos_routes()` from generating a route for it, allowing a custom handler. These need to be in Actix path format.\n/// Additional context will be provided to the app Element.\npub fn generate_route_list_with_exclusions_and_ssg_and_context<IV>(\n    app_fn: impl Fn() -> IV + 'static + Send + Clone,\n    excluded_routes: Option<Vec<String>>,\n    additional_context: impl Fn() + 'static + Send + Clone,\n) -> (Vec<ActixRouteListing>, StaticRouteGenerator)\nwhere\n    IV: IntoView + 'static,\n{\n    let _ = any_spawner::Executor::init_tokio();\n\n    let owner = Owner::new_root(Some(Arc::new(SsrSharedContext::new())));\n    let (mock_meta, _) = ServerMetaContext::new();\n    let routes = owner\n        .with(|| {\n            // stub out a path for now\n            provide_context(RequestUrl::new(\"\"));\n            provide_context(ResponseOptions::default());\n            provide_context(mock_meta);\n            additional_context();\n            RouteList::generate(&app_fn)\n        })\n        .unwrap_or_default();\n\n    let generator = StaticRouteGenerator::new(\n        &routes,\n        app_fn.clone(),\n        additional_context.clone(),\n    );\n\n    // Axum's Router defines Root routes as \"/\" not \"\"\n    let mut routes = routes\n        .into_inner()\n        .into_iter()\n        .flat_map(IntoRouteListing::into_route_listing)\n        .collect::<Vec<_>>();\n\n    let routes = if routes.is_empty() {\n        vec![ActixRouteListing::new(\n            \"/\".to_string(),\n            Default::default(),\n            [leptos_router::Method::Get],\n            vec![],\n        )]\n    } else {\n        // Routes to exclude from auto generation\n        if let Some(excluded_routes) = &excluded_routes {\n            routes.retain(|p| !excluded_routes.iter().any(|e| e == p.path()))\n        }\n        routes\n    };\n\n    let excluded =\n        excluded_routes\n            .into_iter()\n            .flatten()\n            .map(|path| ActixRouteListing {\n                path,\n                mode: Default::default(),\n                methods: Vec::new(),\n                regenerate: Vec::new(),\n                exclude: true,\n            });\n\n    (routes.into_iter().chain(excluded).collect(), generator)\n}\n\n/// Allows generating any prerendered routes.\n#[allow(clippy::type_complexity)]\npub struct StaticRouteGenerator(\n    // this is here to keep the root owner alive for the duration\n    // of the route generation, so that base context provided continues\n    // to exist until it is dropped\n    #[allow(dead_code)] Owner,\n    Box<dyn FnOnce(&LeptosOptions) -> PinnedFuture<()> + Send>,\n);\n\nimpl StaticRouteGenerator {\n    fn render_route<IV: IntoView + 'static>(\n        path: String,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n        additional_context: impl Fn() + Clone + Send + 'static,\n    ) -> impl Future<Output = (Owner, String)> {\n        let (meta_context, meta_output) = ServerMetaContext::new();\n        let additional_context = {\n            let add_context = additional_context.clone();\n            move || {\n                let mock_req = test::TestRequest::with_uri(&path)\n                    .insert_header((\"Accept\", \"text/html\"))\n                    .to_http_request();\n                let res_options = ResponseOptions::default();\n                provide_contexts(\n                    Request::new(&mock_req),\n                    &meta_context,\n                    &res_options,\n                );\n                add_context();\n            }\n        };\n\n        let (owner, stream) = leptos_integration_utils::build_response(\n            app_fn.clone(),\n            additional_context,\n            async_stream_builder,\n            false,\n        );\n\n        let sc = owner.shared_context().unwrap();\n\n        async move {\n            let stream = stream.await;\n            while let Some(pending) = sc.await_deferred() {\n                pending.await;\n            }\n\n            let html = meta_output\n                .inject_meta_context(stream)\n                .await\n                .collect::<String>()\n                .await;\n            (owner, html)\n        }\n    }\n\n    /// Creates a new static route generator from the given list of route definitions.\n    pub fn new<IV>(\n        routes: &RouteList,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n        additional_context: impl Fn() + Clone + Send + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static,\n    {\n        let owner = Owner::new();\n        Self(owner.clone(), {\n            let routes = routes.clone();\n            Box::new(move |options| {\n                let options = options.clone();\n                let app_fn = app_fn.clone();\n                let additional_context = additional_context.clone();\n\n                owner.with(|| {\n                    additional_context();\n                    Box::pin(ScopedFuture::new(routes.generate_static_files(\n                        move |path: &ResolvedStaticPath| {\n                            Self::render_route(\n                                path.to_string(),\n                                app_fn.clone(),\n                                additional_context.clone(),\n                            )\n                        },\n                        move |path: &ResolvedStaticPath,\n                              owner: &Owner,\n                              html: String| {\n                            let options = options.clone();\n                            let path = path.to_owned();\n                            let response_options = owner.with(use_context);\n                            async move {\n                                write_static_route(\n                                    &options,\n                                    response_options,\n                                    path.as_ref(),\n                                    &html,\n                                )\n                                .await\n                            }\n                        },\n                        was_404,\n                    )))\n                })\n            })\n        })\n    }\n\n    /// Generates the routes.\n    pub async fn generate(self, options: &LeptosOptions) {\n        (self.1)(options).await\n    }\n}\n\nstatic STATIC_HEADERS: LazyLock<\n    std::sync::RwLock<HashMap<String, ResponseOptions>>,\n> = LazyLock::new(Default::default);\n\nfn was_404(owner: &Owner) -> bool {\n    let resp = owner.with(|| expect_context::<ResponseOptions>());\n    let status = resp.0.read().or_poisoned().status;\n\n    if let Some(status) = status {\n        return status == StatusCode::NOT_FOUND;\n    }\n\n    false\n}\n\nfn static_path(options: &LeptosOptions, path: &str) -> String {\n    use leptos_integration_utils::static_file_path;\n\n    // If the path ends with a trailing slash, we generate the path\n    // as a directory with a index.html file inside.\n    if path != \"/\" && path.ends_with(\"/\") {\n        static_file_path(options, &format!(\"{path}index\"))\n    } else {\n        static_file_path(options, path)\n    }\n}\n\nasync fn write_static_route(\n    options: &LeptosOptions,\n    response_options: Option<ResponseOptions>,\n    path: &str,\n    html: &str,\n) -> Result<(), std::io::Error> {\n    if let Some(options) = response_options {\n        STATIC_HEADERS\n            .write()\n            .or_poisoned()\n            .insert(path.to_string(), options);\n    }\n\n    let path = static_path(options, path);\n    let path = Path::new(&path);\n    if let Some(path) = path.parent() {\n        tokio::fs::create_dir_all(path).await?;\n    }\n    tokio::fs::write(path, &html).await?;\n\n    Ok(())\n}\n\nfn handle_static_route<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    regenerate: Vec<RegenerationFn>,\n) -> Route\nwhere\n    IV: IntoView + 'static,\n{\n    let handler = move |req: HttpRequest, data: Data<LeptosOptions>| {\n        Box::pin({\n            let app_fn = app_fn.clone();\n            let additional_context = additional_context.clone();\n            let regenerate = regenerate.clone();\n            async move {\n                let options = data.into_inner();\n                let orig_path = req.uri().path();\n                let path = static_path(&options, orig_path);\n                let path = Path::new(&path);\n                let exists = tokio::fs::try_exists(path).await.unwrap_or(false);\n\n                let (response_options, html) = if !exists {\n                    let path = ResolvedStaticPath::new(orig_path);\n\n                    let (owner, html) = path\n                        .build(\n                            move |path: &ResolvedStaticPath| {\n                                StaticRouteGenerator::render_route(\n                                    path.to_string(),\n                                    app_fn.clone(),\n                                    additional_context.clone(),\n                                )\n                            },\n                            move |path: &ResolvedStaticPath,\n                                  owner: &Owner,\n                                  html: String| {\n                                let options = options.clone();\n                                let path = path.to_owned();\n                                let response_options = owner.with(use_context);\n                                async move {\n                                    write_static_route(\n                                        &options,\n                                        response_options,\n                                        path.as_ref(),\n                                        &html,\n                                    )\n                                    .await\n                                }\n                            },\n                            was_404,\n                            regenerate,\n                        )\n                        .await;\n                    (owner.with(use_context::<ResponseOptions>), html)\n                } else {\n                    let headers = STATIC_HEADERS\n                        .read()\n                        .or_poisoned()\n                        .get(orig_path)\n                        .cloned();\n                    (headers, None)\n                };\n\n                // if html is Some(_), it means that `was_error_response` is true and we're not\n                // actually going to cache this route, just return it as HTML\n                //\n                // this if for thing like 404s, where we do not want to cache an endless series of\n                // typos (or malicious requests)\n                let mut res = ActixResponse(match html {\n                    Some(html) => {\n                        HttpResponse::Ok().content_type(\"text/html\").body(html)\n                    }\n                    None => match NamedFile::open(path) {\n                        Ok(res) => res.into_response(&req),\n                        Err(err) => HttpResponse::InternalServerError()\n                            .body(err.to_string()),\n                    },\n                });\n\n                if let Some(options) = response_options {\n                    res.extend_response(&options);\n                }\n\n                res.0\n            }\n        })\n    };\n    web::get().to(handler)\n}\n\n/// This trait allows one to pass a list of routes and a render function to Actix's router, letting us avoid\n/// having to use wildcards or manually define all routes in multiple places.\npub trait LeptosRoutes {\n    /// Adds routes to the Axum router that have either\n    /// 1) been generated by `leptos_router`, or\n    /// 2) handle a server function.\n    fn leptos_routes<IV>(\n        self,\n        paths: Vec<ActixRouteListing>,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static;\n\n    /// Adds routes to the Axum router that have either\n    /// 1) been generated by `leptos_router`, or\n    /// 2) handle a server function.\n    ///\n    /// Runs `additional_context` to provide additional data to the reactive system via context,\n    /// when handling a route.\n    fn leptos_routes_with_context<IV>(\n        self,\n        paths: Vec<ActixRouteListing>,\n        additional_context: impl Fn() + 'static + Clone + Send,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static;\n}\n\n/// The default implementation of `LeptosRoutes` which takes in a list of paths, and dispatches GET requests\n/// to those paths to Leptos's renderer.\nimpl<T> LeptosRoutes for actix_web::App<T>\nwhere\n    T: ServiceFactory<\n        ServiceRequest,\n        Config = (),\n        Error = Error,\n        InitError = (),\n    >,\n{\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", fields(error), skip_all)\n    )]\n    fn leptos_routes<IV>(\n        self,\n        paths: Vec<ActixRouteListing>,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static,\n    {\n        self.leptos_routes_with_context(paths, || {}, app_fn)\n    }\n\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", fields(error), skip_all)\n    )]\n    fn leptos_routes_with_context<IV>(\n        self,\n        paths: Vec<ActixRouteListing>,\n        additional_context: impl Fn() + 'static + Clone + Send,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static,\n    {\n        let mut router = self;\n\n        let excluded = paths\n            .iter()\n            .filter(|&p| p.exclude)\n            .map(|p| p.path.as_str())\n            .collect::<HashSet<_>>();\n\n        // register server functions first to allow for wildcard route in Leptos's Router\n        for (path, _) in server_fn::actix::server_fn_paths() {\n            if !excluded.contains(path) {\n                let additional_context = additional_context.clone();\n                let handler =\n                    handle_server_fns_with_context(additional_context);\n                router = router.route(path, handler);\n            }\n        }\n\n        // register routes defined in Leptos's Router\n        for listing in paths.iter().filter(|p| !p.exclude) {\n            let path = listing.path();\n            let mode = listing.mode();\n\n            for method in listing.methods() {\n                let additional_context = additional_context.clone();\n                let additional_context_and_method = move || {\n                    provide_context(method);\n                    additional_context();\n                };\n                router = if matches!(listing.mode(), SsrMode::Static(_)) {\n                    router.route(\n                        path,\n                        handle_static_route(\n                            additional_context_and_method.clone(),\n                            app_fn.clone(),\n                            listing.regenerate.clone(),\n                        ),\n                    )\n                } else {\n                    router\n                        .route(path, web::head().to(HttpResponse::Ok))\n                        .route(\n                            path,\n                            match mode {\n                                SsrMode::OutOfOrder => {\n                                    render_app_to_stream_with_context(\n                                        additional_context_and_method.clone(),\n                                        app_fn.clone(),\n                                        method,\n                                    )\n                                }\n                                SsrMode::PartiallyBlocked => {\n                                    render_app_to_stream_with_context_and_replace_blocks(\n                                        additional_context_and_method.clone(),\n                                        app_fn.clone(),\n                                        method,\n                                        true,\n                                    )\n                                }\n                                SsrMode::InOrder => {\n                                    render_app_to_stream_in_order_with_context(\n                                        additional_context_and_method.clone(),\n                                        app_fn.clone(),\n                                        method,\n                                    )\n                                }\n                                SsrMode::Async => render_app_async_with_context(\n                                    additional_context_and_method.clone(),\n                                    app_fn.clone(),\n                                    method,\n                                ),\n                                _ => unreachable!()\n                            },\n                        )\n                };\n            }\n        }\n\n        router\n    }\n}\n\n/// The default implementation of `LeptosRoutes` which takes in a list of paths, and dispatches GET requests\n/// to those paths to Leptos's renderer.\nimpl LeptosRoutes for &mut ServiceConfig {\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", fields(error), skip_all)\n    )]\n    fn leptos_routes<IV>(\n        self,\n        paths: Vec<ActixRouteListing>,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static,\n    {\n        self.leptos_routes_with_context(paths, || {}, app_fn)\n    }\n\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", fields(error), skip_all)\n    )]\n    fn leptos_routes_with_context<IV>(\n        self,\n        paths: Vec<ActixRouteListing>,\n        additional_context: impl Fn() + 'static + Clone + Send,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static,\n    {\n        let mut router = self;\n\n        let excluded = paths\n            .iter()\n            .filter(|&p| p.exclude)\n            .map(|p| p.path.as_str())\n            .collect::<HashSet<_>>();\n\n        // register server functions first to allow for wildcard route in Leptos's Router\n        for (path, _) in server_fn::actix::server_fn_paths() {\n            if !excluded.contains(path) {\n                let additional_context = additional_context.clone();\n                let handler =\n                    handle_server_fns_with_context(additional_context);\n                router = router.route(path, handler);\n            }\n        }\n\n        // register routes defined in Leptos's Router\n        for listing in paths.iter().filter(|p| !p.exclude) {\n            let path = listing.path();\n            let mode = listing.mode();\n\n            for method in listing.methods() {\n                if matches!(listing.mode(), SsrMode::Static(_)) {\n                    router = router.route(\n                        path,\n                        handle_static_route(\n                            additional_context.clone(),\n                            app_fn.clone(),\n                            listing.regenerate.clone(),\n                        ),\n                    )\n                } else {\n                    router = router.route(\n                            path,\n                            match mode {\n                                SsrMode::OutOfOrder => {\n                                    render_app_to_stream_with_context(\n                                        additional_context.clone(),\n                                        app_fn.clone(),\n                                        method,\n                                    )\n                                }\n                                SsrMode::PartiallyBlocked => {\n                                    render_app_to_stream_with_context_and_replace_blocks(\n                                        additional_context.clone(),\n                                        app_fn.clone(),\n                                        method,\n                                        true,\n                                    )\n                                }\n                                SsrMode::InOrder => {\n                                    render_app_to_stream_in_order_with_context(\n                                        additional_context.clone(),\n                                        app_fn.clone(),\n                                        method,\n                                    )\n                                }\n                                SsrMode::Async => render_app_async_with_context(\n                                    additional_context.clone(),\n                                    app_fn.clone(),\n                                    method,\n                                ),\n                                _ => unreachable!()\n                            },\n                        );\n                }\n            }\n        }\n\n        router\n    }\n}\n\n/// A helper to make it easier to use Actix extractors in server functions.\n///\n/// It is generic over some type `T` that implements [`FromRequest`] and can\n/// therefore be used in an extractor. The compiler can often infer this type.\n///\n/// Any error that occurs during extraction is converted to a [`ServerFnError`].\n///\n/// ```rust\n/// use leptos::prelude::*;\n///\n/// #[server]\n/// pub async fn extract_connection_info() -> Result<String, ServerFnError> {\n///     use actix_web::dev::ConnectionInfo;\n///     use leptos_actix::*;\n///\n///     // this can be any type you can use an Actix extractor with, as long as\n///     // it works on the head, not the body of the request\n///     let info: ConnectionInfo = extract().await?;\n///\n///     // do something with the data\n///\n///     Ok(format!(\"{info:?}\"))\n/// }\n/// ```\npub async fn extract<T>() -> Result<T, ServerFnErrorErr>\nwhere\n    T: actix_web::FromRequest,\n    <T as FromRequest>::Error: Display,\n{\n    let req = use_context::<Request>().ok_or_else(|| {\n        ServerFnErrorErr::ServerError(\n            \"HttpRequest should have been provided via context\".to_string(),\n        )\n    })?;\n\n    SendWrapper::new(async move {\n        T::extract(&req)\n            .await\n            .map_err(|e| ServerFnErrorErr::ServerError(e.to_string()))\n    })\n    .await\n}\n"
  },
  {
    "path": "integrations/actix/tests/extract_routes.rs",
    "content": "// TODO these tests relate to trailing-slash logic, which is still TBD for 0.7\n\n// use leptos::*;\n// use leptos_actix::generate_route_list;\n// use leptos_router::{\n//     components::{Route, Router, Routes},\n//     path,\n// };\n//\n// #[component]\n// fn DefaultApp() -> impl IntoView {\n//     let view = || view! { \"\" };\n//     view! {\n//         <Router>\n//             <Routes>\n//                 <Route path=path!(\"/foo\") view/>\n//                 <Route path=path!(\"/bar/\") view/>\n//                 <Route path=path!(\"/baz/:id\") view/>\n//                 <Route path=path!(\"/baz/:name/\") view/>\n//                 <Route path=path!(\"/baz/*any\") view/>\n//             </Routes>\n//         </Router>\n//     }\n// }\n//\n// #[test]\n// fn test_default_app() {\n//     let routes = generate_route_list(DefaultApp);\n//\n//     // We still have access to the original (albeit normalized) Leptos paths:\n//     assert_same(\n//         &routes,\n//         |r| r.leptos_path(),\n//         &[\"/bar\", \"/baz/*any\", \"/baz/:id\", \"/baz/:name\", \"/foo\"],\n//     );\n//\n//     // ... But leptos-actix has also reformatted \"paths\" to work for Actix.\n//     assert_same(\n//         &routes,\n//         |r| r.path(),\n//         &[\"/bar\", \"/baz/{id}\", \"/baz/{name}\", \"/baz/{tail:.*}\", \"/foo\"],\n//     );\n// }\n//\n// #[component]\n// fn ExactApp() -> impl IntoView {\n//     let view = || view! { \"\" };\n//     //let trailing_slash = TrailingSlash::Exact;\n//     view! {\n//         <Router>\n//             <Routes>\n//                 <Route path=path!(\"/foo\") view/>\n//                 <Route path=path!(\"/bar/\") view/>\n//                 <Route path=path!(\"/baz/:id\") view/>\n//                 <Route path=path!(\"/baz/:name/\") view/>\n//                 <Route path=path!(\"/baz/*any\") view/>\n//             </Routes>\n//         </Router>\n//     }\n// }\n//\n// #[test]\n// fn test_exact_app() {\n//     let routes = generate_route_list(ExactApp);\n//\n//     // In Exact mode, the Leptos paths no longer have their trailing slashes stripped:\n//     assert_same(\n//         &routes,\n//         |r| r.leptos_path(),\n//         &[\"/bar/\", \"/baz/*any\", \"/baz/:id\", \"/baz/:name/\", \"/foo\"],\n//     );\n//\n//     // Actix paths also have trailing slashes as a result:\n//     assert_same(\n//         &routes,\n//         |r| r.path(),\n//         &[\n//             \"/bar/\",\n//             \"/baz/{id}\",\n//             \"/baz/{name}/\",\n//             \"/baz/{tail:.*}\",\n//             \"/foo\",\n//         ],\n//     );\n// }\n//\n// #[component]\n// fn RedirectApp() -> impl IntoView {\n//     let view = || view! { \"\" };\n//     //let trailing_slash = TrailingSlash::Redirect;\n//     view! {\n//         <Router>\n//             <Routes>\n//                 <Route path=path!(\"/foo\") view/>\n//                 <Route path=path!(\"/bar/\") view/>\n//                 <Route path=path!(\"/baz/:id\") view/>\n//                 <Route path=path!(\"/baz/:name/\") view/>\n//                 <Route path=path!(\"/baz/*any\") view/>\n//             </Routes>\n//         </Router>\n//     }\n// }\n//\n// #[test]\n// fn test_redirect_app() {\n//     let routes = generate_route_list(RedirectApp);\n//\n//     assert_same(\n//         &routes,\n//         |r| r.leptos_path(),\n//         &[\n//             \"/bar\",\n//             \"/bar/\",\n//             \"/baz/*any\",\n//             \"/baz/:id\",\n//             \"/baz/:id/\",\n//             \"/baz/:name\",\n//             \"/baz/:name/\",\n//             \"/foo\",\n//             \"/foo/\",\n//         ],\n//     );\n//\n//     // ... But leptos-actix has also reformatted \"paths\" to work for Actix.\n//     assert_same(\n//         &routes,\n//         |r| r.path(),\n//         &[\n//             \"/bar\",\n//             \"/bar/\",\n//             \"/baz/{id}\",\n//             \"/baz/{id}/\",\n//             \"/baz/{name}\",\n//             \"/baz/{name}/\",\n//             \"/baz/{tail:.*}\",\n//             \"/foo\",\n//             \"/foo/\",\n//         ],\n//     );\n// }\n//\n// fn assert_same<'t, T, F, U>(\n//     input: &'t Vec<T>,\n//     mapper: F,\n//     expected_sorted_values: &[U],\n// ) where\n//     F: Fn(&'t T) -> U + 't,\n//     U: Ord + std::fmt::Debug,\n// {\n//     let mut values: Vec<U> = input.iter().map(mapper).collect();\n//     values.sort();\n//     assert_eq!(values, expected_sorted_values);\n// }\n"
  },
  {
    "path": "integrations/axum/Cargo.toml",
    "content": "[package]\nname = \"leptos_axum\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Axum integrations for the Leptos web framework.\"\nversion = \"0.8.8\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nany_spawner = { workspace = true, features = [\"tokio\"] }\nhydration_context = { workspace = true }\naxum = { default-features = false, features = [\n  \"matched-path\",\n], workspace = true }\nfutures = { workspace = true, default-features = true }\nleptos = { workspace = true, features = [\"nonce\", \"ssr\"] }\nserver_fn = { workspace = true, features = [\"axum-no-default\"] }\nleptos_macro = { workspace = true, features = [\"axum\"] }\nleptos_meta = { workspace = true, features = [\"ssr\", \"nonce\"] }\nleptos_router = { workspace = true, features = [\"ssr\"] }\nleptos_integration_utils = { workspace = true }\ntachys = { workspace = true }\ntokio = { default-features = false, workspace = true }\ntower = { features = [\"util\"], workspace = true, default-features = true }\ntower-http = { workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\nor_poisoned = { workspace = true, default-features = true }\n\n[dev-dependencies]\nanyhow = { workspace = true }\naxum = { workspace = true, default-features = true }\nreqwest = { workspace = true }\ntempfile = { workspace = true, default-features = true }\ntokio = { features = [\n  \"io-util\",\n  \"net\",\n  \"process\",\n  \"rt-multi-thread\",\n  \"time\",\n], workspace = true, default-features = true }\n\n[features]\nwasm = []\ndefault = [\n  \"tokio/fs\",\n  \"tokio/sync\",\n  \"tower-http/fs\",\n  \"tower/util\",\n  \"server_fn/axum\",\n]\nislands-router = [\"tachys/islands\"]\ntracing = [\"dep:tracing\"]\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\"]\nextra_features = [\"default\"]\nmax_combination_size = 2\n"
  },
  {
    "path": "integrations/axum/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../../\" }\n"
  },
  {
    "path": "integrations/axum/src/lib.rs",
    "content": "#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n#![allow(clippy::type_complexity)]\n\n//! Provides functions to easily integrate Leptos with Axum.\n//!\n//! ## JS Fetch Integration\n//! The `leptos_axum` integration supports running in JavaScript-hosted WebAssembly\n//! runtimes, e.g., running inside Deno, Cloudflare Workers, or other JS environments.\n//! To run in this environment, you need to disable the default feature set and enable\n//! the `wasm` feature on `leptos_axum` in your `Cargo.toml`.\n//! ```toml\n//! leptos_axum = { version = \"0.6.0\", default-features = false, features = [\"wasm\"] }\n//! ```\n//!\n//! ## Features\n//! - `default`: supports running in a typical native Tokio/Axum environment\n//! - `wasm`: with `default-features = false`, supports running in a JS Fetch-based\n//!   environment\n//!\n//! ### Important Note\n//! Prior to 0.5, using `default-features = false` on `leptos_axum` simply did nothing. Now, it actively\n//! disables features necessary to support the normal native/Tokio runtime environment we create. This can\n//! generate errors like the following, which don’t point to an obvious culprit:\n//! `\n//! `spawn_local` called from outside of a `task::LocalSet`\n//! `\n//! If you are not using the `wasm` feature, do not set `default-features = false` on this package.\n//!\n//!\n//! ## More information\n//!\n//! For more details on how to use the integrations, see the\n//! [`examples`](https://github.com/leptos-rs/leptos/tree/main/examples)\n//! directory in the Leptos repository.\n\n#[cfg(feature = \"default\")]\nuse axum::http::Uri;\nuse axum::{\n    body::{Body, Bytes},\n    extract::{FromRef, FromRequestParts, MatchedPath, State},\n    http::{\n        header::{self, HeaderName, HeaderValue, ACCEPT, LOCATION, REFERER},\n        request::Parts,\n        HeaderMap, Method, Request, Response, StatusCode,\n    },\n    response::IntoResponse,\n    routing::{delete, get, patch, post, put},\n};\nuse futures::{stream::once, Future, Stream, StreamExt};\nuse hydration_context::SsrSharedContext;\nuse leptos::{\n    config::LeptosOptions,\n    context::{provide_context, use_context},\n    prelude::*,\n    reactive::{computed::ScopedFuture, owner::Owner},\n    IntoView,\n};\nuse leptos_integration_utils::{\n    BoxedFnOnce, ExtendResponse, PinnedFuture, PinnedStream,\n};\nuse leptos_meta::ServerMetaContext;\n#[cfg(feature = \"default\")]\nuse leptos_router::static_routes::ResolvedStaticPath;\nuse leptos_router::{\n    components::provide_server_redirect, location::RequestUrl,\n    static_routes::RegenerationFn, ExpandOptionals, PathSegment, RouteList,\n    RouteListing, SsrMode,\n};\nuse or_poisoned::OrPoisoned;\nuse server_fn::{error::ServerFnErrorErr, redirect::REDIRECT_HEADER};\n#[cfg(feature = \"default\")]\nuse std::sync::LazyLock;\n#[cfg(feature = \"default\")]\nuse std::{collections::HashMap, path::Path};\nuse std::{\n    collections::HashSet,\n    fmt::Debug,\n    io,\n    pin::Pin,\n    sync::{Arc, RwLock},\n};\n#[cfg(feature = \"default\")]\nuse tower::util::ServiceExt;\n#[cfg(feature = \"default\")]\nuse tower_http::services::ServeDir;\n// use tracing::Instrument; // TODO check tracing span -- was this used in 0.6 for a missing link?\n\n#[cfg(feature = \"default\")]\nmod service;\n#[cfg(feature = \"default\")]\npub use service::ErrorHandler;\n\n/// This struct lets you define headers and override the status of the Response from an Element or a Server Function\n/// Typically contained inside of a ResponseOptions. Setting this is useful for cookies and custom responses.\n#[derive(Debug, Clone, Default)]\npub struct ResponseParts {\n    /// If provided, this will overwrite any other status code for this response.\n    pub status: Option<StatusCode>,\n    /// The map of headers that should be added to the response.\n    pub headers: HeaderMap,\n}\n\nimpl ResponseParts {\n    /// Insert a header, overwriting any previous value with the same key\n    pub fn insert_header(&mut self, key: HeaderName, value: HeaderValue) {\n        self.headers.insert(key, value);\n    }\n    /// Append a header, leaving any header with the same key intact\n    pub fn append_header(&mut self, key: HeaderName, value: HeaderValue) {\n        self.headers.append(key, value);\n    }\n}\n\n/// Allows you to override details of the HTTP response like the status code and add Headers/Cookies.\n///\n/// `ResponseOptions` is provided via context when you use most of the handlers provided in this\n/// crate, including [`.leptos_routes`](LeptosRoutes::leptos_routes),\n/// [`.leptos_routes_with_context`](LeptosRoutes::leptos_routes_with_context), [`handle_server_fns`], etc.\n/// You can find the full set of provided context types in each handler function.\n///\n/// If you provide your own handler, you will need to provide `ResponseOptions` via context\n/// yourself if you want to access it via context.\n/// ```\n/// use leptos::prelude::*;\n///\n/// #[server]\n/// pub async fn get_opts() -> Result<(), ServerFnError> {\n///     let opts = expect_context::<leptos_axum::ResponseOptions>();\n///     Ok(())\n/// }\n#[derive(Debug, Clone, Default)]\npub struct ResponseOptions(pub Arc<RwLock<ResponseParts>>);\n\nimpl ResponseOptions {\n    /// A simpler way to overwrite the contents of `ResponseOptions` with a new `ResponseParts`.\n    pub fn overwrite(&self, parts: ResponseParts) {\n        let mut writable = self.0.write().or_poisoned();\n        *writable = parts\n    }\n    /// Set the status of the returned Response.\n    pub fn set_status(&self, status: StatusCode) {\n        let mut writeable = self.0.write().or_poisoned();\n        let res_parts = &mut *writeable;\n        res_parts.status = Some(status);\n    }\n    /// Insert a header, overwriting any previous value with the same key.\n    pub fn insert_header(&self, key: HeaderName, value: HeaderValue) {\n        let mut writeable = self.0.write().or_poisoned();\n        let res_parts = &mut *writeable;\n        res_parts.headers.insert(key, value);\n    }\n    /// Append a header, leaving any header with the same key intact.\n    pub fn append_header(&self, key: HeaderName, value: HeaderValue) {\n        let mut writeable = self.0.write().or_poisoned();\n        let res_parts = &mut *writeable;\n        res_parts.headers.append(key, value);\n    }\n}\n\nstruct AxumResponse(Response<Body>);\n\nimpl ExtendResponse for AxumResponse {\n    type ResponseOptions = ResponseOptions;\n\n    fn from_stream(\n        stream: impl Stream<Item = String> + Send + 'static,\n    ) -> Self {\n        AxumResponse(\n            Body::from_stream(\n                stream.map(|chunk| Ok(chunk) as Result<String, std::io::Error>),\n            )\n            .into_response(),\n        )\n    }\n\n    fn extend_response(&mut self, res_options: &Self::ResponseOptions) {\n        let mut res_options = res_options.0.write().or_poisoned();\n        if let Some(status) = res_options.status {\n            *self.0.status_mut() = status;\n        }\n        self.0\n            .headers_mut()\n            .extend(std::mem::take(&mut res_options.headers));\n    }\n\n    fn set_default_content_type(&mut self, content_type: &str) {\n        let headers = self.0.headers_mut();\n        if !headers.contains_key(header::CONTENT_TYPE) {\n            // Set the Content Type headers on all responses. This makes Firefox show the page source\n            // without complaining\n            headers.insert(\n                header::CONTENT_TYPE,\n                HeaderValue::from_str(content_type).unwrap(),\n            );\n        }\n    }\n}\n\n/// Provides an easy way to redirect the user from within a server function.\n///\n/// Calling `redirect` in a server function will redirect the browser in three\n/// situations:\n/// 1. A server function that is calling in a [blocking\n///    resource](leptos::server::Resource::new_blocking).\n/// 2. A server function that is called from WASM running in the client (e.g., a dispatched action\n///    or a spawned `Future`).\n/// 3. A `<form>` submitted to the server function endpoint using default browser APIs (often due\n///    to using [`ActionForm`] without JS/WASM present.)\n///\n/// Using it with a non-blocking [`Resource`] will not work if you are using streaming rendering,\n/// as the response's headers will already have been sent by the time the server function calls `redirect()`.\n///\n/// ### Implementation\n///\n/// This sets the `Location` header to the URL given.\n///\n/// If the route or server function in which this is called is being accessed\n/// by an ordinary `GET` request or an HTML `<form>` without any enhancement, it also sets a\n/// status code of `302` for a temporary redirect. (This is determined by whether the `Accept`\n/// header contains `text/html` as it does for an ordinary navigation.)\n///\n/// Otherwise, it sets a custom header that indicates to the client that it should redirect,\n/// without actually setting the status code. This means that the client will not follow the\n/// redirect, and can therefore return the value of the server function and then handle\n/// the redirect with client-side routing.\npub fn redirect(path: &str) {\n    if let (Some(req), Some(res)) =\n        (use_context::<Parts>(), use_context::<ResponseOptions>())\n    {\n        // insert the Location header in any case\n        res.insert_header(\n            header::LOCATION,\n            header::HeaderValue::from_str(path)\n                .expect(\"Failed to create HeaderValue\"),\n        );\n\n        let accepts_html = req\n            .headers\n            .get(ACCEPT)\n            .and_then(|v| v.to_str().ok())\n            .map(|v| v.contains(\"text/html\"))\n            .unwrap_or(false);\n        if accepts_html {\n            // if the request accepts text/html, it's a plain form request and needs\n            // to have the 302 code set\n            res.set_status(StatusCode::FOUND);\n        } else {\n            // otherwise, we sent it from the server fn client and actually don't want\n            // to set a real redirect, as this will break the ability to return data\n            // instead, set the REDIRECT_HEADER to indicate that the client should redirect\n            res.insert_header(\n                HeaderName::from_static(REDIRECT_HEADER),\n                HeaderValue::from_str(\"\").unwrap(),\n            );\n        }\n    } else {\n        #[cfg(feature = \"tracing\")]\n        {\n            tracing::warn!(\n                \"Couldn't retrieve either Parts or ResponseOptions while \\\n                 trying to redirect().\"\n            );\n        }\n        #[cfg(not(feature = \"tracing\"))]\n        {\n            eprintln!(\n                \"Couldn't retrieve either Parts or ResponseOptions while \\\n                 trying to redirect().\"\n            );\n        }\n    }\n}\n\n/// Decomposes an HTTP request into its parts, allowing you to read its headers\n/// and other data without consuming the body. Creates a new Request from the\n/// original parts for further processing\npub fn generate_request_and_parts(\n    req: Request<Body>,\n) -> (Request<Body>, Parts) {\n    let (parts, body) = req.into_parts();\n    let parts2 = parts.clone();\n    (Request::from_parts(parts, body), parts2)\n}\n\n/// An Axum handlers to listens for a request with Leptos server function arguments in the body,\n/// run the server function if found, and return the resulting [`Response`].\n///\n/// This can then be set up at an appropriate route in your application:\n///\n/// ```no_run\n/// use axum::{handler::Handler, routing::post, Router};\n/// use leptos::prelude::*;\n/// use std::net::SocketAddr;\n///\n/// #[cfg(feature = \"default\")]\n/// #[tokio::main]\n/// async fn main() {\n///     let addr = SocketAddr::from(([127, 0, 0, 1], 8082));\n///\n///     // build our application with a route\n///     let app = Router::new()\n///         .route(\"/api/*fn_name\", post(leptos_axum::handle_server_fns));\n///\n///     // run our app with hyper\n///     let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n///     axum::serve(listener, app.into_make_service())\n///         .await\n///         .unwrap();\n/// }\n///\n/// # #[cfg(not(feature = \"default\"))]\n/// # fn main() { }\n/// ```\n/// Leptos provides a generic implementation of `handle_server_fns`. If access to more specific parts of the Request is desired,\n/// you can specify your own server fn handler based on this one and give it it's own route in the server macro.\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub async fn handle_server_fns(req: Request<Body>) -> impl IntoResponse {\n    handle_server_fns_inner(|| {}, req).await\n}\n\nfn init_executor() {\n    #[cfg(feature = \"wasm\")]\n    let _ = any_spawner::Executor::init_wasm_bindgen();\n    #[cfg(all(not(feature = \"wasm\"), feature = \"default\"))]\n    let _ = any_spawner::Executor::init_tokio();\n    #[cfg(all(not(feature = \"wasm\"), not(feature = \"default\")))]\n    {\n        eprintln!(\n            \"It appears you have set 'default-features = false' on \\\n             'leptos_axum', but are not using the 'wasm' feature. Either \\\n             remove 'default-features = false' or, if you are running in a \\\n             JS-hosted WASM server environment, add the 'wasm' feature.\"\n        );\n    }\n}\n\n/// An Axum handlers to listens for a request with Leptos server function arguments in the body,\n/// run the server function if found, and return the resulting [`Response`].\n///\n/// This can then be set up at an appropriate route in your application:\n///\n/// This version allows you to pass in a closure to capture additional data from the layers above leptos\n/// and store it in context. To use it, you'll need to define your own route, and a handler function\n/// that takes in the data you'd like. See the [render_app_to_stream_with_context] docs for an example\n/// of one that should work much like this one.\n///\n/// **NOTE**: If your server functions expect a context, make sure to provide it both in\n/// [`handle_server_fns_with_context`] **and** in\n/// [`leptos_routes_with_context`](LeptosRoutes::leptos_routes_with_context) (or whatever\n/// rendering method you are using). During SSR, server functions are called by the rendering\n/// method, while subsequent calls from the client are handled by the server function handler.\n/// The same context needs to be provided to both handlers.\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub async fn handle_server_fns_with_context(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    req: Request<Body>,\n) -> impl IntoResponse {\n    handle_server_fns_inner(additional_context, req).await\n}\n\nasync fn handle_server_fns_inner(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    req: Request<Body>,\n) -> impl IntoResponse {\n    let method = req.method().clone();\n    let path = req.uri().path().to_string();\n    let (req, parts) = generate_request_and_parts(req);\n\n    if let Some(mut service) =\n        server_fn::axum::get_server_fn_service(&path, method)\n    {\n        let owner = Owner::new();\n        owner\n            .with(|| {\n                ScopedFuture::new(async move {\n                    provide_context(parts);\n                    let res_options = ResponseOptions::default();\n                    provide_context(res_options.clone());\n                    additional_context();\n\n                    // store Accepts and Referer in case we need them for redirect (below)\n                    let accepts_html = req\n                        .headers()\n                        .get(ACCEPT)\n                        .and_then(|v| v.to_str().ok())\n                        .map(|v| v.contains(\"text/html\"))\n                        .unwrap_or(false);\n                    let referrer = req.headers().get(REFERER).cloned();\n\n                    // actually run the server fn\n                    let mut res = AxumResponse(service.run(req).await);\n\n                    // if it accepts text/html (i.e., is a plain form post) and doesn't already have a\n                    // Location set, then redirect to the Referer\n                    if accepts_html {\n                        if let Some(referrer) = referrer {\n                            let has_location =\n                                res.0.headers().get(LOCATION).is_some();\n                            if !has_location {\n                                *res.0.status_mut() = StatusCode::FOUND;\n                                res.0.headers_mut().insert(LOCATION, referrer);\n                            }\n                        }\n                    }\n\n                    // apply status code and headers if user changed them\n                    res.extend_response(&res_options);\n                    Ok(res.0)\n                })\n            })\n            .await\n    } else {\n        Response::builder()\n            .status(StatusCode::BAD_REQUEST)\n            .body(Body::from(format!(\n                \"Could not find a server function at the route {path}. \\\n                 \\n\\nIt's likely that either\n                         1. The API prefix you specify in the `#[server]` \\\n                 macro doesn't match the prefix at which your server function \\\n                 handler is mounted, or \\n2. You are on a platform that \\\n                 doesn't support automatic server function registration and \\\n                 you need to call ServerFn::register_explicit() on the server \\\n                 function type, somewhere in your `main` function.\",\n            )))\n    }\n    .expect(\"could not build Response\")\n}\n\n/// A stream of bytes of HTML.\npub type PinnedHtmlStream =\n    Pin<Box<dyn Stream<Item = io::Result<Bytes>> + Send>>;\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an HTML stream of your application.\n///\n/// This can then be set up at an appropriate route in your application:\n/// ```no_run\n/// use axum::{handler::Handler, Router};\n/// use leptos::{config::get_configuration, prelude::*};\n/// use std::{env, net::SocketAddr};\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     view! { <main>\"Hello, world!\"</main> }\n/// }\n///\n/// #[cfg(feature = \"default\")]\n/// #[tokio::main]\n/// async fn main() {\n///     let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n///     let leptos_options = conf.leptos_options;\n///     let addr = leptos_options.site_addr.clone();\n///\n///     // build our application with a route\n///     let app = Router::new().fallback(leptos_axum::render_app_to_stream(\n///         || { /* your application here */ },\n///     ));\n///\n///     // run our app with hyper\n///     let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n///     axum::serve(listener, app.into_make_service())\n///         .await\n///         .unwrap();\n/// }\n///\n/// # #[cfg(not(feature = \"default\"))]\n/// # fn main() { }\n/// ```\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n/// - [`ServerMetaContext`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream<IV>(\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    render_app_to_stream_with_context(|| {}, app_fn)\n}\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an HTML stream of your application.\n/// The difference between calling this and `render_app_to_stream_with_context()` is that this\n/// one respects the `SsrMode` on each Route and thus requires `Vec<AxumRouteListing>` for route checking.\n/// This is useful if you are using `.leptos_routes_with_handler()`\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_route<S, IV>(\n    paths: Vec<AxumRouteListing>,\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    State<S>,\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n    LeptosOptions: FromRef<S>,\n    S: Send + 'static,\n{\n    render_route_with_context(paths, || {}, app_fn)\n}\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an in-order HTML stream of your application.\n/// This stream will pause at each `<Suspense/>` node and wait for it to resolve before\n/// sending down its HTML. The app will become interactive once it has fully loaded.\n///\n/// This can then be set up at an appropriate route in your application:\n/// ```no_run\n/// use axum::{handler::Handler, Router};\n/// use leptos::{config::get_configuration, prelude::*};\n/// use std::{env, net::SocketAddr};\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     view! { <main>\"Hello, world!\"</main> }\n/// }\n///\n/// #[cfg(feature = \"default\")]\n/// #[tokio::main]\n/// async fn main() {\n///     let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n///     let leptos_options = conf.leptos_options;\n///     let addr = leptos_options.site_addr.clone();\n///\n///     // build our application with a route\n///     let app = Router::new().fallback(\n///         leptos_axum::render_app_to_stream_in_order(|| view! { <MyApp/> }),\n///     );\n///\n///     // run our app with hyper\n///     let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n///     axum::serve(listener, app.into_make_service())\n///         .await\n///         .unwrap();\n/// }\n///\n/// # #[cfg(not(feature = \"default\"))]\n/// # fn main() { }\n/// ```\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n/// - [`ServerMetaContext`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream_in_order<IV>(\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    render_app_to_stream_in_order_with_context(|| {}, app_fn)\n}\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an HTML stream of your application.\n///\n/// This version allows us to pass Axum State/Extension/Extractor or other info from Axum or network\n/// layers above Leptos itself. To use it, you'll need to write your own handler function that provides\n/// the data to leptos in a closure. An example is below\n/// ```\n/// use axum::{\n///     body::Body,\n///     extract::Path,\n///     http::Request,\n///     response::{IntoResponse, Response},\n/// };\n/// use leptos::{config::LeptosOptions, context::provide_context, prelude::*};\n///\n/// async fn custom_handler(\n///     Path(id): Path<String>,\n///     req: Request<Body>,\n/// ) -> Response {\n///     let handler = leptos_axum::render_app_to_stream_with_context(\n///         move || {\n///             provide_context(id.clone());\n///         },\n///         || { /* your app here */ },\n///     );\n///     handler(req).await.into_response()\n/// }\n/// ```\n/// Otherwise, this function is identical to [render_app_to_stream].\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n/// - [`ServerMetaContext`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream_with_context<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send + Sync,\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + Sync\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    render_app_to_stream_with_context_and_replace_blocks(\n        additional_context,\n        app_fn,\n        false,\n    )\n}\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an HTML stream of your application. It allows you\n/// to pass in a context function with additional info to be made available to the app\n/// The difference between calling this and `render_app_to_stream_with_context()` is that this\n/// one respects the `SsrMode` on each Route, and thus requires `Vec<AxumRouteListing>` for route checking.\n/// This is useful if you are using `.leptos_routes_with_handler()`.\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_route_with_context<S, IV>(\n    paths: Vec<AxumRouteListing>,\n    additional_context: impl Fn() + 'static + Clone + Send + Sync,\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    State<S>,\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n    LeptosOptions: FromRef<S>,\n    S: Send + 'static,\n{\n    let ooo = render_app_to_stream_with_context(\n        additional_context.clone(),\n        app_fn.clone(),\n    );\n    let pb = render_app_to_stream_with_context_and_replace_blocks(\n        additional_context.clone(),\n        app_fn.clone(),\n        true,\n    );\n    let io = render_app_to_stream_in_order_with_context(\n        additional_context.clone(),\n        app_fn.clone(),\n    );\n    let asyn = render_app_async_stream_with_context(\n        additional_context.clone(),\n        app_fn.clone(),\n    );\n\n    move |state, req| {\n        // 1. Process route to match the values in routeListing\n        let path = req\n            .extensions()\n            .get::<MatchedPath>()\n            .expect(\"Failed to get Axum router rule\")\n            .as_str();\n        // 2. Find RouteListing in paths. This should probably be optimized, we probably don't want to\n        // search for this every time\n        let listing: &AxumRouteListing =\n            paths.iter().find(|r| r.path() == path).unwrap_or_else(|| {\n                panic!(\n                    \"Failed to find the route {path} requested by the user. \\\n                     This suggests that the routing rules in the Router that \\\n                     call this handler needs to be edited!\"\n                )\n            });\n        // 3. Match listing mode against known, and choose function\n        match listing.mode() {\n            SsrMode::OutOfOrder => ooo(req),\n            SsrMode::PartiallyBlocked => pb(req),\n            SsrMode::InOrder => io(req),\n            SsrMode::Async => asyn(req),\n            SsrMode::Static(_) => {\n                #[cfg(feature = \"default\")]\n                {\n                    let regenerate = listing.regenerate.clone();\n                    handle_static_route(\n                        additional_context.clone(),\n                        app_fn.clone(),\n                        regenerate,\n                    )(state, req)\n                }\n                #[cfg(not(feature = \"default\"))]\n                {\n                    _ = state;\n                    panic!(\n                        \"Static routes are not currently supported on WASM32 \\\n                         server targets.\"\n                    );\n                }\n            }\n        }\n    }\n}\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an HTML stream of your application.\n///\n/// This version allows us to pass Axum State/Extension/Extractor or other info from Axum or network\n/// layers above Leptos itself. To use it, you'll need to write your own handler function that provides\n/// the data to leptos in a closure.\n///\n/// `replace_blocks` additionally lets you specify whether `<Suspense/>` fragments that read\n/// from blocking resources should be retrojected into the HTML that's initially served, rather\n/// than dynamically inserting them with JavaScript on the client. This means you will have\n/// better support if JavaScript is not enabled, in exchange for a marginally slower response time.\n///\n/// Otherwise, this function is identical to [render_app_to_stream_with_context].\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n/// - [`ServerMetaContext`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream_with_context_and_replace_blocks<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send + Sync,\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n    replace_blocks: bool,\n) -> impl Fn(\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + Sync\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    _ = replace_blocks; // TODO\n    handle_response(additional_context, app_fn, |app, chunks, supports_ooo| {\n        Box::pin(async move {\n            let app = if cfg!(feature = \"islands-router\") {\n                if supports_ooo {\n                    app.to_html_stream_out_of_order_branching()\n                } else {\n                    app.to_html_stream_in_order_branching()\n                }\n            } else if supports_ooo {\n                app.to_html_stream_out_of_order()\n            } else {\n                app.to_html_stream_in_order()\n            };\n            Box::pin(app.chain(chunks())) as PinnedStream<String>\n        })\n    })\n}\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], serving an in-order HTML stream of your application.\n/// This stream will pause at each `<Suspense/>` node and wait for it to resolve before\n/// sending down its HTML. The app will become interactive once it has fully loaded.\n///\n/// This version allows us to pass Axum State/Extension/Extractor or other info from Axum or network\n/// layers above Leptos itself. To use it, you'll need to write your own handler function that provides\n/// the data to leptos in a closure. An example is below\n/// ```\n/// use axum::{\n///     body::Body,\n///     extract::Path,\n///     http::Request,\n///     response::{IntoResponse, Response},\n/// };\n/// use leptos::context::provide_context;\n///\n/// async fn custom_handler(\n///     Path(id): Path<String>,\n///     req: Request<Body>,\n/// ) -> Response {\n///     let handler = leptos_axum::render_app_to_stream_in_order_with_context(\n///         move || {\n///             provide_context(id.clone());\n///         },\n///         || { /* your application here */ },\n///     );\n///     handler(req).await.into_response()\n/// }\n/// ```\n/// Otherwise, this function is identical to [render_app_to_stream].\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n/// - [`ServerMetaContext`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_to_stream_in_order_with_context<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send + Sync,\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    handle_response(additional_context, app_fn, |app, chunks, _supports_ooo| {\n        let app = if cfg!(feature = \"islands-router\") {\n            app.to_html_stream_in_order_branching()\n        } else {\n            app.to_html_stream_in_order()\n        };\n        Box::pin(async move {\n            Box::pin(app.chain(chunks())) as PinnedStream<String>\n        })\n    })\n}\n\nfn handle_response<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send + Sync,\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n    stream_builder: fn(\n        IV,\n        BoxedFnOnce<PinnedStream<String>>,\n        bool,\n    ) -> PinnedFuture<PinnedStream<String>>,\n) -> impl Fn(Request<Body>) -> PinnedFuture<Response<Body>>\n       + Clone\n       + Send\n       + Sync\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    move |req: Request<Body>| {\n        let app_fn = app_fn.clone();\n        let additional_context = additional_context.clone();\n        handle_response_inner(additional_context, app_fn, req, stream_builder)\n    }\n}\n\n/// Can be used in conjunction with a custom [file_and_error_handler_with_context] to process an Axum [Request](axum::extract::Request) into an Axum [Response](axum::response::Response)\npub fn handle_response_inner<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    app_fn: impl FnOnce() -> IV + Send + 'static,\n    req: Request<Body>,\n    stream_builder: fn(\n        IV,\n        BoxedFnOnce<PinnedStream<String>>,\n        bool,\n    ) -> PinnedFuture<PinnedStream<String>>,\n) -> PinnedFuture<Response<Body>>\nwhere\n    IV: IntoView + 'static,\n{\n    Box::pin(async move {\n        let is_island_router_navigation = cfg!(feature = \"islands-router\")\n            && req.headers().get(\"Islands-Router\").is_some();\n\n        let add_context = additional_context.clone();\n        let res_options = ResponseOptions::default();\n        let (meta_context, meta_output) = ServerMetaContext::new();\n\n        let additional_context = {\n            let meta_context = meta_context.clone();\n            let res_options = res_options.clone();\n            move || {\n                // Need to get the path and query string of the Request\n                // For reasons that escape me, if the incoming URI protocol is https, it provides the absolute URI\n                let path = req.uri().path_and_query().unwrap().as_str();\n\n                let full_path = format!(\"http://leptos.dev{path}\");\n                let (_, req_parts) = generate_request_and_parts(req);\n                provide_contexts(\n                    &full_path,\n                    &meta_context,\n                    req_parts,\n                    res_options.clone(),\n                );\n                add_context();\n\n                if is_island_router_navigation {\n                    provide_context(IslandsRouterNavigation);\n                }\n            }\n        };\n\n        let res = AxumResponse::from_app(\n            app_fn,\n            meta_output,\n            additional_context,\n            res_options,\n            stream_builder,\n            !is_island_router_navigation,\n        )\n        .await;\n\n        res.0\n    })\n}\n\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\nfn provide_contexts(\n    path: &str,\n    meta_context: &ServerMetaContext,\n    parts: Parts,\n    default_res_options: ResponseOptions,\n) {\n    provide_context(RequestUrl::new(path));\n    provide_context(meta_context.clone());\n    provide_context(parts);\n    provide_context(default_res_options);\n    provide_server_redirect(redirect);\n    leptos::nonce::provide_nonce();\n}\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], asynchronously rendering an HTML page after all\n/// `async` resources have loaded.\n///\n/// This can then be set up at an appropriate route in your application:\n/// ```no_run\n/// use axum::{handler::Handler, Router};\n/// use leptos::{config::get_configuration, prelude::*};\n/// use std::{env, net::SocketAddr};\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     view! { <main>\"Hello, world!\"</main> }\n/// }\n///\n/// #[cfg(feature = \"default\")]\n/// #[tokio::main]\n/// async fn main() {\n///     let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n///     let leptos_options = conf.leptos_options;\n///     let addr = leptos_options.site_addr.clone();\n///\n///     // build our application with a route\n///     let app = Router::new()\n///         .fallback(leptos_axum::render_app_async(|| view! { <MyApp/> }));\n///\n///     // run our app with hyper\n///     // `axum::Server` is a re-export of `hyper::Server`\n///     let listener =\n///         tokio::net::TcpListener::bind(\"0.0.0.0:3000\").await.unwrap();\n///     axum::serve(listener, app.into_make_service())\n///         .await\n///         .unwrap();\n/// }\n///\n/// # #[cfg(not(feature = \"default\"))]\n/// # fn main() { }\n/// ```\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n/// - [`ServerMetaContext`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_async<IV>(\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    render_app_async_with_context(|| {}, app_fn)\n}\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], asynchronously rendering an HTML page after all\n/// `async` resources have loaded.\n///\n/// This version allows us to pass Axum State/Extension/Extractor or other info from Axum or network\n/// layers above Leptos itself. To use it, you'll need to write your own handler function that provides\n/// the data to leptos in a closure. An example is below\n/// ```\n/// use axum::{\n///     body::Body,\n///     extract::Path,\n///     http::Request,\n///     response::{IntoResponse, Response},\n/// };\n/// use leptos::context::provide_context;\n///\n/// async fn custom_handler(\n///     Path(id): Path<String>,\n///     req: Request<Body>,\n/// ) -> Response {\n///     let handler = leptos_axum::render_app_async_with_context(\n///         move || {\n///             provide_context(id.clone());\n///         },\n///         || { /* your application here */ },\n///     );\n///     handler(req).await.into_response()\n/// }\n/// ```\n/// Otherwise, this function is identical to [render_app_to_stream].\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n/// - [`ServerMetaContext`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_async_stream_with_context<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send + Sync,\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    handle_response(additional_context, app_fn, |app, chunks, _supports_ooo| {\n        Box::pin(async move {\n            let app = if cfg!(feature = \"islands-router\") {\n                app.to_html_stream_in_order_branching()\n            } else {\n                app.to_html_stream_in_order()\n            };\n            let app = app.collect::<String>().await;\n            let chunks = chunks();\n            Box::pin(once(async move { app }).chain(chunks))\n                as PinnedStream<String>\n        })\n    })\n}\n\n/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries\n/// to route it using [leptos_router], asynchronously rendering an HTML page after all\n/// `async` resources have loaded.\n///\n/// This version allows us to pass Axum State/Extension/Extractor or other info from Axum or network\n/// layers above Leptos itself. To use it, you'll need to write your own handler function that provides\n/// the data to leptos in a closure. An example is below\n/// ```\n/// use axum::{\n///     body::Body,\n///     extract::Path,\n///     http::Request,\n///     response::{IntoResponse, Response},\n/// };\n/// use leptos::context::provide_context;\n///\n/// async fn custom_handler(\n///     Path(id): Path<String>,\n///     req: Request<Body>,\n/// ) -> Response {\n///     let handler = leptos_axum::render_app_async_with_context(\n///         move || {\n///             provide_context(id.clone());\n///         },\n///         || { /* your application here */ },\n///     );\n///     handler(req).await.into_response()\n/// }\n/// ```\n/// Otherwise, this function is identical to [render_app_to_stream].\n///\n/// ## Provided Context Types\n/// This function always provides context values including the following types:\n/// - [`Parts`]\n/// - [`ResponseOptions`]\n/// - [`ServerMetaContext`]\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn render_app_async_with_context<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send + Sync,\n    app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n) -> impl Fn(\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n{\n    handle_response(additional_context, app_fn, async_stream_builder)\n}\n\nfn async_stream_builder<IV>(\n    app: IV,\n    chunks: BoxedFnOnce<PinnedStream<String>>,\n    _supports_ooo: bool,\n) -> PinnedFuture<PinnedStream<String>>\nwhere\n    IV: IntoView + 'static,\n{\n    Box::pin(async move {\n        let app = if cfg!(feature = \"islands-router\") {\n            app.to_html_stream_in_order_branching()\n        } else {\n            app.to_html_stream_in_order()\n        };\n        let app = app.collect::<String>().await;\n        let chunks = chunks();\n        Box::pin(once(async move { app }).chain(chunks)) as PinnedStream<String>\n    })\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths.\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn generate_route_list<IV>(\n    app_fn: impl Fn() -> IV + 'static + Clone + Send,\n) -> Vec<AxumRouteListing>\nwhere\n    IV: IntoView + 'static,\n{\n    generate_route_list_with_exclusions_and_ssg(app_fn, None).0\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths.\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn generate_route_list_with_ssg<IV>(\n    app_fn: impl Fn() -> IV + 'static + Clone + Send,\n) -> (Vec<AxumRouteListing>, StaticRouteGenerator)\nwhere\n    IV: IntoView + 'static,\n{\n    generate_route_list_with_exclusions_and_ssg(app_fn, None)\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths. Adding excluded_routes\n/// to this function will stop `.leptos_routes()` from generating a route for it, allowing a custom handler. These need to be in Axum path format\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn generate_route_list_with_exclusions<IV>(\n    app_fn: impl Fn() -> IV + 'static + Clone + Send,\n    excluded_routes: Option<Vec<String>>,\n) -> Vec<AxumRouteListing>\nwhere\n    IV: IntoView + 'static,\n{\n    generate_route_list_with_exclusions_and_ssg(app_fn, excluded_routes).0\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths. Adding excluded_routes\n/// to this function will stop `.leptos_routes()` from generating a route for it, allowing a custom handler. These need to be in Axum path format\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn generate_route_list_with_exclusions_and_ssg<IV>(\n    app_fn: impl Fn() -> IV + 'static + Clone + Send,\n    excluded_routes: Option<Vec<String>>,\n) -> (Vec<AxumRouteListing>, StaticRouteGenerator)\nwhere\n    IV: IntoView + 'static,\n{\n    generate_route_list_with_exclusions_and_ssg_and_context(\n        app_fn,\n        excluded_routes,\n        || {},\n    )\n}\n\n#[derive(Clone, Debug, Default)]\n/// A route that this application can serve.\npub struct AxumRouteListing {\n    path: String,\n    mode: SsrMode,\n    methods: Vec<leptos_router::Method>,\n    #[allow(unused)]\n    regenerate: Vec<RegenerationFn>,\n    exclude: bool,\n}\n\ntrait IntoRouteListing: Sized {\n    fn into_route_listing(self) -> Vec<AxumRouteListing>;\n}\n\nimpl IntoRouteListing for RouteListing {\n    fn into_route_listing(self) -> Vec<AxumRouteListing> {\n        self.path()\n            .to_vec()\n            .expand_optionals()\n            .into_iter()\n            .map(|path| {\n                let path = path.to_axum_path();\n                let path = if path.is_empty() {\n                    \"/\".to_string()\n                } else {\n                    path\n                };\n                let mode = self.mode();\n                let methods = self.methods().collect();\n                let regenerate = self.regenerate().into();\n                AxumRouteListing {\n                    path,\n                    mode: mode.clone(),\n                    methods,\n                    regenerate,\n                    exclude: false,\n                }\n            })\n            .collect()\n    }\n}\n\nimpl AxumRouteListing {\n    /// Create a route listing from its parts.\n    pub fn new(\n        path: String,\n        mode: SsrMode,\n        methods: impl IntoIterator<Item = leptos_router::Method>,\n        regenerate: impl Into<Vec<RegenerationFn>>,\n    ) -> Self {\n        Self {\n            path,\n            mode,\n            methods: methods.into_iter().collect(),\n            regenerate: regenerate.into(),\n            exclude: false,\n        }\n    }\n\n    /// The path this route handles.\n    pub fn path(&self) -> &str {\n        &self.path\n    }\n\n    /// The rendering mode for this path.\n    pub fn mode(&self) -> &SsrMode {\n        &self.mode\n    }\n\n    /// The HTTP request methods this path can handle.\n    pub fn methods(&self) -> impl Iterator<Item = leptos_router::Method> + '_ {\n        self.methods.iter().copied()\n    }\n}\n\n/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically\n/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element\n/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths. Adding excluded_routes\n/// to this function will stop `.leptos_routes()` from generating a route for it, allowing a custom handler. These need to be in Axum path format\n/// Additional context will be provided to the app Element.\n#[cfg_attr(\n    feature = \"tracing\",\n    tracing::instrument(level = \"trace\", fields(error), skip_all)\n)]\npub fn generate_route_list_with_exclusions_and_ssg_and_context<IV>(\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    excluded_routes: Option<Vec<String>>,\n    additional_context: impl Fn() + Clone + Send + 'static,\n) -> (Vec<AxumRouteListing>, StaticRouteGenerator)\nwhere\n    IV: IntoView + 'static,\n{\n    // do some basic reactive setup\n    init_executor();\n    let owner = Owner::new_root(Some(Arc::new(SsrSharedContext::new())));\n\n    let routes = owner\n        .with(|| {\n            // stub out a path for now\n            provide_context(RequestUrl::new(\"\"));\n            let (mock_parts, _) = Request::new(Body::from(\"\")).into_parts();\n            let (mock_meta, _) = ServerMetaContext::new();\n            provide_contexts(\"\", &mock_meta, mock_parts, Default::default());\n            additional_context();\n            RouteList::generate(&app_fn)\n        })\n        .unwrap_or_default();\n\n    let generator = StaticRouteGenerator::new(\n        &routes,\n        app_fn.clone(),\n        additional_context.clone(),\n    );\n\n    // Axum's Router defines Root routes as \"/\" not \"\"\n    let mut routes = routes\n        .into_inner()\n        .into_iter()\n        .flat_map(IntoRouteListing::into_route_listing)\n        .collect::<Vec<_>>();\n\n    let routes = if routes.is_empty() {\n        vec![AxumRouteListing::new(\n            \"/\".to_string(),\n            Default::default(),\n            [leptos_router::Method::Get],\n            vec![],\n        )]\n    } else {\n        // Routes to exclude from auto generation\n        if let Some(excluded_routes) = &excluded_routes {\n            routes.retain(|p| !excluded_routes.iter().any(|e| e == p.path()))\n        }\n        routes\n    };\n    let excluded =\n        excluded_routes\n            .into_iter()\n            .flatten()\n            .map(|path| AxumRouteListing {\n                path,\n                mode: Default::default(),\n                methods: Vec::new(),\n                regenerate: Vec::new(),\n                exclude: true,\n            });\n\n    (routes.into_iter().chain(excluded).collect(), generator)\n}\n\n/// Allows generating any prerendered routes.\n#[allow(clippy::type_complexity)]\npub struct StaticRouteGenerator(\n    // this is here to keep the root owner alive for the duration\n    // of the route generation, so that base context provided continues\n    // to exist until it is dropped\n    #[allow(dead_code)] Owner,\n    Box<dyn FnOnce(&LeptosOptions) -> PinnedFuture<()> + Send>,\n);\n\nimpl StaticRouteGenerator {\n    #[cfg(feature = \"default\")]\n    fn render_route<IV: IntoView + 'static>(\n        path: String,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n        additional_context: impl Fn() + Clone + Send + 'static,\n    ) -> impl Future<Output = (Owner, String)> {\n        let (meta_context, meta_output) = ServerMetaContext::new();\n        let additional_context = {\n            let add_context = additional_context.clone();\n            move || {\n                let full_path = format!(\"http://leptos.dev{path}\");\n                let mock_req = Request::builder()\n                    .method(Method::GET)\n                    .header(\"Accept\", \"text/html\")\n                    .body(Body::empty())\n                    .unwrap();\n                let (mock_parts, _) = mock_req.into_parts();\n                let res_options = ResponseOptions::default();\n                provide_contexts(\n                    &full_path,\n                    &meta_context,\n                    mock_parts,\n                    res_options,\n                );\n                add_context();\n            }\n        };\n\n        let (owner, stream) = leptos_integration_utils::build_response(\n            app_fn.clone(),\n            additional_context,\n            async_stream_builder,\n            false,\n        );\n\n        let sc = owner.shared_context().unwrap();\n\n        async move {\n            let stream = stream.await;\n            while let Some(pending) = sc.await_deferred() {\n                pending.await;\n            }\n\n            let html = meta_output\n                .inject_meta_context(stream)\n                .await\n                .collect::<String>()\n                .await;\n            (owner, html)\n        }\n    }\n\n    /// Creates a new static route generator from the given list of route definitions.\n    pub fn new<IV>(\n        routes: &RouteList,\n        app_fn: impl Fn() -> IV + Clone + Send + 'static,\n        additional_context: impl Fn() + Clone + Send + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static,\n    {\n        #[cfg(feature = \"default\")]\n        {\n            let owner = Owner::new();\n            Self(owner.clone(), {\n                let routes = routes.clone();\n                Box::new(move |options| {\n                    let options = options.clone();\n                    let app_fn = app_fn.clone();\n                    let additional_context = additional_context.clone();\n                    owner.with(|| {\n                        additional_context();\n                        Box::pin(ScopedFuture::new(routes.generate_static_files(\n                        move |path: &ResolvedStaticPath| {\n                            Self::render_route(\n                                path.to_string(),\n                                app_fn.clone(),\n                                additional_context.clone(),\n                            )\n                        },\n                        move |path: &ResolvedStaticPath,\n                              owner: &Owner,\n                              html: String| {\n                            let options = options.clone();\n                            let path = path.to_owned();\n                            let response_options = owner.with(use_context);\n                            async move {\n                                write_static_route(\n                                    &options,\n                                    response_options,\n                                    path.as_ref(),\n                                    &html,\n                                )\n                                .await\n                            }\n                        },\n                        was_404,\n                    )))\n                    })\n                })\n            })\n        }\n\n        #[cfg(not(feature = \"default\"))]\n        {\n            _ = routes;\n            _ = app_fn;\n            _ = additional_context;\n            Self(\n                Owner::new(),\n                Box::new(|_| {\n                    panic!(\n                        \"Static routes are not currently supported on WASM32 \\\n                         server targets.\"\n                    );\n                }),\n            )\n        }\n    }\n\n    /// Generates the routes.\n    pub async fn generate(self, options: &LeptosOptions) {\n        (self.1)(options).await\n    }\n}\n\n#[cfg(feature = \"default\")]\nstatic STATIC_HEADERS: LazyLock<\n    std::sync::RwLock<HashMap<String, ResponseOptions>>,\n> = LazyLock::new(Default::default);\n\n#[cfg(feature = \"default\")]\nfn was_404(owner: &Owner) -> bool {\n    let resp = owner.with(|| expect_context::<ResponseOptions>());\n    let status = resp.0.read().or_poisoned().status;\n\n    if let Some(status) = status {\n        return status == StatusCode::NOT_FOUND;\n    }\n\n    false\n}\n\n#[cfg(feature = \"default\")]\nfn static_path(options: &LeptosOptions, path: &str) -> String {\n    use leptos_integration_utils::static_file_path;\n\n    // If the path ends with a trailing slash, we generate the path\n    // as a directory with a index.html file inside.\n    if path != \"/\" && path.ends_with(\"/\") {\n        static_file_path(options, &format!(\"{path}index\"))\n    } else {\n        static_file_path(options, path)\n    }\n}\n\n#[cfg(feature = \"default\")]\nasync fn write_static_route(\n    options: &LeptosOptions,\n    response_options: Option<ResponseOptions>,\n    path: &str,\n    html: &str,\n) -> Result<(), std::io::Error> {\n    if let Some(options) = response_options {\n        STATIC_HEADERS\n            .write()\n            .or_poisoned()\n            .insert(path.to_string(), options);\n    }\n\n    let path = static_path(options, path);\n    let path = Path::new(&path);\n    if let Some(path) = path.parent() {\n        tokio::fs::create_dir_all(path).await?;\n    }\n    tokio::fs::write(path, &html).await?;\n\n    Ok(())\n}\n\n#[cfg(feature = \"default\")]\nfn handle_static_route<S, IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    app_fn: impl Fn() -> IV + Clone + Send + 'static,\n    regenerate: Vec<RegenerationFn>,\n) -> impl Fn(\n    State<S>,\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    LeptosOptions: FromRef<S>,\n    S: Send + 'static,\n    IV: IntoView + 'static,\n{\n    use tower_http::services::ServeFile;\n\n    move |state, req| {\n        let app_fn = app_fn.clone();\n        let additional_context = additional_context.clone();\n        let regenerate = regenerate.clone();\n        Box::pin(async move {\n            let options = LeptosOptions::from_ref(&state);\n            let orig_path = req.uri().path();\n            let path = static_path(&options, orig_path);\n            let path = Path::new(&path);\n            let exists = tokio::fs::try_exists(path).await.unwrap_or(false);\n\n            let (response_options, html) = if !exists {\n                let path = ResolvedStaticPath::new(orig_path);\n\n                let (owner, html) = path\n                    .build(\n                        move |path: &ResolvedStaticPath| {\n                            StaticRouteGenerator::render_route(\n                                path.to_string(),\n                                app_fn.clone(),\n                                additional_context.clone(),\n                            )\n                        },\n                        move |path: &ResolvedStaticPath,\n                              owner: &Owner,\n                              html: String| {\n                            let options = options.clone();\n                            let path = path.to_owned();\n                            let response_options = owner.with(use_context);\n                            async move {\n                                write_static_route(\n                                    &options,\n                                    response_options,\n                                    path.as_ref(),\n                                    &html,\n                                )\n                                .await\n                            }\n                        },\n                        was_404,\n                        regenerate,\n                    )\n                    .await;\n                (owner.with(use_context::<ResponseOptions>), html)\n            } else {\n                let headers =\n                    STATIC_HEADERS.read().or_poisoned().get(orig_path).cloned();\n                (headers, None)\n            };\n\n            // if html is Some(_), it means that `was_error_response` is true and we're not\n            // actually going to cache this route, just return it as HTML\n            //\n            // this if for thing like 404s, where we do not want to cache an endless series of\n            // typos (or malicious requests)\n            let mut res = AxumResponse(match html {\n                Some(html) => axum::response::Html(html).into_response(),\n                None => match ServeFile::new(path).oneshot(req).await {\n                    Ok(res) => res.into_response(),\n                    Err(err) => (\n                        StatusCode::INTERNAL_SERVER_ERROR,\n                        format!(\"Something went wrong: {err}\"),\n                    )\n                        .into_response(),\n                },\n            });\n\n            if let Some(options) = response_options {\n                res.extend_response(&options);\n            }\n\n            res.0\n        })\n    }\n}\n\n/// This trait allows one to pass a list of routes and a render function to Axum's router, letting us avoid\n/// having to use wildcards or manually define all routes in multiple places.\npub trait LeptosRoutes<S>\nwhere\n    S: Clone + Send + Sync + 'static,\n    LeptosOptions: FromRef<S>,\n{\n    /// Adds routes to the Axum router that have either\n    /// 1) been generated by `leptos_router`, or\n    /// 2) handle a server function.\n    fn leptos_routes<IV>(\n        self,\n        options: &S,\n        paths: Vec<AxumRouteListing>,\n        app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static;\n\n    /// Adds routes to the Axum router that have either\n    /// 1) been generated by `leptos_router`, or\n    /// 2) handle a server function.\n    ///\n    /// Runs `additional_context` to provide additional data to the reactive system via context,\n    /// when handling a route.\n    fn leptos_routes_with_context<IV>(\n        self,\n        options: &S,\n        paths: Vec<AxumRouteListing>,\n        additional_context: impl Fn() + 'static + Clone + Send + Sync,\n        app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static;\n\n    /// Extends the Axum router with the given paths, and handles the requests with the given\n    /// handler.\n    fn leptos_routes_with_handler<H, T>(\n        self,\n        paths: Vec<AxumRouteListing>,\n        handler: H,\n    ) -> Self\n    where\n        H: axum::handler::Handler<T, S>,\n        T: 'static;\n}\n\ntrait AxumPath {\n    fn to_axum_path(&self) -> String;\n}\n\nimpl AxumPath for Vec<PathSegment> {\n    fn to_axum_path(&self) -> String {\n        let mut path = String::new();\n        for segment in self.iter() {\n            // TODO trailing slash handling\n            let raw = segment.as_raw_str();\n            if !raw.is_empty() && !raw.starts_with('/') {\n                path.push('/');\n            }\n            match segment {\n                PathSegment::Static(s) => path.push_str(s),\n                PathSegment::Param(s) => {\n                    path.push('{');\n                    path.push_str(s);\n                    path.push('}');\n                }\n                PathSegment::Splat(s) => {\n                    path.push('{');\n                    path.push('*');\n                    path.push_str(s);\n                    path.push('}');\n                }\n                PathSegment::Unit => {}\n                PathSegment::OptionalParam(_) => {\n                    #[cfg(feature = \"tracing\")]\n                    tracing::error!(\n                        \"to_axum_path should only be called on expanded \\\n                         paths, which do not have OptionalParam any longer\"\n                    );\n                    Default::default()\n                }\n            }\n        }\n        path\n    }\n}\n\n/// The default implementation of `LeptosRoutes` which takes in a list of paths, and dispatches GET requests\n/// to those paths to Leptos's renderer.\nimpl<S> LeptosRoutes<S> for axum::Router<S>\nwhere\n    S: Clone + Send + Sync + 'static,\n    LeptosOptions: FromRef<S>,\n{\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", fields(error), skip_all)\n    )]\n    fn leptos_routes<IV>(\n        self,\n        state: &S,\n        paths: Vec<AxumRouteListing>,\n        app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static,\n    {\n        self.leptos_routes_with_context(state, paths, || {}, app_fn)\n    }\n\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", fields(error), skip_all)\n    )]\n    fn leptos_routes_with_context<IV>(\n        self,\n        state: &S,\n        paths: Vec<AxumRouteListing>,\n        additional_context: impl Fn() + 'static + Clone + Send + Sync,\n        app_fn: impl Fn() -> IV + Clone + Send + Sync + 'static,\n    ) -> Self\n    where\n        IV: IntoView + 'static,\n    {\n        init_executor();\n\n        // S represents the router's finished state allowing us to provide\n        // it to the user's server functions.\n        let state = state.clone();\n        let cx_with_state = move || {\n            provide_context::<S>(state.clone());\n            additional_context();\n        };\n\n        let mut router = self;\n\n        let excluded = paths\n            .iter()\n            .filter(|&p| p.exclude)\n            .map(|p| p.path.as_str())\n            .collect::<HashSet<_>>();\n\n        // register server functions\n        for (path, method) in server_fn::axum::server_fn_paths() {\n            let cx_with_state = cx_with_state.clone();\n            let handler = move |req: Request<Body>| async move {\n                handle_server_fns_with_context(cx_with_state, req).await\n            };\n\n            if !excluded.contains(path) {\n                router = router.route(\n                    path,\n                    match method {\n                        Method::GET => get(handler),\n                        Method::POST => post(handler),\n                        Method::PUT => put(handler),\n                        Method::DELETE => delete(handler),\n                        Method::PATCH => patch(handler),\n                        _ => {\n                            panic!(\n                                \"Unsupported server function HTTP method: \\\n                                 {method:?}\"\n                            );\n                        }\n                    },\n                );\n            }\n        }\n\n        // register router paths\n        for listing in paths.iter().filter(|p| !p.exclude) {\n            let path = listing.path();\n\n            for method in listing.methods() {\n                let cx_with_state = cx_with_state.clone();\n                let cx_with_state_and_method = move || {\n                    provide_context(method);\n                    cx_with_state();\n                };\n                router = if matches!(listing.mode(), SsrMode::Static(_)) {\n                    #[cfg(feature = \"default\")]\n                    {\n                        router.route(\n                            path,\n                            get(handle_static_route(\n                                cx_with_state_and_method.clone(),\n                                app_fn.clone(),\n                                listing.regenerate.clone(),\n                            )),\n                        )\n                    }\n                    #[cfg(not(feature = \"default\"))]\n                    {\n                        panic!(\n                            \"Static routes are not currently supported on \\\n                             WASM32 server targets.\"\n                        );\n                    }\n                } else {\n                    router.route(\n                        path,\n                        match listing.mode() {\n                            SsrMode::OutOfOrder => {\n                                let s = render_app_to_stream_with_context(\n                                    cx_with_state_and_method.clone(),\n                                    app_fn.clone(),\n                                );\n                                match method {\n                                    leptos_router::Method::Get => get(s),\n                                    leptos_router::Method::Post => post(s),\n                                    leptos_router::Method::Put => put(s),\n                                    leptos_router::Method::Delete => delete(s),\n                                    leptos_router::Method::Patch => patch(s),\n                                }\n                            }\n                            SsrMode::PartiallyBlocked => {\n                                let s = render_app_to_stream_with_context_and_replace_blocks(\n                                    cx_with_state_and_method.clone(),\n                                    app_fn.clone(),\n                                    true\n                                );\n                                match method {\n                                    leptos_router::Method::Get => get(s),\n                                    leptos_router::Method::Post => post(s),\n                                    leptos_router::Method::Put => put(s),\n                                    leptos_router::Method::Delete => delete(s),\n                                    leptos_router::Method::Patch => patch(s),\n                                }\n                            }\n                            SsrMode::InOrder => {\n                                let s = render_app_to_stream_in_order_with_context(\n                                    cx_with_state_and_method.clone(),\n                                    app_fn.clone(),\n                                );\n                                match method {\n                                    leptos_router::Method::Get => get(s),\n                                    leptos_router::Method::Post => post(s),\n                                    leptos_router::Method::Put => put(s),\n                                    leptos_router::Method::Delete => delete(s),\n                                    leptos_router::Method::Patch => patch(s),\n                                }\n                            }\n                            SsrMode::Async => {\n                                let s = render_app_async_with_context(\n                                    cx_with_state_and_method.clone(),\n                                    app_fn.clone(),\n                                );\n                                match method {\n                                    leptos_router::Method::Get => get(s),\n                                    leptos_router::Method::Post => post(s),\n                                    leptos_router::Method::Put => put(s),\n                                    leptos_router::Method::Delete => delete(s),\n                                    leptos_router::Method::Patch => patch(s),\n                                }\n                            }\n                            _ => unreachable!()\n                        },\n                    )\n                };\n            }\n        }\n\n        router\n    }\n\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", fields(error), skip_all)\n    )]\n    fn leptos_routes_with_handler<H, T>(\n        self,\n        paths: Vec<AxumRouteListing>,\n        handler: H,\n    ) -> Self\n    where\n        H: axum::handler::Handler<T, S>,\n        T: 'static,\n    {\n        let mut router = self;\n        for listing in paths.iter().filter(|p| !p.exclude) {\n            for method in listing.methods() {\n                router = router.route(\n                    listing.path(),\n                    match method {\n                        leptos_router::Method::Get => get(handler.clone()),\n                        leptos_router::Method::Post => post(handler.clone()),\n                        leptos_router::Method::Put => put(handler.clone()),\n                        leptos_router::Method::Delete => {\n                            delete(handler.clone())\n                        }\n                        leptos_router::Method::Patch => patch(handler.clone()),\n                    },\n                );\n            }\n        }\n        router\n    }\n}\n\n/// A helper to make it easier to use Axum extractors in server functions.\n///\n/// It is generic over some type `T` that implements [`FromRequestParts`] and can\n/// therefore be used in an extractor. The compiler can often infer this type.\n///\n/// Any error that occurs during extraction is converted to a [`ServerFnError`].\n///\n/// ```rust\n/// use leptos::prelude::*;\n///\n/// #[server]\n/// pub async fn request_method() -> Result<String, ServerFnError> {\n///     use axum::http::Method;\n///     use leptos_axum::extract;\n///\n///     // you can extract anything that a regular Axum extractor can extract\n///     // from the head (not from the body of the request)\n///     let method: Method = extract().await?;\n///\n///     Ok(format!(\"{method:?}\"))\n/// }\n/// ```\npub async fn extract<T>() -> Result<T, ServerFnErrorErr>\nwhere\n    T: Sized + FromRequestParts<()>,\n    T::Rejection: Debug,\n{\n    extract_with_state::<T, ()>(&()).await\n}\n\n/// A helper to make it easier to use Axum extractors in server functions. This\n/// function is compatible with extractors that require access to `State`.\n///\n/// It is generic over some type `T` that implements [`FromRequestParts`] and can\n/// therefore be used in an extractor. The compiler can often infer this type.\n///\n/// Any error that occurs during extraction is converted to a [`ServerFnError`].\npub async fn extract_with_state<T, S>(state: &S) -> Result<T, ServerFnErrorErr>\nwhere\n    T: Sized + FromRequestParts<S>,\n    T::Rejection: Debug,\n{\n    let mut parts = use_context::<Parts>().ok_or_else(|| {\n        ServerFnErrorErr::ServerError(\n            \"should have had Parts provided by the leptos_axum integration\"\n                .to_string(),\n        )\n    })?;\n    T::from_request_parts(&mut parts, state)\n        .await\n        .map_err(|e| ServerFnErrorErr::ServerError(format!(\"{e:?}\")))\n}\n\n/// A reasonable handler for serving static files (like JS/WASM/CSS) and 404 errors.\n///\n/// This is provided as a convenience, but is a fairly simple function. If you need to adapt it,\n/// simply reuse the source code of this function in your own application.  A more compositional\n/// implementation is offered by [`ErrorHandler`] as it implements a tower [`Service`] which\n/// may be composed with other tower services.\n///\n/// [`Service`]: tower::Service\n#[cfg(feature = \"default\")]\npub fn file_and_error_handler_with_context<S, IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    shell: impl Fn(LeptosOptions) -> IV + 'static + Clone + Send,\n) -> impl Fn(\n    Uri,\n    State<S>,\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n    S: Send + Sync + Clone + 'static,\n    LeptosOptions: FromRef<S>,\n{\n    move |uri: Uri, State(state): State<S>, req: Request<Body>| {\n        Box::pin({\n            let additional_context = additional_context.clone();\n            let shell = shell.clone();\n            async move {\n                let options = LeptosOptions::from_ref(&state);\n                let res =\n                    get_static_file(uri, &options.site_root, req.headers());\n                let res = res.await.unwrap();\n\n                if res.status() == StatusCode::OK {\n                    let owner = Owner::new();\n                    owner.with(|| {\n                        additional_context();\n                        let res = res.into_response();\n                        if let Some(response_options) =\n                            use_context::<ResponseOptions>()\n                        {\n                            let mut res = AxumResponse(res);\n                            res.extend_response(&response_options);\n                            res.0\n                        } else {\n                            res\n                        }\n                    })\n                } else {\n                    let mut res = handle_response_inner(\n                        move || {\n                            provide_context(state.clone());\n                            additional_context();\n                        },\n                        move || shell(options),\n                        req,\n                        |app, chunks, _supports_ooo| {\n                            Box::pin(async move {\n                                let app = if cfg!(feature = \"islands-router\") {\n                                    app.to_html_stream_in_order_branching()\n                                } else {\n                                    app.to_html_stream_in_order()\n                                };\n                                let app = app.collect::<String>().await;\n                                let chunks = chunks();\n                                Box::pin(once(async move { app }).chain(chunks))\n                                    as PinnedStream<String>\n                            })\n                        },\n                    )\n                    .await;\n\n                    // set the status to 404\n                    // but if the status was already set (for example, to a 302 redirect) don't\n                    // overwrite it\n                    let status = res.status_mut();\n                    if *status == StatusCode::OK {\n                        *res.status_mut() = StatusCode::NOT_FOUND;\n                    }\n\n                    res\n                }\n            }\n        })\n    }\n}\n\n/// A reasonable handler for serving static files (like JS/WASM/CSS) and 404 errors.\n///\n/// This is provided as a convenience, but is a fairly simple function. If you need to adapt it,\n/// simply reuse the source code of this function in your own application.  A more compositional\n/// implementation is offered by [`ErrorHandler`] as it implements a tower [`Service`] which\n/// may be composed with other tower services.\n///\n/// [`Service`]: tower::Service\n#[cfg(feature = \"default\")]\npub fn file_and_error_handler<S, IV>(\n    shell: impl Fn(LeptosOptions) -> IV + 'static + Clone + Send,\n) -> impl Fn(\n    Uri,\n    State<S>,\n    Request<Body>,\n) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>\n       + Clone\n       + Send\n       + 'static\nwhere\n    IV: IntoView + 'static,\n    S: Send + Sync + Clone + 'static,\n    LeptosOptions: FromRef<S>,\n{\n    file_and_error_handler_with_context(move || (), shell)\n}\n\n#[cfg(feature = \"default\")]\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n    headers: &HeaderMap<HeaderValue>,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    use axum::http::header::ACCEPT_ENCODING;\n\n    let req = Request::builder().uri(uri);\n\n    let req = match headers.get(ACCEPT_ENCODING) {\n        Some(value) => req.header(ACCEPT_ENCODING, value),\n        None => req,\n    };\n\n    let req = req.body(Body::empty()).unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root)\n        .precompressed_gzip()\n        .precompressed_br()\n        .oneshot(req)\n        .await\n    {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n\n/// A helper to create a [`ServeDir`] service for the static files under\n/// `LEPTOS_SITE_ROOT`.  This may be further configured before being assigned\n/// as the fallback service, or be attached as a service route on the router,\n/// typically with the path derived from [`site_pkg_dir_service_route_path`].\n///\n/// [`ServeDir`]: tower_http::services::ServeDir\n#[cfg(feature = \"default\")]\npub fn site_pkg_dir_service(options: &LeptosOptions) -> ServeDir {\n    ServeDir::new(&*options.site_root)\n        .precompressed_gzip()\n        .precompressed_br()\n}\n\n/// A helper for constructing the axum route path from the `LeptosOptions`, can be used\n/// in conjunction with the [`ServeDir`] service produced by [`site_pkg_dir_service`]\n/// for setting up a routed site pkg service with [`Router::route_service`].\n///\n/// [`ServeDir`]: tower_http::services::ServeDir\npub fn site_pkg_dir_service_route_path(options: &LeptosOptions) -> String {\n    // The path of the route being built will be constained to serve only the\n    // contents of `site_pkg_dir` to avoid conflicts with the root routes.\n    let mut path = String::new();\n    // While it shouldn't start with a '/', but check anyway.\n    if !options.site_pkg_dir.starts_with('/') {\n        path.push('/');\n    }\n    path.push_str(&options.site_pkg_dir);\n    if !path.ends_with('/') {\n        path.push('/');\n    }\n    path.push_str(\"{*path}\");\n    path\n}\n"
  },
  {
    "path": "integrations/axum/src/service.rs",
    "content": "use crate::{handle_response_inner, PinnedStream};\nuse axum::{\n    body::Body,\n    http::{Request, Response, StatusCode},\n};\nuse futures::{stream::once, Future, StreamExt};\nuse leptos::{config::LeptosOptions, context::provide_context, IntoView};\nuse std::{\n    convert::Infallible,\n    pin::Pin,\n    task::{Context, Poll},\n};\nuse tower::Service;\n\n/// Service for serving error pages generated with the provided application shell.\n///\n/// This error handler is typically set up as a fallback service on some other services, such as the\n/// Axum's Router set up with a Leptos app, and is provided as a tower [`Service`] to enable composition\n/// with other tower services.\n///\n/// The behavior of [`file_and_error_handler`] can be approximately replicated with the following by\n/// composing with the [`ServeDir`] service returned by [`site_pkg_dir_service`].\n///\n/// [`file_and_error_handler`]: crate::file_and_error_handler\n/// [`site_pkg_dir_service`]: crate::site_pkg_dir_service\n/// [`Service`]: tower::Service\n/// [`ServeDir`]: tower_http::services::ServeDir\n///\n/// ```\n/// # use axum::Router;\n/// # use leptos::prelude::*;\n/// # use leptos_axum::{LeptosRoutes, generate_route_list};\n/// # #[component]\n/// # fn App() -> impl IntoView {\n/// #     view! { <main>\"Hello, world!\"</main> }\n/// # }\n/// # let conf = get_configuration(None).unwrap();\n/// # let addr = conf.leptos_options.site_addr;\n/// # let leptos_options = conf.leptos_options;\n/// # let routes = generate_route_list(App);\n/// fn shell(options: LeptosOptions) -> impl IntoView {\n///     view! { <App/> }\n/// }\n///\n/// let app = Router::new()\n///     .leptos_routes(&leptos_options, routes, {\n///         let leptos_options = leptos_options.clone();\n///         move || shell(leptos_options.clone())\n///     })\n///     // the following `fallback_service(...)` call approximately replicates\n///     // .fallback(leptos_axum::file_and_error_handler(shell))\n///     .fallback_service(\n///         leptos_axum::site_pkg_dir_service(&leptos_options).fallback(\n///             leptos_axum::ErrorHandler::new(shell, leptos_options),\n///         ),\n///     );\n/// ```\n#[derive(Clone, Debug)]\npub struct ErrorHandler<CX, SH> {\n    additional_context: CX,\n    shell: SH,\n    options: LeptosOptions,\n}\n\nimpl<SH> ErrorHandler<(), SH> {\n    /// Create a new handler with the provided shell and options.\n    pub fn new(shell: SH, options: LeptosOptions) -> Self {\n        Self {\n            additional_context: (),\n            shell,\n            options,\n        }\n    }\n}\n\nimpl<CX, SH> ErrorHandler<CX, SH> {\n    /// Create a new handler with an additional context along with the provided shell and options.\n    pub fn new_with_context(\n        additional_context: CX,\n        shell: SH,\n        options: LeptosOptions,\n    ) -> Self {\n        Self {\n            additional_context,\n            shell,\n            options,\n        }\n    }\n}\n\nimpl<SH, IV> Service<Request<Body>> for ErrorHandler<(), SH>\nwhere\n    SH: Fn(LeptosOptions) -> IV + 'static + Clone + Send,\n    IV: IntoView + 'static,\n{\n    type Response = Response<Body>;\n    type Error = Infallible;\n    type Future = Pin<\n        Box<\n            dyn Future<Output = Result<Response<Body>, Infallible>>\n                + Send\n                + 'static,\n        >,\n    >;\n\n    #[inline]\n    fn poll_ready(\n        &mut self,\n        _cx: &mut Context<'_>,\n    ) -> Poll<Result<(), Self::Error>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn call(&mut self, req: Request<Body>) -> Self::Future {\n        let options = self.options.clone();\n        let shell = self.shell.clone();\n        render_error_handler(|| {}, shell, options, req)\n    }\n}\n\nimpl<CX, SH, IV> Service<Request<Body>> for ErrorHandler<CX, SH>\nwhere\n    CX: Fn() + 'static + Clone + Send,\n    SH: Fn(LeptosOptions) -> IV + 'static + Clone + Send,\n    IV: IntoView + 'static,\n{\n    type Response = Response<Body>;\n    type Error = Infallible;\n    type Future = Pin<\n        Box<\n            dyn Future<Output = Result<Response<Body>, Infallible>>\n                + Send\n                + 'static,\n        >,\n    >;\n\n    #[inline]\n    fn poll_ready(\n        &mut self,\n        _cx: &mut Context<'_>,\n    ) -> Poll<Result<(), Self::Error>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn call(&mut self, req: Request<Body>) -> Self::Future {\n        let options = self.options.clone();\n        let shell = self.shell.clone();\n        let additional_context = self.additional_context.clone();\n        render_error_handler(additional_context, shell, options, req)\n    }\n}\n\nfn render_error_handler<IV>(\n    additional_context: impl Fn() + 'static + Clone + Send,\n    shell: impl Fn(LeptosOptions) -> IV + 'static + Clone + Send,\n    options: LeptosOptions,\n    req: Request<Body>,\n) -> Pin<\n    Box<\n        dyn Future<Output = Result<Response<Body>, Infallible>>\n            + Send\n            + 'static,\n    >,\n>\nwhere\n    IV: IntoView + 'static,\n{\n    Box::pin(async move {\n        let mut res = handle_response_inner(\n            {\n                let options = options.clone();\n                let additional_context = additional_context.clone();\n                move || {\n                    provide_context(options.clone());\n                    additional_context();\n                }\n            },\n            {\n                let options = options.clone();\n                let shell = shell.clone();\n                move || shell(options)\n            },\n            req,\n            |app, chunks, _supports_ooo| {\n                Box::pin(async move {\n                    let app = if cfg!(feature = \"islands-router\") {\n                        app.to_html_stream_in_order_branching()\n                    } else {\n                        app.to_html_stream_in_order()\n                    };\n                    let app = app.collect::<String>().await;\n                    let chunks = chunks();\n                    Box::pin(once(async move { app }).chain(chunks))\n                        as PinnedStream<String>\n                })\n            },\n        )\n        .await;\n\n        // set the status to 404\n        // but if the status was already set (for example, to a 302 redirect) don't\n        // overwrite it\n        let status = res.status_mut();\n        if *status == StatusCode::OK {\n            *res.status_mut() = StatusCode::NOT_FOUND;\n        }\n\n        Ok(res)\n    })\n}\n"
  },
  {
    "path": "integrations/axum/tests/axum_integration.rs",
    "content": "use reqwest::{\n    header::{HeaderName, HeaderValue},\n    Client, StatusCode, Url,\n};\nuse std::{\n    path::Path,\n    process::Stdio,\n    sync::Once,\n    time::{Duration, Instant},\n};\nuse tempfile::TempDir;\nuse tokio::{\n    io::AsyncReadExt,\n    process::{Child, Command},\n    time::timeout,\n};\n\n#[tokio::test]\nasync fn bare_no_fallback() -> anyhow::Result<()> {\n    let service = start_test_service(\"service_mode\", \"bare\").await;\n    let client = Client::new();\n    // this version has no fallbacks attached, so no other response, no error page.\n    let res = client\n        .get(service.url(\"/pkg/service_mode.js\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    assert_eq!(res.content_length(), Some(0));\n    Ok(())\n}\n\n#[tokio::test]\nasync fn fallback() -> anyhow::Result<()> {\n    let service = start_test_service(\"service_mode\", \"fallback\").await;\n    let client = Client::new();\n    // should provide the two site artifacts.\n    let res = client\n        .get(service.url(\"/pkg/service_mode.js\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_ne!(res.content_length(), Some(0));\n    let res = client\n        .get(service.url(\"/pkg/service_mode.wasm\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_ne!(res.content_length(), Some(0));\n    // the basic fallback will also have a shell to render the 404 Not Found\n    let res = client.get(service.url(\"/pkg/no_such_path\")?).send().await?;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    assert_ne!(res.content_length(), Some(0));\n    assert!(res\n        .text()\n        .await?\n        .contains(\"<title>Error from fallback</title>\"));\n    Ok(())\n}\n\n#[tokio::test]\nasync fn fallback_with_context() -> anyhow::Result<()> {\n    // ensure fixes implemented in #4394 for the headers to show up actually do show up.\n    let service =\n        start_test_service(\"service_mode\", \"fallback-with-context\").await;\n    let client = Client::new();\n    let res = client\n        .get(service.url(\"/pkg/service_mode.wasm\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_ne!(res.content_length(), Some(0));\n    assert_eq!(\n        res.headers()\n            .get(HeaderName::from_static(\"cross-origin-opener-policy\")),\n        Some(&HeaderValue::from_static(\"same-origin\")),\n    );\n    assert_eq!(\n        res.headers()\n            .get(HeaderName::from_static(\"cross-origin-embedder-policy\")),\n        Some(&HeaderValue::from_static(\"require-corp\")),\n    );\n    Ok(())\n}\n\n#[tokio::test]\nasync fn error_handler_service() -> anyhow::Result<()> {\n    let service =\n        start_test_service(\"service_mode\", \"error-handler-service\").await;\n    let client = Client::new();\n    // no site artifact, but has the error page as only the error handler is applied\n    let res = client\n        .get(service.url(\"/pkg/service_mode.js\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    assert_ne!(res.content_length(), Some(0));\n    assert!(res\n        .text()\n        .await?\n        .contains(\"<title>Error from fallback</title>\"));\n    Ok(())\n}\n\n#[tokio::test]\nasync fn error_handler_service_fallback() -> anyhow::Result<()> {\n    let service =\n        start_test_service(\"service_mode\", \"error-handler-service-fallback\")\n            .await;\n    let client = Client::new();\n    // should provide the two site artifacts.\n    let res = client\n        .get(service.url(\"/pkg/service_mode.js\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_ne!(res.content_length(), Some(0));\n    let res = client\n        .get(service.url(\"/pkg/service_mode.wasm\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_ne!(res.content_length(), Some(0));\n    // this composed service falback setup is similar to the basic non-service fallback setup.\n    let res = client.get(service.url(\"/pkg/no_such_path\")?).send().await?;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    assert_ne!(res.content_length(), Some(0));\n    assert!(res\n        .text()\n        .await?\n        .contains(\"<title>Error from fallback</title>\"));\n    Ok(())\n}\n\n#[tokio::test]\nasync fn route_site_pkg_no_fallback() -> anyhow::Result<()> {\n    let service =\n        start_test_service(\"service_mode\", \"route-site-pkg-no-fallback\").await;\n    let client = Client::new();\n    // should provide the two site artifacts.\n    let res = client\n        .get(service.url(\"/pkg/service_mode.js\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_ne!(res.content_length(), Some(0));\n    let res = client\n        .get(service.url(\"/pkg/service_mode.wasm\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert_ne!(res.content_length(), Some(0));\n    // there is no fallback assigned to the routes under /pkg/ under this setup, so no error page\n    let res = client.get(service.url(\"/pkg/no_such_path\")?).send().await?;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    assert_eq!(res.content_length(), Some(0));\n    // however, the fallback service will trigger for all other unrouted paths.\n    let res = client\n        .get(service.url(\"/no_such_path_elsewhere\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::NOT_FOUND);\n    assert_ne!(res.content_length(), Some(0));\n    assert!(res\n        .text()\n        .await?\n        .contains(\"<title>Error from fallback</title>\"));\n    Ok(())\n}\n\n#[tokio::test]\nasync fn leptos_options_css_base() -> anyhow::Result<()> {\n    let service =\n        start_test_service(\"service_mode\", \"leptos-options-css-base\").await;\n    let client = Client::new();\n    // should route the css file that was setup using `LeptosOptions`.\n    let res = client\n        .get(service.url(\"/pkg/service_mode.css\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert!(res.text().await?.contains(\"font-family: sans-serif;\"));\n    Ok(())\n}\n\n#[tokio::test]\nasync fn leptos_options_css_moved() -> anyhow::Result<()> {\n    build_services();\n    // Note: this test does not test the behavior of `cargo-leptos` but instead will test how the\n    // end user might configure the files on the server and how they might set the environment variables\n    // to reflect their configuration before starting their axum based service, where those variables will\n    // influence values provided by `LeptosOptions`.\n    let working_dir = Path::new(\"tests\").join(\"service_mode\");\n    let site_root = TempDir::new()?;\n    let my_pkg = Path::new(\"my\").join(\"pkg\");\n    let dest = site_root.path().join(my_pkg);\n    std::fs::create_dir_all(&dest)?;\n    std::fs::copy(\n        working_dir\n            .join(\"target\")\n            .join(\"site\")\n            .join(\"pkg\")\n            .join(\"service_mode.css\"),\n        dest.join(\"service_mode.css\"),\n    )?;\n\n    let service = start_test_service_with_envs(\n        \"service_mode\",\n        \"leptos-options-css-base\",\n        vec![\n            (\n                \"LEPTOS_SITE_ROOT\",\n                site_root.path().to_str().expect(\"valid utf8\"),\n            ),\n            (\"LEPTOS_SITE_PKG_DIR\", \"my/pkg\"),\n        ],\n    )\n    .await;\n    let client = Client::new();\n    // should route the css file that was setup using `LeptosOptions`.\n    let res = client\n        .get(service.url(\"/my/pkg/service_mode.css\")?)\n        .send()\n        .await?;\n    assert_eq!(res.status(), StatusCode::OK);\n    assert!(res.text().await?.contains(\"font-family: sans-serif;\"));\n    Ok(())\n}\n\n// Killing `cargo leptos watch` may not necessarily kill the underlying server task, so rather\n// than running that, build and run the service in separate steps.  This also has the advantage\n// of avoiding parallel build issues with generating the site onto the same location.\nfn build_test_service(name: &str) {\n    // this assumes the current working dir is at the root of this crate, i.e. `integration/axum`.\n    let working_dir = Path::new(\"tests\").join(name);\n\n    // If set, assume that `cargo-nextest` is running this and that it already built this service.\n    if std::env::var(\"NEXTEST\").as_deref() == Ok(\"1\") {\n        return;\n    }\n    // TODO provide the ability to skip this step if and only if the source code hasn't been changed\n    // to not require using cargo-nextest setup scripts to prepare this.  Essentially if this is done\n    // it will become possible to parallelize in both `cargo test` and `cargo nextest` correctly.\n\n    let cmd = Command::new(\"cargo\");\n    let mut build = cmd\n        .into_std()\n        .arg(\"leptos\")\n        .arg(\"build\")\n        // need to manually specify this to avoid mismatch between this value that may be set (e.g.\n        // during CI) and the `output-name` defined in Cargo.toml for this relevant project.\n        .env(\"LEPTOS_OUTPUT_NAME\", name)\n        .current_dir(&working_dir)\n        .spawn()\n        .expect(\"cargo leptos build should start\");\n    if !build\n        .wait()\n        .expect(\"there shouldn't be i/o error\")\n        .success()\n    {\n        panic!(\"failed to run `cargo leptos build`\");\n    }\n}\n\nstruct Service {\n    _child: Child,\n    port: u16,\n}\n\nimpl Service {\n    fn url(&self, path: &str) -> anyhow::Result<Url> {\n        Ok(format!(\"http://127.0.0.1:{}/\", self.port)\n            .parse::<Url>()?\n            .join(path)?)\n    }\n}\n\nstatic BUILDER: Once = Once::new();\n\nfn build_services() {\n    // Note that it should build every single services that may be started\n    // ANOTHER_BUILDER.call_once(|| build_test_service(\"another_service\"));\n    BUILDER.call_once(|| build_test_service(\"service_mode\"));\n}\n\nasync fn start_test_service(name: &str, mode: &str) -> Service {\n    start_test_service_with_envs(name, mode, vec![]).await\n}\n\nasync fn start_test_service_with_envs(\n    name: &str,\n    mode: &str,\n    vars: Vec<(&str, &str)>,\n) -> Service {\n    build_services();\n    // the time limit to wait for service to start and listen\n    let ttl = Duration::from_secs(5);\n    // this assumes the current working dir is at the root of this crate, i.e. `integration/axum`.\n    let working_dir = Path::new(\"tests\").join(name);\n\n    let mut child = Command::new(Path::new(\"target\").join(\"debug\").join(name))\n        .arg(mode)\n        .kill_on_drop(true)\n        .current_dir(&working_dir)\n        .env(\"LEPTOS_SITE_ADDR\", \"127.0.0.1:0\")\n        // need to manually specify this to avoid mismatch between this value that may be set (e.g.\n        // during CI) and the `output-name` defined in Cargo.toml for this relevant project.\n        .env(\"LEPTOS_OUTPUT_NAME\", name)\n        .envs(vars)\n        .stdout(Stdio::piped())\n        .spawn()\n        .expect(\"the service should have been built and can start\");\n\n    let mut stdout = child.stdout.take().expect(\"stdout is not captured\");\n\n    let buff = tokio::spawn(timeout(ttl, async move {\n        let mut buff = Vec::new();\n        let _ = stdout.read_buf(&mut buff).await;\n        buff\n    }))\n    .await\n    .unwrap();\n\n    let start_time = Instant::now();\n\n    let port = str::from_utf8(&buff.unwrap())\n        .unwrap()\n        .trim()\n        .parse()\n        .unwrap();\n\n    let _child = child;\n    let service = Service { _child, port };\n    let client = Client::new();\n\n    while start_time.elapsed() < ttl {\n        if client\n            .get(service.url(\"/\").unwrap())\n            .timeout(ttl)\n            .send()\n            .await\n            .is_ok()\n        {\n            return service;\n        }\n        tokio::time::sleep(Duration::from_secs(1)).await;\n    }\n    panic!(\"The web server did not become ready within the expected time.\");\n}\n"
  },
  {
    "path": "integrations/axum/tests/service_mode/.gitignore",
    "content": "Cargo.lock\n"
  },
  {
    "path": "integrations/axum/tests/service_mode/Cargo.toml",
    "content": "[workspace]\n\n[package]\nname = \"service_mode\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.8.1\", optional = true }\nclap = { version = \"4.5\", features = [\"derive\"], optional = true }\nconsole_error_panic_hook = \"0.1.7\"\nleptos = { path = \"../../../../leptos\", features = [\"tracing\"] }\nleptos_meta = { path = \"../../../../meta\" }\nleptos_axum = { path = \"../../../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../../../router\" }\ntokio = { version = \"1.39\", features = [\n  \"rt-multi-thread\",\n  \"macros\",\n  \"time\",\n], optional = true }\ntower = { version = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\nwasm-bindgen = \"0.2.106\"\n\n[features]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:clap\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"dep:leptos_axum\",\n  \"leptos_router/ssr\",\n]\n\n[profile.release]\npanic = \"abort\"\n\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.cargo-all-features]\nskip_feature_sets = [[\"ssr\", \"hydrate\"], []]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"service_mode\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.css\"\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\n# assets-dir = \"assets\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\n# end2end-cmd = \"npx playwright test\"\n# end2end-dir = \"end2end\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "integrations/axum/tests/service_mode/src/app.rs",
    "content": "use leptos::prelude::*;\nuse leptos_meta::{MetaTags, *};\nuse leptos_router::{\n    StaticSegment,\n    components::{FlatRoutes, Route, Router},\n};\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone()/>\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n    let fallback = || {\n        view! {\n            <Title text=\"Error from fallback\"/>\n            <h1>\"This is fallback rendering.\"</h1>\n        }\n        .into_view()\n    };\n\n    view! {\n        <Router>\n            <nav>\n                <a href=\"/\">\"Home\"</a>\n            </nav>\n            <main>\n                <FlatRoutes fallback>\n                    <Route path=StaticSegment(\"\") view=HomePage/>\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[component]\nfn HomePage() -> impl IntoView {\n    view! {\n        <h1>\"Home Page\"</h1>\n    }\n}\n"
  },
  {
    "path": "integrations/axum/tests/service_mode/src/lib.rs",
    "content": "pub mod app;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n"
  },
  {
    "path": "integrations/axum/tests/service_mode/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\nmod router {\n    use axum::{\n        Router,\n        http::{HeaderName, HeaderValue},\n    };\n    use clap::{Parser, Subcommand};\n    use leptos::prelude::{get_configuration, provide_context, use_context};\n    use leptos_axum::{ErrorHandler, LeptosRoutes, generate_route_list};\n    use service_mode::app::{App, shell};\n\n    #[derive(Parser)]\n    pub struct Cli {\n        #[command(subcommand)]\n        mode: Mode,\n    }\n\n    #[derive(Subcommand)]\n    enum Mode {\n        Bare,\n        Fallback,\n        FallbackWithContext,\n        ErrorHandlerService,\n        ErrorHandlerServiceFallback,\n        RouteSitePkgNoFallback,\n\n        LeptosOptionsCssBase,\n    }\n\n    impl From<Cli> for Router {\n        fn from(cli: Cli) -> Self {\n            let conf = get_configuration(None).unwrap();\n            let leptos_options = conf.leptos_options;\n            let routes = generate_route_list(App);\n\n            match cli.mode {\n                Mode::Bare => Router::new()\n                    .leptos_routes(&leptos_options, routes, {\n                        let leptos_options = leptos_options.clone();\n                        move || shell(leptos_options.clone())\n                    })\n                    .with_state(leptos_options),\n                Mode::Fallback => Router::new()\n                    .leptos_routes(&leptos_options, routes, {\n                        let leptos_options = leptos_options.clone();\n                        move || shell(leptos_options.clone())\n                    })\n                    .fallback(leptos_axum::file_and_error_handler(shell))\n                    .with_state(leptos_options),\n                Mode::FallbackWithContext => Router::new()\n                    .leptos_routes(&leptos_options, routes, {\n                        let leptos_options = leptos_options.clone();\n                        move || shell(leptos_options.clone())\n                    })\n                    .fallback(leptos_axum::file_and_error_handler_with_context(\n                        move || {\n                            let opts =\n                                use_context::<leptos_axum::ResponseOptions>()\n                                    .unwrap_or_default();\n                            opts.insert_header(\n                                HeaderName::from_static(\n                                    \"cross-origin-opener-policy\",\n                                ),\n                                HeaderValue::from_static(\"same-origin\"),\n                            );\n                            opts.insert_header(\n                                HeaderName::from_static(\n                                    \"cross-origin-embedder-policy\",\n                                ),\n                                HeaderValue::from_static(\"require-corp\"),\n                            );\n                            provide_context(opts);\n                        },\n                        shell,\n                    ))\n                    .with_state(leptos_options),\n                Mode::ErrorHandlerService => Router::new()\n                    .leptos_routes(&leptos_options, routes, {\n                        let leptos_options = leptos_options.clone();\n                        move || shell(leptos_options.clone())\n                    })\n                    .fallback_service(ErrorHandler::new(\n                        shell,\n                        leptos_options.clone(),\n                    ))\n                    .with_state(leptos_options),\n                Mode::ErrorHandlerServiceFallback => Router::new()\n                    .leptos_routes(&leptos_options, routes, {\n                        let leptos_options = leptos_options.clone();\n                        move || shell(leptos_options.clone())\n                    })\n                    .fallback_service(\n                        leptos_axum::site_pkg_dir_service(&leptos_options)\n                            .fallback(ErrorHandler::new(\n                                shell,\n                                leptos_options.clone(),\n                            )),\n                    )\n                    .with_state(leptos_options),\n                Mode::RouteSitePkgNoFallback => Router::new()\n                    .leptos_routes(&leptos_options, routes, {\n                        let leptos_options = leptos_options.clone();\n                        move || shell(leptos_options.clone())\n                    })\n                    .route_service(\n                        &leptos_axum::site_pkg_dir_service_route_path(\n                            &leptos_options,\n                        ),\n                        leptos_axum::site_pkg_dir_service(&leptos_options),\n                    )\n                    .fallback_service(ErrorHandler::new(\n                        shell,\n                        leptos_options.clone(),\n                    ))\n                    .with_state(leptos_options),\n\n                Mode::LeptosOptionsCssBase => Router::new().nest(\n                    &leptos_options.css_path(),\n                    Router::new().route_service(\n                        \"/\",\n                        tower_http::services::ServeFile::new(\n                            &leptos_options.css_file_path(),\n                        ),\n                    ),\n                ),\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use clap::Parser;\n    use leptos::prelude::get_configuration;\n\n    let app = Router::from(router::Cli::parse());\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    // write out the port from the bounded local_addr to allow the caller to know how to connect.\n    println!(\"{}\", listener.local_addr().unwrap().port());\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {}\n"
  },
  {
    "path": "integrations/axum/tests/service_mode/style/main.css",
    "content": "* { font-family: sans-serif; }\n"
  },
  {
    "path": "integrations/utils/Cargo.toml",
    "content": "[package]\nname = \"leptos_integration_utils\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Utilities to help build server integrations for the Leptos web framework.\"\nversion = \"0.8.8\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nfutures = { workspace = true, default-features = true }\nhydration_context = { workspace = true }\nleptos = { workspace = true, features = [\"nonce\"] }\nleptos_meta = { workspace = true, features = [\"ssr\"] }\nleptos_router = { workspace = true, features = [\"ssr\"] }\nleptos_config = { workspace = true }\nreactive_graph = { workspace = true, features = [\"sandboxed-arenas\"] }\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n"
  },
  {
    "path": "integrations/utils/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../../\" }\n"
  },
  {
    "path": "integrations/utils/src/lib.rs",
    "content": "#![allow(clippy::type_complexity)]\n\nuse futures::{stream::once, Stream, StreamExt};\nuse hydration_context::{SharedContext, SsrSharedContext};\nuse leptos::{\n    context::provide_context,\n    nonce::use_nonce,\n    prelude::ReadValue,\n    reactive::owner::{Owner, Sandboxed},\n    IntoView, PrefetchLazyFn, WasmSplitManifest,\n};\nuse leptos_config::LeptosOptions;\nuse leptos_meta::{Link, ServerMetaContextOutput};\nuse std::{future::Future, pin::Pin, sync::Arc};\n\npub type PinnedStream<T> = Pin<Box<dyn Stream<Item = T> + Send>>;\npub type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;\npub type BoxedFnOnce<T> = Box<dyn FnOnce() -> T + Send>;\n\npub trait ExtendResponse: Sized {\n    type ResponseOptions: Send;\n\n    fn from_stream(stream: impl Stream<Item = String> + Send + 'static)\n        -> Self;\n\n    fn extend_response(&mut self, opt: &Self::ResponseOptions);\n\n    fn set_default_content_type(&mut self, content_type: &str);\n\n    fn from_app<IV>(\n        app_fn: impl FnOnce() -> IV + Send + 'static,\n        meta_context: ServerMetaContextOutput,\n        additional_context: impl FnOnce() + Send + 'static,\n        res_options: Self::ResponseOptions,\n        stream_builder: fn(\n            IV,\n            BoxedFnOnce<PinnedStream<String>>,\n            bool,\n        ) -> PinnedFuture<PinnedStream<String>>,\n        supports_ooo: bool,\n    ) -> impl Future<Output = Self> + Send\n    where\n        IV: IntoView + 'static,\n    {\n        async move {\n            let prefetches = PrefetchLazyFn::default();\n\n            let (owner, stream) = build_response(\n                app_fn,\n                additional_context,\n                stream_builder,\n                supports_ooo,\n            );\n\n            owner.with(|| provide_context(prefetches.clone()));\n\n            let sc = owner.shared_context().unwrap();\n\n            let stream = stream.await.ready_chunks(32).map(|n| n.join(\"\"));\n\n            while let Some(pending) = sc.await_deferred() {\n                pending.await;\n            }\n\n            if !prefetches.0.read_value().is_empty() {\n                use leptos::prelude::*;\n\n                let nonce =\n                    use_nonce().map(|n| n.to_string()).unwrap_or_default();\n                if let Some(manifest) = use_context::<WasmSplitManifest>() {\n                    let (pkg_path, manifest, wasm_split_file) =\n                        &*manifest.0.read_value();\n                    let prefetches = prefetches.0.read_value();\n\n                    let all_prefetches = prefetches.iter().flat_map(|key| {\n                        manifest.get(*key).into_iter().flatten()\n                    });\n\n                    for module in all_prefetches {\n                        // to_html() on leptos_meta components registers them with the meta context,\n                        // rather than returning HTML directly\n                        _ = view! {\n                            <Link\n                                rel=\"preload\"\n                                href=format!(\"{pkg_path}/{module}.wasm\")\n                                as_=\"fetch\"\n                                type_=\"application/wasm\"\n                                crossorigin=nonce.clone()\n                            />\n                        }\n                        .to_html();\n                    }\n                    _ = view! {\n                        <Link rel=\"modulepreload\" href=format!(\"{pkg_path}/{wasm_split_file}\") crossorigin=nonce/>\n                    }\n                    .to_html();\n                }\n            }\n\n            let mut stream = Box::pin(\n                meta_context.inject_meta_context(stream).await.then({\n                    let sc = Arc::clone(&sc);\n                    move |chunk| {\n                        let sc = Arc::clone(&sc);\n                        async move {\n                            while let Some(pending) = sc.await_deferred() {\n                                pending.await;\n                            }\n                            chunk\n                        }\n                    }\n                }),\n            );\n\n            // wait for the first chunk of the stream, then set the status and headers\n            let first_chunk = stream.next().await.unwrap_or_default();\n\n            let mut res = Self::from_stream(Sandboxed::new(\n                once(async move { first_chunk })\n                    .chain(stream)\n                    // drop the owner, cleaning up the reactive runtime,\n                    // once the stream is over\n                    .chain(once(async move {\n                        owner.unset_with_forced_cleanup();\n                        Default::default()\n                    })),\n            ));\n\n            res.extend_response(&res_options);\n\n            // Set the Content Type headers on all responses. This makes Firefox show the page source\n            // without complaining\n            res.set_default_content_type(\"text/html; charset=utf-8\");\n\n            res\n        }\n    }\n}\n\npub fn build_response<IV>(\n    app_fn: impl FnOnce() -> IV + Send + 'static,\n    additional_context: impl FnOnce() + Send + 'static,\n    stream_builder: fn(\n        IV,\n        BoxedFnOnce<PinnedStream<String>>,\n        // this argument indicates whether a request wants to support out-of-order streaming\n        // responses\n        bool,\n    ) -> PinnedFuture<PinnedStream<String>>,\n    is_islands_router_navigation: bool,\n) -> (Owner, PinnedFuture<PinnedStream<String>>)\nwhere\n    IV: IntoView + 'static,\n{\n    let shared_context = Arc::new(SsrSharedContext::new())\n        as Arc<dyn SharedContext + Send + Sync>;\n    let owner = Owner::new_root(Some(Arc::clone(&shared_context)));\n    let stream = Box::pin(Sandboxed::new({\n        let owner = owner.clone();\n        async move {\n            let stream = owner.with(|| {\n                additional_context();\n\n                // run app\n                let app = app_fn();\n\n                let nonce = use_nonce()\n                    .as_ref()\n                    .map(|nonce| format!(\" nonce=\\\"{nonce}\\\"\"))\n                    .unwrap_or_default();\n\n                let shared_context = Owner::current_shared_context().unwrap();\n\n                let chunks = Box::new({\n                    let shared_context = shared_context.clone();\n                    move || {\n                        Box::pin(shared_context.pending_data().unwrap().map(\n                            move |chunk| {\n                                format!(\"<script{nonce}>{chunk}</script>\")\n                            },\n                        ))\n                            as Pin<Box<dyn Stream<Item = String> + Send>>\n                    }\n                });\n\n                // convert app to appropriate response type\n                // and chain the app stream, followed by chunks\n                // in theory, we could select here, and intersperse them\n                // the problem is that during the DOM walk, that would be mean random <script> tags\n                // interspersed where we expect other children\n                //\n                // we also don't actually start hydrating until after the whole stream is complete,\n                // so it's not useful to send those scripts down earlier.\n                stream_builder(app, chunks, is_islands_router_navigation)\n            });\n\n            stream.await\n        }\n    }));\n    (owner, stream)\n}\n\npub fn static_file_path(options: &LeptosOptions, path: &str) -> String {\n    let trimmed_path = path.trim_start_matches('/');\n    let path = if trimmed_path.is_empty() {\n        \"index\"\n    } else {\n        trimmed_path\n    };\n    format!(\"{}/{}.html\", options.site_root, path)\n}\n"
  },
  {
    "path": "leptos/Cargo.toml",
    "content": "[package]\nname = \"leptos\"\nversion = \"0.8.17\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\nhomepage = \"https://leptos.dev/\"\ndescription = \"Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces.\"\nreadme = \"../README.md\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nthrow_error = { workspace = true }\nany_spawner = { workspace = true, features = [\n  \"wasm-bindgen\",\n  \"futures-executor\",\n] }\nbase64 = { optional = true, workspace = true, default-features = true }\ncfg-if = { workspace = true, default-features = true }\nhydration_context = { workspace = true }\neither_of = { workspace = true }\nleptos_dom = { workspace = true }\nleptos_hot_reload = { workspace = true }\nleptos_macro = { workspace = true }\nleptos_server = { workspace = true, features = [\"tachys\"] }\nleptos_config = { workspace = true }\nleptos-spin-macro = { optional = true, workspace = true, default-features = true }\noco_ref = { workspace = true }\nor_poisoned = { workspace = true }\npaste = { workspace = true, default-features = true }\nrand = { optional = true, workspace = true, default-features = true }\n# NOTE: While not used directly, `getrandom`'s `wasm_js` feature is needed when `rand` is used on WASM to\n#       avoid a compilation error\ngetrandom = { optional = true, workspace = true, default-features = true }\nreactive_graph = { workspace = true, features = [\"serde\"] }\nrustc-hash = { workspace = true, default-features = true }\ntachys = { workspace = true, features = [\n  \"reactive_graph\",\n  \"reactive_stores\",\n  \"oco\",\n] }\nthiserror = { workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\ntyped-builder = { workspace = true, default-features = true }\ntyped-builder-macro = { workspace = true, default-features = true }\nserde = { workspace = true, default-features = true }\nserde_json = { workspace = true, default-features = true }\nserver_fn = { workspace = true, features = [\"form-redirects\", \"browser\"] }\nweb-sys = { features = [\n  \"ShadowRoot\",\n  \"ShadowRootInit\",\n  \"ShadowRootMode\",\n], workspace = true, default-features = true }\nwasm-bindgen = { workspace = true, default-features = true }\nwasm-bindgen-futures = { workspace = true, default-features = true }\nserde_qs = { workspace = true, default-features = true }\nslotmap = { workspace = true, default-features = true }\nfutures = { workspace = true, default-features = true }\nsend_wrapper = { workspace = true, default-features = true }\nwasm_split_helpers = { workspace = true, default-features = true }\nsubsecond = { workspace = true, default-features = true, optional = true }\ndioxus-cli-config = { workspace = true, default-features = true, optional = true }\ndioxus-devtools = { workspace = true, default-features = true, optional = true }\n\n[features]\nhydration = [\n  \"reactive_graph/hydration\",\n  \"leptos_server/hydration\",\n  \"hydration_context/browser\",\n  \"leptos_dom/hydration\",\n]\ncsr = [\"leptos_macro/csr\", \"reactive_graph/effects\", \"getrandom?/wasm_js\"]\nhydrate = [\n  \"leptos_macro/hydrate\",\n  \"hydration\",\n  \"tachys/hydrate\",\n  \"reactive_graph/effects\",\n  \"getrandom?/wasm_js\",\n]\ndefault-tls = [\"server_fn/default-tls\"]\nrustls = [\"server_fn/rustls\"]\nssr = [\n  \"leptos_macro/ssr\",\n  \"leptos_server/ssr\",\n  \"server_fn/ssr\",\n  \"hydration\",\n  \"tachys/ssr\",\n]\nnightly = [\"leptos_macro/nightly\", \"reactive_graph/nightly\", \"tachys/nightly\"]\nrkyv = [\"server_fn/rkyv\", \"leptos_server/rkyv\"]\nbitcode = [\"server_fn/bitcode\"]\nbitcode-serde = [\"server_fn/bitcode-serde\"]\nserde-lite = [\"server_fn/serde-lite\", \"leptos_server/serde-lite\"]\ncbor = [\"server_fn/cbor\"]\nmsgpack = [\"server_fn/msgpack\"]\npostcard = [\"server_fn/postcard\"]\nmultipart = [\"server_fn/multipart\"]\ntracing = [\n  \"dep:tracing\",\n  \"reactive_graph/tracing\",\n  \"tachys/tracing\",\n  \"leptos_macro/tracing\",\n  \"leptos_dom/tracing\",\n  \"leptos_server/tracing\",\n]\nnonce = [\"base64\", \"rand\", \"dep:getrandom\"]\nspin = [\"leptos-spin-macro\"]\nislands = [\"leptos_macro/islands\"]\ntrace-component-props = [\n  \"leptos_macro/trace-component-props\",\n  \"leptos_dom/trace-component-props\",\n]\ndelegation = [\"tachys/delegation\"]\nislands-router = [\"tachys/mark_branches\"]\nsubsecond = [\n  \"reactive_graph/subsecond\",\n  \"dep:subsecond\",\n  \"dep:dioxus-cli-config\",\n  \"dep:dioxus-devtools\",\n  \"web-sys/Location\",\n  \"web-sys/MessageEvent\",\n  \"web-sys/WebSocket\",\n  \"web-sys/Window\",\n]\n\n[dev-dependencies]\ntokio = { features = [\n  \"rt-multi-thread\",\n  \"macros\",\n], workspace = true, default-features = true }\ntokio-test = { workspace = true, default-features = true }\nany_spawner = { workspace = true, features = [\"futures-executor\", \"tokio\"] }\n\n[build-dependencies]\nrustc_version = { workspace = true, default-features = true }\n\n# Having an erasure feature rather than normal --cfg erase_components for the proc macro crate is a workaround for this rust issue:\n# https://github.com/rust-lang/cargo/issues/4423\n# TLDR proc macros will ignore RUSTFLAGS when --target is specified on the cargo command.\n# This works around the issue by the non proc-macro crate which does see RUSTFLAGS enabling the replacement feature on the proc-macro crate, which wouldn't.\n# This is automatic as long as the leptos crate is depended upon,\n# downstream usage should never manually enable this feature.\n[target.'cfg(erase_components)'.dependencies]\nleptos_macro = { workspace = true, features = [\"__internal_erase_components\"] }\n\n[package.metadata.cargo-all-features]\ndenylist = [\n  \"tracing\",\n  \"template_macro\",\n  \"rustls\",\n  \"default-tls\",\n  \"wasm-bindgen\",\n  \"rkyv\",                  # was causing clippy issues on nightly\n  \"trace-component-props\",\n  \"spin\",\n  \"islands\",\n  \"bitcode\",\n  \"bitcode-serde\",\n  \"serde-lite\",\n  \"cbor\",\n  \"msgpack\",\n  \"postcard\",\n  \"multipart\",\n]\nskip_feature_sets = [\n  [\"csr\", \"ssr\"],\n  [\"csr\", \"hydrate\"],\n  [\"ssr\", \"hydrate\"],\n  [\"serde\", \"serde-lite\"],\n  [\"serde\", \"miniserde\"],\n  [\"serde\", \"rkyv\"],\n  [\"miniserde\", \"rkyv\"],\n  [\"serde-lite\", \"rkyv\"],\n  [\"default-tls\", \"rustls\"],\n]\nmax_combination_size = 2\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\", \"--cfg\", \"erase_components\"]\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = [\n  'cfg(leptos_debuginfo)',\n  'cfg(rustc_nightly)',\n] }\n"
  },
  {
    "path": "leptos/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n\n[tasks.check]\nclear = true\ndependencies = [\n  \"clippy-each-feature\",\n  \"check-wasm\",\n  \"check-release\",\n  \"check-wasm-release\",\n]\n\n[tasks.check-wasm]\nclear = true\ndependencies = [\"check-hydrate\", \"check-csr\"]\n\n[tasks.check-wasm-release]\nclear = true\ndependencies = [\"check-hydrate-release\", \"check-csr-release\"]\n\n[tasks.check-hydrate]\ncommand = \"cargo\"\nargs = [\n  \"check\",\n  \"--no-default-features\",\n  \"--features=hydrate\",\n  \"--target=wasm32-unknown-unknown\",\n]\n\n[tasks.check-csr]\ncommand = \"cargo\"\nargs = [\n  \"check\",\n  \"--no-default-features\",\n  \"--features=csr\",\n  \"--target=wasm32-unknown-unknown\",\n]\n\n[tasks.check-hydrate-release]\ncommand = \"cargo\"\nargs = [\n  \"check\",\n  \"--release\",\n  \"--no-default-features\",\n  \"--features=hydrate\",\n  \"--target=wasm32-unknown-unknown\",\n]\n\n[tasks.check-csr-release]\ncommand = \"cargo\"\nargs = [\n  \"check\",\n  \"--release\",\n  \"--no-default-features\",\n  \"--features=csr\",\n  \"--target=wasm32-unknown-unknown\",\n]\n\n[tasks.check-release]\ncommand = \"cargo\"\nargs = [\"check\", \"--release\"]\n"
  },
  {
    "path": "leptos/build.rs",
    "content": "use rustc_version::{version_meta, Channel};\n\nfn main() {\n    let target = std::env::var(\"TARGET\").unwrap_or_default();\n\n    // Set cfg flags depending on release channel\n    if matches!(version_meta().unwrap().channel, Channel::Nightly) {\n        println!(\"cargo:rustc-cfg=rustc_nightly\");\n    }\n    // Set cfg flag for getrandom wasm_js\n    if target == \"wasm32-unknown-unknown\" {\n        // Set a custom cfg flag for wasm builds\n        println!(\"cargo:rustc-cfg=getrandom_backend=\\\"wasm_js\\\"\");\n    }\n}\n"
  },
  {
    "path": "leptos/src/animated_show.rs",
    "content": "use crate::{children::ChildrenFn, component, control_flow::Show, IntoView};\nuse core::time::Duration;\nuse leptos_dom::helpers::TimeoutHandle;\nuse leptos_macro::view;\nuse reactive_graph::{\n    effect::RenderEffect,\n    owner::{on_cleanup, StoredValue},\n    signal::RwSignal,\n    traits::{Get, GetUntracked, GetValue, Set, SetValue},\n    wrappers::read::Signal,\n};\nuse tachys::prelude::*;\n\n/// A component that will show its children when the `when` condition is `true`.\n/// Additionally, you need to specify a `hide_delay`. If the `when` condition changes to `false`,\n/// the unmounting of the children will be delayed by the specified Duration.\n/// If you provide the optional `show_class` and `hide_class`, you can create very easy mount /\n/// unmount animations.\n///\n/// ```rust\n/// # use core::time::Duration;\n/// # use leptos::prelude::*;\n/// # #[component]\n/// # pub fn App() -> impl IntoView {\n/// let show = RwSignal::new(false);\n///\n/// view! {\n///     <div\n///         class=\"hover-me\"\n///         on:mouseenter=move |_| show.set(true)\n///         on:mouseleave=move |_| show.set(false)\n///     >\n///         \"Hover Me\"\n///     </div>\n///\n///     <AnimatedShow\n///        when=show\n///        show_class=\"fade-in-1000\"\n///        hide_class=\"fade-out-1000\"\n///        hide_delay=Duration::from_millis(1000)\n///     >\n///        <div class=\"here-i-am\">\n///            \"Here I Am!\"\n///        </div>\n///     </AnimatedShow>\n/// }\n/// # }\n/// ```\n#[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\", skip_all))]\n#[component]\npub fn AnimatedShow(\n    /// The components Show wraps\n    children: ChildrenFn,\n    /// If the component should show or not\n    #[prop(into)]\n    when: Signal<bool>,\n    /// Optional CSS class to apply if `when == true`\n    #[prop(optional)]\n    show_class: &'static str,\n    /// Optional CSS class to apply if `when == false`\n    #[prop(optional)]\n    hide_class: &'static str,\n    /// The timeout after which the component will be unmounted if `when == false`\n    hide_delay: Duration,\n) -> impl IntoView {\n    let handle: StoredValue<Option<TimeoutHandle>> = StoredValue::new(None);\n    let cls = RwSignal::new(if when.get_untracked() {\n        show_class\n    } else {\n        hide_class\n    });\n    let show = RwSignal::new(when.get_untracked());\n\n    let eff = RenderEffect::new(move |_| {\n        if when.get() {\n            // clear any possibly active timer\n            if let Some(h) = handle.get_value() {\n                h.clear();\n            }\n\n            cls.set(show_class);\n            show.set(true);\n        } else {\n            cls.set(hide_class);\n\n            let h = leptos_dom::helpers::set_timeout_with_handle(\n                move || show.set(false),\n                hide_delay,\n            )\n            .expect(\"set timeout in AnimatedShow\");\n            handle.set_value(Some(h));\n        }\n    });\n\n    on_cleanup(move || {\n        if let Some(Some(h)) = handle.try_get_value() {\n            h.clear();\n        }\n        drop(eff);\n    });\n\n    view! {\n        <Show when=move || show.get() fallback=|| ()>\n            <div class=move || cls.get()>{children()}</div>\n        </Show>\n    }\n}\n"
  },
  {
    "path": "leptos/src/attribute_interceptor.rs",
    "content": "use crate::attr::{\n    any_attribute::{AnyAttribute, IntoAnyAttribute},\n    Attribute, NextAttribute,\n};\nuse leptos::prelude::*;\n\n/// Function stored to build/rebuild the wrapped children when attributes are added.\ntype ChildBuilder<T> = dyn Fn(AnyAttribute) -> T + Send + Sync + 'static;\n\n/// Intercepts attributes passed to your component, allowing passing them to any element.\n///\n/// By default, Leptos passes any attributes passed to your component (e.g. `<MyComponent\n/// attr:class=\"some-class\"/>`) to the top-level element in the view returned by your component.\n/// [`AttributeInterceptor`] allows you to intercept this behavior and pass it onto any element in\n/// your component instead.\n///\n/// Must be the top level element in your component's view.\n///\n/// ## Example\n///\n/// Any attributes passed to MyComponent will be passed to the #inner element.\n///\n/// ```\n/// # use leptos::prelude::*;\n/// use leptos::attribute_interceptor::AttributeInterceptor;\n///\n/// #[component]\n/// pub fn MyComponent() -> impl IntoView {\n///     view! {\n///         <AttributeInterceptor let:attrs>\n///             <div id=\"wrapper\">\n///                 <div id=\"inner\" {..attrs} />\n///             </div>\n///         </AttributeInterceptor>\n///     }\n/// }\n/// ```\n#[component(transparent)]\npub fn AttributeInterceptor<Chil, T>(\n    /// The elements that will be rendered, with the attributes this component received as a\n    /// parameter.\n    children: Chil,\n) -> impl IntoView\nwhere\n    Chil: Fn(AnyAttribute) -> T + Send + Sync + 'static,\n    T: IntoView + 'static,\n{\n    AttributeInterceptorInner::new(children)\n}\n\n/// Wrapper to intercept attributes passed to a component so you can apply them to a different\n/// element.\nstruct AttributeInterceptorInner<T: IntoView, A> {\n    children_builder: Box<ChildBuilder<T>>,\n    children: T,\n    attributes: A,\n}\n\nimpl<T: IntoView> AttributeInterceptorInner<T, ()> {\n    /// Use this as the returned view from your component to collect the attributes that are passed\n    /// to your component so you can manually handle them.\n    pub fn new<F>(children: F) -> Self\n    where\n        F: Fn(AnyAttribute) -> T + Send + Sync + 'static,\n    {\n        let children_builder = Box::new(children);\n        let children = children_builder(().into_any_attr());\n\n        Self {\n            children_builder,\n            children,\n            attributes: (),\n        }\n    }\n}\n\nimpl<T: IntoView, A: Attribute> Render for AttributeInterceptorInner<T, A> {\n    type State = <T as Render>::State;\n\n    fn build(self) -> Self::State {\n        self.children.build()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.children.rebuild(state);\n    }\n}\n\nimpl<T: IntoView + 'static, A> AddAnyAttr for AttributeInterceptorInner<T, A>\nwhere\n    A: Attribute,\n{\n    type Output<SomeNewAttr: leptos::attr::Attribute> =\n        AttributeInterceptorInner<T, <<A as NextAttribute>::Output<SomeNewAttr> as Attribute>::CloneableOwned>;\n\n    fn add_any_attr<NewAttr: leptos::attr::Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let attributes =\n            self.attributes.add_any_attr(attr).into_cloneable_owned();\n\n        let children =\n            (self.children_builder)(attributes.clone().into_any_attr());\n\n        AttributeInterceptorInner {\n            children_builder: self.children_builder,\n            children,\n            attributes,\n        }\n    }\n}\n\nimpl<T: IntoView + 'static, A: Attribute> RenderHtml\n    for AttributeInterceptorInner<T, A>\n{\n    type AsyncOutput = T::AsyncOutput;\n    type Owned = AttributeInterceptorInner<T, A::CloneableOwned>;\n\n    const MIN_LENGTH: usize = T::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {\n        self.children.dry_resolve()\n    }\n\n    fn resolve(\n        self,\n    ) -> impl std::future::Future<Output = Self::AsyncOutput> + Send {\n        self.children.resolve()\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut leptos::tachys::view::Position,\n        escape: bool,\n        mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        self.children.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            vec![],\n        )\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &leptos::tachys::hydration::Cursor,\n        position: &leptos::tachys::view::PositionState,\n    ) -> Self::State {\n        self.children.hydrate::<FROM_SERVER>(cursor, position)\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &leptos::tachys::hydration::Cursor,\n        position: &leptos::tachys::view::PositionState,\n    ) -> Self::State {\n        self.children.hydrate_async(cursor, position).await\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        AttributeInterceptorInner {\n            children_builder: self.children_builder,\n            children: self.children,\n            attributes: self.attributes.into_cloneable_owned(),\n        }\n    }\n}\n"
  },
  {
    "path": "leptos/src/await_.rs",
    "content": "use crate::{prelude::Suspend, suspense_component::Suspense, IntoView};\nuse leptos_macro::{component, view};\nuse leptos_server::ArcOnceResource;\nuse reactive_graph::prelude::ReadUntracked;\nuse serde::{de::DeserializeOwned, Serialize};\n\n#[component]\n/// Allows you to inline the data loading for an `async` block or\n/// server function directly into your view. This is the equivalent of combining a\n/// [`create_resource`] that only loads once (i.e., with a source signal `|| ()`) with\n/// a [`Suspense`] with no `fallback`.\n///\n/// Adding `let:{variable name}` to the props makes the data available in the children\n/// that variable name, when resolved.\n/// ```\n/// # use leptos::prelude::*;\n/// # if false {\n/// async fn fetch_monkeys(monkey: i32) -> i32 {\n///     // do some expensive work\n///     3\n/// }\n///\n/// view! {\n///     <Await\n///         future=fetch_monkeys(3)\n///         let:data\n///     >\n///         <p>{*data} \" little monkeys, jumping on the bed.\"</p>\n///     </Await>\n/// }\n/// # ;\n/// # }\n/// ```\npub fn Await<T, Fut, Chil, V>(\n    /// A [`Future`](std::future::Future) that will the component will `.await`\n    /// before rendering.\n    future: Fut,\n    /// If `true`, the component will create a blocking resource, preventing\n    /// the HTML stream from returning anything before `future` has resolved.\n    #[prop(optional)]\n    blocking: bool,\n    /// A function that takes a reference to the resolved data from the `future`\n    /// renders a view.\n    ///\n    /// ## Syntax\n    /// This can be passed in the `view` children of the `<Await/>` by using the\n    /// `let:` syntax to specify the name for the data variable.\n    ///\n    /// ```rust\n    /// # use leptos::prelude::*;\n    /// # if false {\n    /// # async fn fetch_monkeys(monkey: i32) -> i32 {\n    /// #    3\n    /// # }\n    /// view! {\n    ///     <Await\n    ///         future=fetch_monkeys(3)\n    ///         let:data\n    ///     >\n    ///         <p>{*data} \" little monkeys, jumping on the bed.\"</p>\n    ///     </Await>\n    /// }\n    /// # ;\n    /// # }\n    /// ```\n    /// is the same as\n    ///  ```rust\n    /// # use leptos::prelude::*;\n    /// # if false {\n    /// # async fn fetch_monkeys(monkey: i32) -> i32 {\n    /// #    3\n    /// # }\n    /// view! {\n    ///     <Await\n    ///         future=fetch_monkeys(3)\n    ///         children=|data| view! {\n    ///           <p>{*data} \" little monkeys, jumping on the bed.\"</p>\n    ///         }\n    ///     />\n    /// }\n    /// # ;\n    /// # }\n    /// ```\n    children: Chil,\n) -> impl IntoView\nwhere\n    T: Send + Sync + Serialize + DeserializeOwned + 'static,\n    Fut: std::future::Future<Output = T> + Send + 'static,\n    Chil: FnOnce(&T) -> V + Send + 'static,\n    V: IntoView + 'static,\n{\n    let res = ArcOnceResource::<T>::new_with_options(future, blocking);\n    let ready = res.ready();\n\n    view! {\n        <Suspense fallback=|| ()>\n            {Suspend::new(async move {\n                ready.await;\n                children(res.read_untracked().as_ref().unwrap())\n            })}\n\n        </Suspense>\n    }\n}\n"
  },
  {
    "path": "leptos/src/children.rs",
    "content": "use crate::into_view::{IntoView, View};\nuse std::{\n    fmt::{self, Debug},\n    sync::Arc,\n};\nuse tachys::view::{\n    any_view::{AnyView, IntoAny},\n    fragment::{Fragment, IntoFragment},\n    RenderHtml,\n};\n\n/// The most common type for the `children` property on components,\n/// which can only be called once.\n///\n/// This does not support iterating over individual nodes within the children.\n/// To iterate over children, use [`ChildrenFragment`].\npub type Children = Box<dyn FnOnce() -> AnyView + Send>;\n\n/// A type for the `children` property on components that can be called only once,\n/// and provides a collection of all the children passed to this component.\npub type ChildrenFragment = Box<dyn FnOnce() -> Fragment + Send>;\n\n/// A type for the `children` property on components that can be called\n/// more than once.\npub type ChildrenFn = Arc<dyn Fn() -> AnyView + Send + Sync>;\n\n/// A type for the `children` property on components that can be called more than once,\n/// and provides a collection of all the children passed to this component.\npub type ChildrenFragmentFn = Arc<dyn Fn() -> Fragment + Send>;\n\n/// A type for the `children` property on components that can be called\n/// more than once, but may mutate the children.\npub type ChildrenFnMut = Box<dyn FnMut() -> AnyView + Send>;\n\n/// A type for the `children` property on components that can be called more than once,\n/// but may mutate the children, and provides a collection of all the children\n/// passed to this component.\npub type ChildrenFragmentMut = Box<dyn FnMut() -> Fragment + Send>;\n\n// This is to still support components that accept `Box<dyn Fn() -> AnyView>` as a children.\ntype BoxedChildrenFn = Box<dyn Fn() -> AnyView + Send>;\n\n/// This trait can be used when constructing a component that takes children without needing\n/// to know exactly what children type the component expects. This is used internally by the\n/// `view!` macro implementation, and can also be used explicitly when using the builder syntax.\n///\n///\n/// Different component types take different types for their `children` prop, some of which cannot\n/// be directly constructed. Using `ToChildren` allows the component user to pass children without\n/// explicitly constructing the correct type.\n///\n/// ## Examples\n///\n/// ```\n/// # use leptos::prelude::*;\n/// # use leptos::html::p;\n/// # use leptos::IntoView;\n/// # use leptos_macro::component;\n/// # use leptos::children::ToChildren;\n/// use leptos::context::{Provider, ProviderProps};\n/// use leptos::control_flow::{Show, ShowProps};\n///\n/// #[component]\n/// fn App() -> impl IntoView {\n///     (\n///       Provider(\n///         ProviderProps::builder()\n///             .children(ToChildren::to_children(|| {\n///                 p().child(\"Foo\")\n///             }))\n///             // ...\n///            .value(\"Foo\")\n///            .build(),\n///        ),\n///        Show(\n///          ShowProps::builder()\n///             .children(ToChildren::to_children(|| {\n///                 p().child(\"Foo\")\n///             }))\n///             // ...\n///             .when(|| true)\n///             .fallback(|| p().child(\"foo\"))\n///             .build(),\n///        )\n///     )\n/// }\npub trait ToChildren<F> {\n    /// Convert the provided type (generally a closure) to Self (generally a \"children\" type,\n    /// e.g., [Children]). See the implementations to see exactly which input types are supported\n    /// and which \"children\" type they are converted to.\n    fn to_children(f: F) -> Self;\n}\n\n/// Compiler optimisation, can be used with certain type to avoid unique closures in the view!{} macro.\npub struct ChildrenOptContainer<T>(pub T);\n\nimpl<F, C> ToChildren<F> for Children\nwhere\n    F: FnOnce() -> C + Send + 'static,\n    C: RenderHtml + Send + 'static,\n{\n    #[inline]\n    fn to_children(f: F) -> Self {\n        Box::new(move || f().into_any())\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for Children\nwhere\n    T: IntoAny + Send + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        Box::new(move || t.0.into_any())\n    }\n}\n\nimpl<F, C> ToChildren<F> for ChildrenFn\nwhere\n    F: Fn() -> C + Send + Sync + 'static,\n    C: RenderHtml + Send + 'static,\n{\n    #[inline]\n    fn to_children(f: F) -> Self {\n        Arc::new(move || f().into_any())\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for ChildrenFn\nwhere\n    T: IntoAny + Clone + Send + Sync + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        Arc::new(move || t.0.clone().into_any())\n    }\n}\n\nimpl<F, C> ToChildren<F> for ChildrenFnMut\nwhere\n    F: Fn() -> C + Send + 'static,\n    C: RenderHtml + Send + 'static,\n{\n    #[inline]\n    fn to_children(f: F) -> Self {\n        Box::new(move || f().into_any())\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for ChildrenFnMut\nwhere\n    T: IntoAny + Clone + Send + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        Box::new(move || t.0.clone().into_any())\n    }\n}\n\nimpl<F, C> ToChildren<F> for BoxedChildrenFn\nwhere\n    F: Fn() -> C + Send + 'static,\n    C: RenderHtml + Send + 'static,\n{\n    #[inline]\n    fn to_children(f: F) -> Self {\n        Box::new(move || f().into_any())\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for BoxedChildrenFn\nwhere\n    T: IntoAny + Clone + Send + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        Box::new(move || t.0.clone().into_any())\n    }\n}\n\nimpl<F, C> ToChildren<F> for ChildrenFragment\nwhere\n    F: FnOnce() -> C + Send + 'static,\n    C: IntoFragment,\n{\n    #[inline]\n    fn to_children(f: F) -> Self {\n        Box::new(move || f().into_fragment())\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for ChildrenFragment\nwhere\n    T: IntoAny + Send + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        Box::new(move || Fragment::new(vec![t.0.into_any()]))\n    }\n}\n\nimpl<F, C> ToChildren<F> for ChildrenFragmentFn\nwhere\n    F: Fn() -> C + Send + 'static,\n    C: IntoFragment,\n{\n    #[inline]\n    fn to_children(f: F) -> Self {\n        Arc::new(move || f().into_fragment())\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for ChildrenFragmentFn\nwhere\n    T: IntoAny + Clone + Send + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        Arc::new(move || Fragment::new(vec![t.0.clone().into_any()]))\n    }\n}\n\nimpl<F, C> ToChildren<F> for ChildrenFragmentMut\nwhere\n    F: FnMut() -> C + Send + 'static,\n    C: IntoFragment,\n{\n    #[inline]\n    fn to_children(mut f: F) -> Self {\n        Box::new(move || f().into_fragment())\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for ChildrenFragmentMut\nwhere\n    T: IntoAny + Clone + Send + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        Box::new(move || Fragment::new(vec![t.0.clone().into_any()]))\n    }\n}\n\n/// New-type wrapper for a function that returns a view with `From` and `Default` traits implemented\n/// to enable optional props in for example `<Show>` and `<Suspense>`.\n#[derive(Clone)]\npub struct ViewFn(Arc<dyn Fn() -> AnyView + Send + Sync + 'static>);\n\nimpl Default for ViewFn {\n    fn default() -> Self {\n        Self(Arc::new(|| ().into_any()))\n    }\n}\n\nimpl<F, C> From<F> for ViewFn\nwhere\n    F: Fn() -> C + Send + Sync + 'static,\n    C: RenderHtml + Send + 'static,\n{\n    fn from(value: F) -> Self {\n        Self(Arc::new(move || value().into_any()))\n    }\n}\n\nimpl<C> From<View<C>> for ViewFn\nwhere\n    C: Clone + Send + Sync + 'static,\n    View<C>: IntoAny,\n{\n    fn from(value: View<C>) -> Self {\n        Self(Arc::new(move || value.clone().into_any()))\n    }\n}\n\nimpl ViewFn {\n    /// Execute the wrapped function\n    pub fn run(&self) -> AnyView {\n        (self.0)()\n    }\n}\n\n/// New-type wrapper for a function, which will only be called once and returns a view with `From` and\n/// `Default` traits implemented to enable optional props in for example `<Show>` and `<Suspense>`.\npub struct ViewFnOnce(Box<dyn FnOnce() -> AnyView + Send + 'static>);\n\nimpl Default for ViewFnOnce {\n    fn default() -> Self {\n        Self(Box::new(|| ().into_any()))\n    }\n}\n\nimpl<F, C> From<F> for ViewFnOnce\nwhere\n    F: FnOnce() -> C + Send + 'static,\n    C: RenderHtml + Send + 'static,\n{\n    fn from(value: F) -> Self {\n        Self(Box::new(move || value().into_any()))\n    }\n}\n\nimpl<C> From<View<C>> for ViewFnOnce\nwhere\n    C: Send + Sync + 'static,\n    View<C>: IntoAny,\n{\n    fn from(value: View<C>) -> Self {\n        Self(Box::new(move || value.into_any()))\n    }\n}\n\nimpl ViewFnOnce {\n    /// Execute the wrapped function\n    pub fn run(self) -> AnyView {\n        (self.0)()\n    }\n}\n\n/// A typed equivalent to [`Children`], which takes a generic but preserves type information to\n/// allow the compiler to optimize the view more effectively.\npub struct TypedChildren<T>(Box<dyn FnOnce() -> View<T> + Send>);\n\nimpl<T> TypedChildren<T> {\n    /// Extracts the inner `children` function.\n    pub fn into_inner(self) -> impl FnOnce() -> View<T> + Send {\n        self.0\n    }\n}\n\nimpl<F, C> ToChildren<F> for TypedChildren<C>\nwhere\n    F: FnOnce() -> C + Send + 'static,\n    C: IntoView,\n    C::AsyncOutput: Send,\n{\n    #[inline]\n    fn to_children(f: F) -> Self {\n        TypedChildren(Box::new(move || f().into_view()))\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for TypedChildren<T>\nwhere\n    T: IntoView + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        TypedChildren(Box::new(move || t.0.into_view()))\n    }\n}\n\n/// A typed equivalent to [`ChildrenFnMut`], which takes a generic but preserves type information to\n/// allow the compiler to optimize the view more effectively.\npub struct TypedChildrenMut<T>(Box<dyn FnMut() -> View<T> + Send>);\n\nimpl<T> Debug for TypedChildrenMut<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_tuple(\"TypedChildrenMut\").finish()\n    }\n}\n\nimpl<T> TypedChildrenMut<T> {\n    /// Extracts the inner `children` function.\n    pub fn into_inner(self) -> impl FnMut() -> View<T> + Send {\n        self.0\n    }\n}\n\nimpl<F, C> ToChildren<F> for TypedChildrenMut<C>\nwhere\n    F: FnMut() -> C + Send + 'static,\n    C: IntoView,\n    C::AsyncOutput: Send,\n{\n    #[inline]\n    fn to_children(mut f: F) -> Self {\n        TypedChildrenMut(Box::new(move || f().into_view()))\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for TypedChildrenMut<T>\nwhere\n    T: IntoView + Clone + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        TypedChildrenMut(Box::new(move || t.0.clone().into_view()))\n    }\n}\n\n/// A typed equivalent to [`ChildrenFn`], which takes a generic but preserves type information to\n/// allow the compiler to optimize the view more effectively.\npub struct TypedChildrenFn<T>(Arc<dyn Fn() -> View<T> + Send + Sync>);\n\nimpl<T> Debug for TypedChildrenFn<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_tuple(\"TypedChildrenFn\").finish()\n    }\n}\n\nimpl<T> Clone for TypedChildrenFn<T> {\n    // Manual implementation to avoid the `T: Clone` bound.\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\nimpl<T> TypedChildrenFn<T> {\n    /// Extracts the inner `children` function.\n    pub fn into_inner(self) -> Arc<dyn Fn() -> View<T> + Send + Sync> {\n        self.0\n    }\n}\n\nimpl<F, C> ToChildren<F> for TypedChildrenFn<C>\nwhere\n    F: Fn() -> C + Send + Sync + 'static,\n    C: IntoView,\n    C::AsyncOutput: Send,\n{\n    #[inline]\n    fn to_children(f: F) -> Self {\n        TypedChildrenFn(Arc::new(move || f().into_view()))\n    }\n}\n\nimpl<T> ToChildren<ChildrenOptContainer<T>> for TypedChildrenFn<T>\nwhere\n    T: IntoView + Clone + Sync + 'static,\n{\n    #[inline]\n    fn to_children(t: ChildrenOptContainer<T>) -> Self {\n        TypedChildrenFn(Arc::new(move || t.0.clone().into_view()))\n    }\n}\n"
  },
  {
    "path": "leptos/src/component.rs",
    "content": "//! Utility traits and functions that allow building components,\n//! as either functions of their props or functions with no arguments,\n//! without knowing the name of the props struct.\n\npub trait Component<P> {}\n\npub trait Props {\n    type Builder;\n\n    fn builder() -> Self::Builder;\n}\n\n#[doc(hidden)]\npub trait PropsOrNoPropsBuilder {\n    type Builder;\n\n    fn builder_or_not() -> Self::Builder;\n}\n\n#[doc(hidden)]\n#[derive(Copy, Clone, Debug, Default)]\npub struct EmptyPropsBuilder {}\n\nimpl EmptyPropsBuilder {\n    pub fn build(self) {}\n}\n\nimpl<P: Props> PropsOrNoPropsBuilder for P {\n    type Builder = <P as Props>::Builder;\n\n    fn builder_or_not() -> Self::Builder {\n        Self::builder()\n    }\n}\n\nimpl PropsOrNoPropsBuilder for EmptyPropsBuilder {\n    type Builder = EmptyPropsBuilder;\n\n    fn builder_or_not() -> Self::Builder {\n        EmptyPropsBuilder {}\n    }\n}\n\nimpl<F, R> Component<EmptyPropsBuilder> for F where F: FnOnce() -> R {}\n\nimpl<P, F, R> Component<P> for F\nwhere\n    F: FnOnce(P) -> R,\n    P: Props,\n{\n}\n\npub fn component_props_builder<P: PropsOrNoPropsBuilder>(\n    _f: &impl Component<P>,\n) -> <P as PropsOrNoPropsBuilder>::Builder {\n    <P as PropsOrNoPropsBuilder>::builder_or_not()\n}\n\npub fn component_view<P, T>(f: impl ComponentConstructor<P, T>, props: P) -> T {\n    f.construct(props)\n}\npub trait ComponentConstructor<P, T> {\n    fn construct(self, props: P) -> T;\n}\n\nimpl<Func, T> ComponentConstructor<(), T> for Func\nwhere\n    Func: FnOnce() -> T,\n{\n    fn construct(self, (): ()) -> T {\n        (self)()\n    }\n}\n\nimpl<Func, T, P> ComponentConstructor<P, T> for Func\nwhere\n    Func: FnOnce(P) -> T,\n    P: PropsOrNoPropsBuilder,\n{\n    fn construct(self, props: P) -> T {\n        (self)(props)\n    }\n}\n"
  },
  {
    "path": "leptos/src/error_boundary.rs",
    "content": "use crate::{children::TypedChildren, IntoView};\nuse futures::{channel::oneshot, future::join_all};\nuse hydration_context::{SerializedDataId, SharedContext};\nuse leptos_macro::component;\nuse or_poisoned::OrPoisoned;\nuse reactive_graph::{\n    computed::ArcMemo,\n    effect::RenderEffect,\n    owner::{provide_context, ArcStoredValue, Owner},\n    signal::ArcRwSignal,\n    traits::{Get, Update, With, WithUntracked, WriteValue},\n};\nuse rustc_hash::FxHashMap;\nuse std::{\n    collections::VecDeque,\n    fmt::Debug,\n    mem,\n    sync::{Arc, Mutex},\n};\nuse tachys::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    reactive_graph::OwnedView,\n    ssr::{StreamBuilder, StreamChunk},\n    view::{\n        add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n        RenderHtml,\n    },\n};\nuse throw_error::{Error, ErrorHook, ErrorId};\n\n/// When you render a `Result<_, _>` in your view, in the `Err` case it will\n/// render nothing, and search up through the view tree for an `<ErrorBoundary/>`.\n/// This component lets you define a fallback that should be rendered in that\n/// error case, allowing you to handle errors within a section of the interface.\n///\n/// ```\n/// # use leptos::prelude::*;\n/// #[component]\n/// pub fn ErrorBoundaryExample() -> impl IntoView {\n///   let (value, set_value) = signal(Ok(0));\n///   let on_input =\n///     move |ev| set_value.set(event_target_value(&ev).parse::<i32>());\n///\n///   view! {\n///     <input type=\"text\" on:input=on_input/>\n///     <ErrorBoundary\n///       fallback=move |_| view! { <p class=\"error\">\"Enter a valid number.\"</p>}\n///     >\n///       <p>\"Value is: \" {move || value.get()}</p>\n///     </ErrorBoundary>\n///   }\n/// }\n/// ```\n///\n/// ## Beginner's Tip: ErrorBoundary Requires Your Error To Implement std::error::Error.\n/// `ErrorBoundary` requires your `Result<T,E>` to implement [IntoView](https://docs.rs/leptos/latest/leptos/trait.IntoView.html).\n/// `Result<T,E>` only implements `IntoView` if `E` implements [std::error::Error](https://doc.rust-lang.org/std/error/trait.Error.html).\n/// So, for instance, if you pass a `Result<T,String>` where `T` implements [IntoView](https://docs.rs/leptos/latest/leptos/trait.IntoView.html)\n/// and attempt to render the error for the purposes of `ErrorBoundary` you'll get a compiler error like this.\n///\n/// ```rust,ignore\n/// error[E0599]: the method `into_view` exists for enum `Result<ViewableLoginFlow, String>`, but its trait bounds were not satisfied\n///    --> src/login.rs:229:32\n///     |\n/// 229 |                     err => err.into_view(),\n///     |                                ^^^^^^^^^ method cannot be called on `Result<ViewableLoginFlow, String>` due to unsatisfied trait bounds\n///     |\n///     = note: the following trait bounds were not satisfied:\n///             `<&Result<ViewableLoginFlow, std::string::String> as FnOnce<()>>::Output = _`\n///             which is required by `&Result<ViewableLoginFlow, std::string::String>: leptos::IntoView`\n///    ... more notes here ...\n/// ```\n///\n/// For more information about how to easily implement `Error` see\n/// [thiserror](https://docs.rs/thiserror/latest/thiserror/)\n#[component]\npub fn ErrorBoundary<FalFn, Fal, Chil>(\n    /// The elements that will be rendered, which may include one or more `Result<_>` types.\n    children: TypedChildren<Chil>,\n    /// A fallback that will be shown if an error occurs.\n    fallback: FalFn,\n) -> impl IntoView\nwhere\n    FalFn: FnMut(ArcRwSignal<Errors>) -> Fal + Send + 'static,\n    Fal: IntoView + Send + 'static,\n    Chil: IntoView + Send + 'static,\n{\n    let sc = Owner::current_shared_context();\n    let boundary_id = sc.as_ref().map(|sc| sc.next_id()).unwrap_or_default();\n    let initial_errors =\n        sc.map(|sc| sc.errors(&boundary_id)).unwrap_or_default();\n\n    let hook = Arc::new(ErrorBoundaryErrorHook::new(\n        boundary_id.clone(),\n        initial_errors,\n    ));\n    let errors = hook.errors.clone();\n    let errors_empty = ArcMemo::new({\n        let errors = errors.clone();\n        move |_| errors.with(|map| map.is_empty())\n    });\n    let hook = hook as Arc<dyn ErrorHook>;\n\n    let _guard = throw_error::set_error_hook(Arc::clone(&hook));\n    let suspended_children = ErrorBoundarySuspendedChildren::default();\n\n    let owner = Owner::new();\n    let children = owner.with(|| {\n        provide_context(Arc::clone(&hook));\n        provide_context(suspended_children.clone());\n        children.into_inner()()\n    });\n\n    OwnedView::new_with_owner(\n        ErrorBoundaryView {\n            hook,\n            boundary_id,\n            errors_empty,\n            children,\n            errors,\n            fallback,\n            suspended_children,\n        },\n        owner,\n    )\n}\n\npub(crate) type ErrorBoundarySuspendedChildren =\n    ArcStoredValue<Vec<oneshot::Receiver<()>>>;\n\nstruct ErrorBoundaryView<Chil, FalFn> {\n    hook: Arc<dyn ErrorHook>,\n    boundary_id: SerializedDataId,\n    errors_empty: ArcMemo<bool>,\n    children: Chil,\n    fallback: FalFn,\n    errors: ArcRwSignal<Errors>,\n    suspended_children: ErrorBoundarySuspendedChildren,\n}\n\nstruct ErrorBoundaryViewState<Chil, Fal> {\n    // the children are always present; we toggle between them and the fallback as needed\n    children: Chil,\n    fallback: Option<Fal>,\n}\n\nimpl<Chil, Fal> Mountable for ErrorBoundaryViewState<Chil, Fal>\nwhere\n    Chil: Mountable,\n    Fal: Mountable,\n{\n    fn unmount(&mut self) {\n        if let Some(fallback) = &mut self.fallback {\n            fallback.unmount();\n        } else {\n            self.children.unmount();\n        }\n    }\n\n    fn mount(\n        &mut self,\n        parent: &tachys::renderer::types::Element,\n        marker: Option<&tachys::renderer::types::Node>,\n    ) {\n        if let Some(fallback) = &mut self.fallback {\n            fallback.mount(parent, marker);\n        } else {\n            self.children.mount(parent, marker);\n        }\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        if let Some(fallback) = &self.fallback {\n            fallback.insert_before_this(child)\n        } else {\n            self.children.insert_before_this(child)\n        }\n    }\n\n    fn elements(&self) -> Vec<tachys::renderer::types::Element> {\n        if let Some(fallback) = &self.fallback {\n            fallback.elements()\n        } else {\n            self.children.elements()\n        }\n    }\n}\n\nimpl<Chil, FalFn, Fal> Render for ErrorBoundaryView<Chil, FalFn>\nwhere\n    Chil: Render + 'static,\n    FalFn: FnMut(ArcRwSignal<Errors>) -> Fal + Send + 'static,\n    Fal: Render + 'static,\n{\n    type State = RenderEffect<ErrorBoundaryViewState<Chil::State, Fal::State>>;\n\n    fn build(mut self) -> Self::State {\n        let hook = Arc::clone(&self.hook);\n        let _hook = throw_error::set_error_hook(Arc::clone(&hook));\n        let mut children = Some(self.children.build());\n        RenderEffect::new(\n            move |prev: Option<\n                ErrorBoundaryViewState<Chil::State, Fal::State>,\n            >| {\n                let _hook = throw_error::set_error_hook(Arc::clone(&hook));\n                if let Some(mut state) = prev {\n                    match (self.errors_empty.get(), &mut state.fallback) {\n                        // no errors, and was showing fallback\n                        (true, Some(fallback)) => {\n                            fallback.insert_before_this(&mut state.children);\n                            fallback.unmount();\n                            state.fallback = None;\n                        }\n                        // yes errors, and was showing children\n                        (false, None) => {\n                            state.fallback = Some(\n                                (self.fallback)(self.errors.clone()).build(),\n                            );\n                            state\n                                .children\n                                .insert_before_this(&mut state.fallback);\n                            state.children.unmount();\n                        }\n                        // either there were no errors, and we were already showing the children\n                        // or there are errors, but we were already showing the fallback\n                        // in either case, rebuilding doesn't require us to do anything\n                        _ => {}\n                    }\n                    state\n                } else {\n                    let fallback = (!self.errors_empty.get())\n                        .then(|| (self.fallback)(self.errors.clone()).build());\n                    ErrorBoundaryViewState {\n                        children: children.take().unwrap(),\n                        fallback,\n                    }\n                }\n            },\n        )\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let new = self.build();\n        let mut old = std::mem::replace(state, new);\n        old.insert_before_this(state);\n        old.unmount();\n    }\n}\n\nimpl<Chil, FalFn, Fal> AddAnyAttr for ErrorBoundaryView<Chil, FalFn>\nwhere\n    Chil: RenderHtml + 'static,\n    FalFn: FnMut(ArcRwSignal<Errors>) -> Fal + Send + 'static,\n    Fal: RenderHtml + Send + 'static,\n{\n    type Output<SomeNewAttr: Attribute> =\n        ErrorBoundaryView<Chil::Output<SomeNewAttr::CloneableOwned>, FalFn>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let ErrorBoundaryView {\n            hook,\n            boundary_id,\n            errors_empty,\n            children,\n            fallback,\n            errors,\n            suspended_children,\n        } = self;\n        ErrorBoundaryView {\n            hook,\n            boundary_id,\n            errors_empty,\n            children: children.add_any_attr(attr.into_cloneable_owned()),\n            fallback,\n            errors,\n            suspended_children,\n        }\n    }\n}\n\nimpl<Chil, FalFn, Fal> RenderHtml for ErrorBoundaryView<Chil, FalFn>\nwhere\n    Chil: RenderHtml + Send + 'static,\n    FalFn: FnMut(ArcRwSignal<Errors>) -> Fal + Send + 'static,\n    Fal: RenderHtml + Send + 'static,\n{\n    type AsyncOutput = ErrorBoundaryView<Chil::AsyncOutput, FalFn>;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = Chil::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {\n        self.children.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        let ErrorBoundaryView {\n            hook,\n            boundary_id,\n            errors_empty,\n            children,\n            fallback,\n            errors,\n            suspended_children,\n            ..\n        } = self;\n        ErrorBoundaryView {\n            hook,\n            boundary_id,\n            errors_empty,\n            children: children.resolve().await,\n            fallback,\n            errors,\n            suspended_children,\n        }\n    }\n\n    fn to_html_with_buf(\n        mut self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // first, attempt to serialize the children to HTML, then check for errors\n        let _hook = throw_error::set_error_hook(self.hook);\n        let mut new_buf = String::with_capacity(Chil::MIN_LENGTH);\n        let mut new_pos = *position;\n        self.children.to_html_with_buf(\n            &mut new_buf,\n            &mut new_pos,\n            escape,\n            mark_branches,\n            extra_attrs.clone(),\n        );\n\n        // any thrown errors would've been caught here\n        if self.errors.with_untracked(|map| map.is_empty()) {\n            buf.push_str(&new_buf);\n        } else {\n            // otherwise, serialize the fallback instead\n            (self.fallback)(self.errors).to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        mut self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let _hook = throw_error::set_error_hook(Arc::clone(&self.hook));\n\n        // first, attempt to serialize the children to HTML, then check for errors\n        let mut new_buf = StreamBuilder::new(buf.clone_id());\n        let mut new_pos = *position;\n        self.children.to_html_async_with_buf::<OUT_OF_ORDER>(\n            &mut new_buf,\n            &mut new_pos,\n            escape,\n            mark_branches,\n            extra_attrs.clone(),\n        );\n\n        let suspense_children =\n            mem::take(&mut *self.suspended_children.write_value());\n\n        // not waiting for any suspended children: just render\n        if suspense_children.is_empty() {\n            // any thrown errors would've been caught here\n            if self.errors.with_untracked(|map| map.is_empty()) {\n                buf.append(new_buf);\n            } else {\n                // otherwise, serialize the fallback instead\n                let mut fallback = String::with_capacity(Fal::MIN_LENGTH);\n                (self.fallback)(self.errors).to_html_with_buf(\n                    &mut fallback,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n                buf.push_sync(&fallback);\n            }\n        } else {\n            let mut position = *position;\n            // if we're waiting for suspended children, we'll first wait for them to load\n            // in this implementation, an ErrorBoundary that *contains* Suspense essentially acts\n            // like a Suspense: it will wait for (all top-level) child Suspense to load before rendering anything\n            let mut view_buf = StreamBuilder::new(new_buf.clone_id());\n            view_buf.next_id();\n            let hook = Arc::clone(&self.hook);\n            view_buf.push_async(async move {\n                let _hook = throw_error::set_error_hook(Arc::clone(&hook));\n                let _ = join_all(suspense_children).await;\n\n                let mut my_chunks = VecDeque::new();\n                for chunk in new_buf.take_chunks() {\n                    match chunk {\n                        StreamChunk::Sync(data) => {\n                            my_chunks.push_back(StreamChunk::Sync(data))\n                        }\n                        StreamChunk::Async { chunks } => {\n                            let chunks = chunks.await;\n                            my_chunks.extend(chunks);\n                        }\n                        StreamChunk::OutOfOrder { chunks } => {\n                            let chunks = chunks.await;\n                            my_chunks.push_back(StreamChunk::OutOfOrder {\n                                chunks: Box::pin(async move { chunks }),\n                            });\n                        }\n                    }\n                }\n\n                if self.errors.with_untracked(|map| map.is_empty()) {\n                    // if no errors, just go ahead with the stream\n                    my_chunks\n                } else {\n                    // otherwise, serialize the fallback instead\n                    let mut fallback = String::with_capacity(Fal::MIN_LENGTH);\n                    (self.fallback)(self.errors).to_html_with_buf(\n                        &mut fallback,\n                        &mut position,\n                        escape,\n                        mark_branches,\n                        extra_attrs,\n                    );\n                    my_chunks.clear();\n                    my_chunks.push_back(StreamChunk::Sync(fallback));\n                    my_chunks\n                }\n            });\n            buf.append(view_buf);\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        mut self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let mut children = Some(self.children);\n        let hook = Arc::clone(&self.hook);\n        let cursor = cursor.to_owned();\n        let position = position.to_owned();\n        RenderEffect::new(\n            move |prev: Option<\n                ErrorBoundaryViewState<Chil::State, Fal::State>,\n            >| {\n                let _hook = throw_error::set_error_hook(Arc::clone(&hook));\n                if let Some(mut state) = prev {\n                    match (self.errors_empty.get(), &mut state.fallback) {\n                        // no errors, and was showing fallback\n                        (true, Some(fallback)) => {\n                            fallback.insert_before_this(&mut state.children);\n                            state.fallback.unmount();\n                            state.fallback = None;\n                        }\n                        // yes errors, and was showing children\n                        (false, None) => {\n                            state.fallback = Some(\n                                (self.fallback)(self.errors.clone()).build(),\n                            );\n                            state\n                                .children\n                                .insert_before_this(&mut state.fallback);\n                            state.children.unmount();\n                        }\n                        // either there were no errors, and we were already showing the children\n                        // or there are errors, but we were already showing the fallback\n                        // in either case, rebuilding doesn't require us to do anything\n                        _ => {}\n                    }\n                    state\n                } else {\n                    let children = children.take().unwrap();\n                    let (children, fallback) = if self.errors_empty.get() {\n                        (\n                            children.hydrate::<FROM_SERVER>(&cursor, &position),\n                            None,\n                        )\n                    } else {\n                        (\n                            children.build(),\n                            Some(\n                                (self.fallback)(self.errors.clone())\n                                    .hydrate::<FROM_SERVER>(&cursor, &position),\n                            ),\n                        )\n                    };\n\n                    ErrorBoundaryViewState { children, fallback }\n                }\n            },\n        )\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let mut children = Some(self.children);\n        let hook = Arc::clone(&self.hook);\n        let cursor = cursor.to_owned();\n        let position = position.to_owned();\n\n        let fallback_fn = Arc::new(Mutex::new(self.fallback));\n        let initial = {\n            let errors_empty = self.errors_empty.clone();\n            let errors = self.errors.clone();\n            let fallback_fn = Arc::clone(&fallback_fn);\n            async move {\n                let children = children.take().unwrap();\n                let (children, fallback) = if errors_empty.get() {\n                    (children.hydrate_async(&cursor, &position).await, None)\n                } else {\n                    let children = children.build();\n                    let fallback =\n                        (fallback_fn.lock().or_poisoned())(errors.clone());\n                    let fallback =\n                        fallback.hydrate_async(&cursor, &position).await;\n                    (children, Some(fallback))\n                };\n\n                ErrorBoundaryViewState { children, fallback }\n            }\n        };\n\n        RenderEffect::new_with_async_value(\n            move |prev: Option<\n                ErrorBoundaryViewState<Chil::State, Fal::State>,\n            >| {\n                let _hook = throw_error::set_error_hook(Arc::clone(&hook));\n                if let Some(mut state) = prev {\n                    match (self.errors_empty.get(), &mut state.fallback) {\n                        // no errors, and was showing fallback\n                        (true, Some(fallback)) => {\n                            fallback.insert_before_this(&mut state.children);\n                            state.fallback.unmount();\n                            state.fallback = None;\n                        }\n                        // yes errors, and was showing children\n                        (false, None) => {\n                            state.fallback = Some(\n                                (fallback_fn.lock().or_poisoned())(\n                                    self.errors.clone(),\n                                )\n                                .build(),\n                            );\n                            state\n                                .children\n                                .insert_before_this(&mut state.fallback);\n                            state.children.unmount();\n                        }\n                        // either there were no errors, and we were already showing the children\n                        // or there are errors, but we were already showing the fallback\n                        // in either case, rebuilding doesn't require us to do anything\n                        _ => {}\n                    }\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            initial,\n        )\n        .await\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\n#[derive(Debug)]\nstruct ErrorBoundaryErrorHook {\n    errors: ArcRwSignal<Errors>,\n    id: SerializedDataId,\n    shared_context: Option<Arc<dyn SharedContext + Send + Sync>>,\n}\n\nimpl ErrorBoundaryErrorHook {\n    pub fn new(\n        id: SerializedDataId,\n        initial_errors: impl IntoIterator<Item = (ErrorId, Error)>,\n    ) -> Self {\n        Self {\n            errors: ArcRwSignal::new(Errors(\n                initial_errors.into_iter().collect(),\n            )),\n            id,\n            shared_context: Owner::current_shared_context(),\n        }\n    }\n}\n\nimpl ErrorHook for ErrorBoundaryErrorHook {\n    fn throw(&self, error: Error) -> ErrorId {\n        // generate a unique ID\n        let key: ErrorId = Owner::current_shared_context()\n            .map(|sc| sc.next_id())\n            .unwrap_or_default()\n            .into();\n\n        // register it with the shared context, so that it can be serialized from server to client\n        // as needed\n        if let Some(sc) = &self.shared_context {\n            sc.register_error(self.id.clone(), key.clone(), error.clone());\n        }\n\n        // add it to the reactive map of errors\n        self.errors.update(|map| {\n            map.insert(key.clone(), error);\n        });\n\n        // return the key, which will be owned by the Result being rendered and can be used to\n        // unregister this error if it is rebuilt\n        key\n    }\n\n    fn clear(&self, id: &throw_error::ErrorId) {\n        self.errors.update(|map| {\n            map.remove(id);\n        });\n    }\n}\n\n/// A struct to hold all the possible errors that could be provided by child Views\n#[derive(Debug, Clone, Default)]\n#[repr(transparent)]\npub struct Errors(FxHashMap<ErrorId, Error>);\n\nimpl Errors {\n    /// Returns `true` if there are no errors.\n    #[inline(always)]\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// Add an error to Errors that will be processed by `<ErrorBoundary/>`\n    pub fn insert<E>(&mut self, key: ErrorId, error: E)\n    where\n        E: Into<Error>,\n    {\n        self.0.insert(key, error.into());\n    }\n\n    /// Add an error with the default key for errors outside the reactive system\n    pub fn insert_with_default_key<E>(&mut self, error: E)\n    where\n        E: Into<Error>,\n    {\n        self.0.insert(Default::default(), error.into());\n    }\n\n    /// Remove an error to Errors that will be processed by `<ErrorBoundary/>`\n    pub fn remove(&mut self, key: &ErrorId) -> Option<Error> {\n        self.0.remove(key)\n    }\n\n    /// An iterator over all the errors, in arbitrary order.\n    #[inline(always)]\n    pub fn iter(&self) -> Iter<'_> {\n        Iter(self.0.iter())\n    }\n}\n\nimpl IntoIterator for Errors {\n    type Item = (ErrorId, Error);\n    type IntoIter = IntoIter;\n\n    #[inline(always)]\n    fn into_iter(self) -> Self::IntoIter {\n        IntoIter(self.0.into_iter())\n    }\n}\n\n/// An owning iterator over all the errors contained in the [`Errors`] struct.\n#[repr(transparent)]\npub struct IntoIter(std::collections::hash_map::IntoIter<ErrorId, Error>);\n\nimpl Iterator for IntoIter {\n    type Item = (ErrorId, Error);\n\n    #[inline(always)]\n    fn next(\n        &mut self,\n    ) -> std::option::Option<<Self as std::iter::Iterator>::Item> {\n        self.0.next()\n    }\n}\n\n/// An iterator over all the errors contained in the [`Errors`] struct.\n#[repr(transparent)]\npub struct Iter<'a>(std::collections::hash_map::Iter<'a, ErrorId, Error>);\n\nimpl<'a> Iterator for Iter<'a> {\n    type Item = (&'a ErrorId, &'a Error);\n\n    #[inline(always)]\n    fn next(\n        &mut self,\n    ) -> std::option::Option<<Self as std::iter::Iterator>::Item> {\n        self.0.next()\n    }\n}\n"
  },
  {
    "path": "leptos/src/for_loop.rs",
    "content": "use crate::into_view::IntoView;\nuse leptos_macro::component;\nuse reactive_graph::{\n    owner::Owner,\n    signal::{ArcRwSignal, ReadSignal},\n    traits::Set,\n};\nuse std::hash::Hash;\nuse tachys::{\n    reactive_graph::OwnedView,\n    view::keyed::{keyed, SerializableKey},\n};\n\n/// Iterates over children and displays them, keyed by the `key` function given.\n///\n/// This is much more efficient than naively iterating over nodes with `.iter().map(|n| view! { ... })...`,\n/// as it avoids re-creating DOM nodes that are not being changed.\n///\n/// ```\n/// # use leptos::prelude::*;\n///\n/// #[derive(Copy, Clone, Debug, PartialEq, Eq)]\n/// struct Counter {\n///   id: usize,\n///   count: RwSignal<i32>\n/// }\n///\n/// #[component]\n/// fn Counters() -> impl IntoView {\n///   let (counters, set_counters) = create_signal::<Vec<Counter>>(vec![]);\n///\n///   view! {\n///     <div>\n///       <For\n///         // a function that returns the items we're iterating over; a signal is fine\n///         each=move || counters.get()\n///         // a unique key for each item\n///         key=|counter| counter.id\n///         // renders each item to a view\n///         children=move |counter: Counter| {\n///           view! {\n///             <button>\"Value: \" {move || counter.count.get()}</button>\n///           }\n///         }\n///       />\n///     </div>\n///   }\n/// }\n/// ```\n///\n/// For convenience, you can also choose to write template code directly in the `<For>`\n/// component, using the `let` syntax:\n///\n/// ```\n/// # use leptos::prelude::*;\n///\n/// # #[derive(Copy, Clone, Debug, PartialEq, Eq)]\n/// # struct Counter {\n/// #   id: usize,\n/// #   count: RwSignal<i32>\n/// # }\n/// #\n/// # #[component]\n/// # fn Counters() -> impl IntoView {\n/// #   let (counters, set_counters) = create_signal::<Vec<Counter>>(vec![]);\n/// #\n///   view! {\n///     <div>\n///         <For\n///           each=move || counters.get()\n///           key=|counter| counter.id\n///           let(counter)\n///         >\n///             <button>\"Value: \" {move || counter.count.get()}</button>\n///         </For>\n///     </div>\n///   }\n/// # }\n/// ```\n///\n/// The `let` syntax also supports destructuring the pattern of your data.\n/// `let((one, two))` in the case of tuples, and `let(Struct { field_one, field_two })`\n/// in the case of structs.\n///\n/// ```\n/// # use leptos::prelude::*;\n///\n/// # #[derive(Copy, Clone, Debug, PartialEq, Eq)]\n/// # struct Counter {\n/// #   id: usize,\n/// #   count: RwSignal<i32>\n/// # }\n/// #\n/// # #[component]\n/// # fn Counters() -> impl IntoView {\n/// #   let (counters, set_counters) = create_signal::<Vec<Counter>>(vec![]);\n/// #\n///   view! {\n///     <div>\n///         <For\n///           each=move || counters.get()\n///           key=|counter| counter.id\n///           let(Counter { id, count })\n///         >\n///             <button>\"Value: \" {move || count.get()}</button>\n///         </For>\n///     </div>\n///   }\n/// # }\n/// ```\n#[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\", skip_all))]\n#[component]\npub fn For<IF, I, T, EF, N, KF, K>(\n    /// Items over which the component should iterate.\n    each: IF,\n    /// A key function that will be applied to each item.\n    key: KF,\n    /// A function that takes the item, and returns the view that will be displayed for each item.\n    children: EF,\n) -> impl IntoView\nwhere\n    IF: Fn() -> I + Send + 'static,\n    I: IntoIterator<Item = T> + Send + 'static,\n    EF: Fn(T) -> N + Send + Clone + 'static,\n    N: IntoView + 'static,\n    KF: Fn(&T) -> K + Send + Clone + 'static,\n    K: Eq + Hash + SerializableKey + 'static,\n    T: Send + 'static,\n{\n    // this takes the owner of the For itself\n    // this will end up with N + 1 children\n    // 1) the effect for the `move || keyed(...)` updates\n    // 2) an owner for each child\n    //\n    // this means\n    // a) the reactive owner for each row will not be cleared when the whole list updates\n    // b) context provided in each row will not wipe out the others\n    let parent = Owner::current().expect(\"no reactive owner\");\n    let children = move |_, child| {\n        let owner = parent.with(Owner::new);\n        let view = owner.with(|| children(child));\n        (drop, OwnedView::new_with_owner(view, owner))\n    };\n    move || keyed(each(), key.clone(), children.clone())\n}\n\n/// Iterates over children and displays them, keyed by the `key` function given.\n///\n/// Compared with For, it has an additional index parameter, which can be used to obtain the current index in real time.\n///\n/// This is much more efficient than naively iterating over nodes with `.iter().map(|n| view! { ... })...`,\n/// as it avoids re-creating DOM nodes that are not being changed.\n///\n/// ```\n/// # use leptos::prelude::*;\n///\n/// #[derive(Copy, Clone, Debug, PartialEq, Eq)]\n/// struct Counter {\n///   id: usize,\n///   count: RwSignal<i32>\n/// }\n///\n/// #[component]\n/// fn Counters() -> impl IntoView {\n///   let (counters, set_counters) = create_signal::<Vec<Counter>>(vec![]);\n///\n///   view! {\n///     <div>\n///       <ForEnumerate\n///         // a function that returns the items we're iterating over; a signal is fine\n///         each=move || counters.get()\n///         // a unique key for each item\n///         key=|counter| counter.id\n///         // renders each item to a view\n///         children={move |index: ReadSignal<usize>, counter: Counter| {\n///           view! {\n///             <button>{move || index.get()} \". Value: \" {move || counter.count.get()}</button>\n///           }\n///         }}\n///       />\n///     </div>\n///   }\n/// }\n/// ```\n#[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\", skip_all))]\n#[component]\npub fn ForEnumerate<IF, I, T, EF, N, KF, K>(\n    /// Items over which the component should iterate.\n    each: IF,\n    /// A key function that will be applied to each item.\n    key: KF,\n    /// A function that takes the index and the item, and returns the view that will be displayed for each item.\n    children: EF,\n) -> impl IntoView\nwhere\n    IF: Fn() -> I + Send + 'static,\n    I: IntoIterator<Item = T> + Send + 'static,\n    EF: Fn(ReadSignal<usize>, T) -> N + Send + Clone + 'static,\n    N: IntoView + 'static,\n    KF: Fn(&T) -> K + Send + Clone + 'static,\n    K: Eq + Hash + SerializableKey + 'static,\n    T: Send + 'static,\n{\n    // this takes the owner of the For itself\n    // this will end up with N + 1 children\n    // 1) the effect for the `move || keyed(...)` updates\n    // 2) an owner for each child\n    //\n    // this means\n    // a) the reactive owner for each row will not be cleared when the whole list updates\n    // b) context provided in each row will not wipe out the others\n    let parent = Owner::current().expect(\"no reactive owner\");\n    let children = move |index, child| {\n        let owner = parent.with(Owner::new);\n        let (index, set_index) = ArcRwSignal::new(index).split();\n        let view = owner.with(|| children(index.into(), child));\n        (\n            move |index| set_index.set(index),\n            OwnedView::new_with_owner(view, owner),\n        )\n    };\n    move || keyed(each(), key.clone(), children.clone())\n}\n\n/*\n#[cfg(test)]\nmod tests {\n    use crate::prelude::*;\n    use leptos_macro::view;\n    use tachys::{html::element::HtmlElement, prelude::ElementChild};\n\n    #[test]\n    fn creates_list() {\n        Owner::new().with(|| {\n            let values = RwSignal::new(vec![1, 2, 3, 4, 5]);\n            let list: View<HtmlElement<_, _, _>> = view! {\n                <ol>\n                    <For each=move || values.get() key=|i| *i let:i>\n                        <li>{i}</li>\n                    </For>\n                </ol>\n            };\n            assert_eq!(\n                list.to_html(),\n                \"<ol><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><!></\\\n                 ol>\"\n            );\n        });\n    }\n\n    #[test]\n    fn creates_list_enumerate() {\n        Owner::new().with(|| {\n            let values = RwSignal::new(vec![1, 2, 3, 4, 5]);\n            let list: View<HtmlElement<_, _, _>> = view! {\n                <ol>\n                    <ForEnumerate each=move || values.get() key=|i| *i let(index, i)>\n                        <li>{move || index.get()}\"-\"{i}</li>\n                    </ForEnumerate>\n                </ol>\n            };\n            assert_eq!(\n                list.to_html(),\n                \"<ol><li>0<!>-<!>1</li><li>1<!>-<!>2</li><li>2<!>-<!>3</li><li>3\\\n                <!>-<!>4</li><li>4<!>-<!>5</li><!></ol>\"\n            );\n\n            let list: View<HtmlElement<_, _, _>> = view! {\n                <ol>\n                    <ForEnumerate each=move || values.get() key=|i| *i let(index, i)>\n                        <li>{move || index.get()}\"-\"{i}</li>\n                    </ForEnumerate>\n                </ol>\n            };\n            values.set(vec![5, 4, 1, 2, 3]);\n            assert_eq!(\n                list.to_html(),\n                \"<ol><li>0<!>-<!>5</li><li>1<!>-<!>4</li><li>2<!>-<!>1</li><li>3\\\n                <!>-<!>2</li><li>4<!>-<!>3</li><!></ol>\"\n            );\n        });\n    }\n}\n */\n"
  },
  {
    "path": "leptos/src/form.rs",
    "content": "use crate::{children::Children, component, prelude::*, IntoView};\nuse leptos_dom::helpers::window;\nuse leptos_server::{ServerAction, ServerMultiAction};\nuse serde::de::DeserializeOwned;\nuse server_fn::{\n    client::Client,\n    codec::PostUrl,\n    error::{IntoAppError, ServerFnErrorErr},\n    request::ClientReq,\n    Http, ServerFn,\n};\nuse tachys::{\n    either::Either,\n    html::{\n        element::{form, Form},\n        event::submit,\n    },\n    reactive_graph::node_ref::NodeRef,\n};\nuse thiserror::Error;\nuse wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt};\nuse web_sys::{\n    Event, FormData, HtmlButtonElement, HtmlFormElement, HtmlInputElement,\n    SubmitEvent,\n};\n\n/// Automatically turns a server [Action](leptos_server::Action) into an HTML\n/// [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)\n/// progressively enhanced to use client-side routing.\n///\n/// ## Encoding\n/// **Note:** `<ActionForm/>` only works with server functions that use the\n/// default `Url` encoding. This is to ensure that `<ActionForm/>` works correctly\n/// both before and after WASM has loaded.\n///\n/// ## Complex Inputs\n/// Server function arguments that are structs with nested serializable fields\n/// should make use of indexing notation of `serde_qs`.\n///\n/// ```rust\n/// # use leptos::prelude::*;\n/// use leptos::form::ActionForm;\n///\n/// #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]\n/// struct HeftyData {\n///     first_name: String,\n///     last_name: String,\n/// }\n///\n/// #[component]\n/// fn ComplexInput() -> impl IntoView {\n///     let submit = ServerAction::<VeryImportantFn>::new();\n///\n///     view! {\n///       <ActionForm action=submit>\n///         <input type=\"text\" name=\"hefty_arg[first_name]\" value=\"leptos\"/>\n///         <input\n///           type=\"text\"\n///           name=\"hefty_arg[last_name]\"\n///           value=\"closures-everywhere\"\n///         />\n///         <input type=\"submit\"/>\n///       </ActionForm>\n///     }\n/// }\n///\n/// #[server]\n/// async fn very_important_fn(\n///     hefty_arg: HeftyData,\n/// ) -> Result<(), ServerFnError> {\n///     assert_eq!(hefty_arg.first_name.as_str(), \"leptos\");\n///     assert_eq!(hefty_arg.last_name.as_str(), \"closures-everywhere\");\n///     Ok(())\n/// }\n/// ```\n#[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\", skip_all))]\n#[component]\npub fn ActionForm<ServFn, OutputProtocol>(\n    /// The action from which to build the form.\n    action: ServerAction<ServFn>,\n    /// A [`NodeRef`] in which the `<form>` element should be stored.\n    #[prop(optional)]\n    node_ref: Option<NodeRef<Form>>,\n    /// Component children; should include the HTML of the form elements.\n    children: Children,\n) -> impl IntoView\nwhere\n    ServFn: DeserializeOwned\n        + ServerFn<Protocol = Http<PostUrl, OutputProtocol>>\n        + Clone\n        + Send\n        + Sync\n        + 'static,\n    <<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<\n        ServFn::Error,\n    >>::FormData: From<FormData>,\n    ServFn: Send + Sync + 'static,\n    ServFn::Output: Send + Sync + 'static,\n    ServFn::Error: Send + Sync + 'static,\n    <ServFn as ServerFn>::Client: Client<<ServFn as ServerFn>::Error>,\n{\n    // if redirect hook has not yet been set (by a router), defaults to a browser redirect\n    _ = server_fn::redirect::set_redirect_hook(|loc: &str| {\n        if let Some(url) = resolve_redirect_url(loc) {\n            _ = window().location().set_href(&url.href());\n        }\n    });\n\n    let version = action.version();\n    let value = action.value();\n\n    let on_submit = {\n        move |ev: SubmitEvent| {\n            if ev.default_prevented() {\n                return;\n            }\n\n            ev.prevent_default();\n\n            match ServFn::from_event(&ev) {\n                Ok(new_input) => {\n                    action.dispatch(new_input);\n                }\n                Err(err) => {\n                    crate::logging::error!(\n                        \"Error converting form field into server function \\\n                         arguments: {err:?}\"\n                    );\n                    value.set(Some(Err(ServerFnErrorErr::Serialization(\n                        err.to_string(),\n                    )\n                    .into_app_error())));\n                    version.update(|n| *n += 1);\n                }\n            }\n        }\n    };\n\n    let action_form = form()\n        .action(ServFn::url())\n        .method(\"post\")\n        .on(submit, on_submit)\n        .child(children());\n    if let Some(node_ref) = node_ref {\n        Either::Left(action_form.node_ref(node_ref))\n    } else {\n        Either::Right(action_form)\n    }\n}\n\n/// Automatically turns a server [MultiAction](leptos_server::MultiAction) into an HTML\n/// [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)\n/// progressively enhanced to use client-side routing.\n#[component]\npub fn MultiActionForm<ServFn, OutputProtocol>(\n    /// The action from which to build the form.\n    action: ServerMultiAction<ServFn>,\n    /// A [`NodeRef`] in which the `<form>` element should be stored.\n    #[prop(optional)]\n    node_ref: Option<NodeRef<Form>>,\n    /// Component children; should include the HTML of the form elements.\n    children: Children,\n) -> impl IntoView\nwhere\n    ServFn: Send\n        + Sync\n        + Clone\n        + DeserializeOwned\n        + ServerFn<Protocol = Http<PostUrl, OutputProtocol>>\n        + 'static,\n    ServFn::Output: Send + Sync + 'static,\n    <<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<\n        ServFn::Error,\n    >>::FormData: From<FormData>,\n    ServFn::Error: Send + Sync + 'static,\n    <ServFn as ServerFn>::Client: Client<<ServFn as ServerFn>::Error>,\n{\n    // if redirect hook has not yet been set (by a router), defaults to a browser redirect\n    _ = server_fn::redirect::set_redirect_hook(|loc: &str| {\n        if let Some(url) = resolve_redirect_url(loc) {\n            _ = window().location().set_href(&url.href());\n        }\n    });\n\n    let on_submit = move |ev: SubmitEvent| {\n        if ev.default_prevented() {\n            return;\n        }\n\n        ev.prevent_default();\n\n        match ServFn::from_event(&ev) {\n            Ok(new_input) => {\n                action.dispatch(new_input);\n            }\n            Err(err) => {\n                action.dispatch_sync(Err(ServerFnErrorErr::Serialization(\n                    err.to_string(),\n                )\n                .into_app_error()));\n            }\n        }\n    };\n\n    let action_form = form()\n        .action(ServFn::url())\n        .method(\"post\")\n        .attr(\"method\", \"post\")\n        .on(submit, on_submit)\n        .child(children());\n    if let Some(node_ref) = node_ref {\n        Either::Left(action_form.node_ref(node_ref))\n    } else {\n        Either::Right(action_form)\n    }\n}\n\n/// Resolves a redirect location to an (absolute) URL.\npub(crate) fn resolve_redirect_url(loc: &str) -> Option<web_sys::Url> {\n    let origin = match window().location().origin() {\n        Ok(origin) => origin,\n        Err(e) => {\n            leptos::logging::error!(\"Failed to get origin: {:#?}\", e);\n            return None;\n        }\n    };\n\n    // TODO: Use server function's URL as base instead.\n    let base = origin;\n\n    match web_sys::Url::new_with_base(loc, &base) {\n        Ok(url) => Some(url),\n        Err(e) => {\n            leptos::logging::error!(\n                \"Invalid redirect location: {}\",\n                e.as_string().unwrap_or_default(),\n            );\n            None\n        }\n    }\n}\n\n/// Tries to deserialize a type from form data. This can be used for client-side\n/// validation during form submission.\npub trait FromFormData\nwhere\n    Self: Sized + serde::de::DeserializeOwned,\n{\n    /// Tries to deserialize the data, given only the `submit` event.\n    fn from_event(ev: &web_sys::Event) -> Result<Self, FromFormDataError>;\n\n    /// Tries to deserialize the data, given the actual form data.\n    fn from_form_data(\n        form_data: &web_sys::FormData,\n    ) -> Result<Self, serde_qs::Error>;\n}\n\n/// Errors that can arise when converting from an HTML event or form into a Rust data type.\n#[derive(Error, Debug)]\npub enum FromFormDataError {\n    /// Could not find a `<form>` connected to the event.\n    #[error(\"Could not find <form> connected to event.\")]\n    MissingForm(Event),\n    /// Could not create `FormData` from the form.\n    #[error(\"Could not create FormData from <form>: {0:?}\")]\n    FormData(JsValue),\n    /// Failed to deserialize this Rust type from the form data.\n    #[error(\"Deserialization error: {0:?}\")]\n    Deserialization(serde_qs::Error),\n}\n\nimpl<T> FromFormData for T\nwhere\n    T: serde::de::DeserializeOwned,\n{\n    fn from_event(ev: &Event) -> Result<Self, FromFormDataError> {\n        let submit_ev = ev.unchecked_ref();\n        let form_data = form_data_from_event(submit_ev)?;\n        Self::from_form_data(&form_data)\n            .map_err(FromFormDataError::Deserialization)\n    }\n\n    fn from_form_data(\n        form_data: &web_sys::FormData,\n    ) -> Result<Self, serde_qs::Error> {\n        let data =\n            web_sys::UrlSearchParams::new_with_str_sequence_sequence(form_data)\n                .unwrap_throw();\n        let data = data.to_string().as_string().unwrap_or_default();\n        serde_qs::Config::new(5, false).deserialize_str::<Self>(&data)\n    }\n}\n\nfn form_data_from_event(\n    ev: &SubmitEvent,\n) -> Result<FormData, FromFormDataError> {\n    let submitter = ev.submitter();\n    let mut submitter_name_value = None;\n    let opt_form = match &submitter {\n        Some(el) => {\n            if let Some(form) = el.dyn_ref::<HtmlFormElement>() {\n                Some(form.clone())\n            } else if let Some(input) = el.dyn_ref::<HtmlInputElement>() {\n                submitter_name_value = Some((input.name(), input.value()));\n                Some(ev.target().unwrap().unchecked_into())\n            } else if let Some(button) = el.dyn_ref::<HtmlButtonElement>() {\n                submitter_name_value = Some((button.name(), button.value()));\n                Some(ev.target().unwrap().unchecked_into())\n            } else {\n                None\n            }\n        }\n        None => ev.target().map(|form| form.unchecked_into()),\n    };\n    match opt_form.as_ref().map(FormData::new_with_form) {\n        None => Err(FromFormDataError::MissingForm(ev.clone().into())),\n        Some(Err(e)) => Err(FromFormDataError::FormData(e)),\n        Some(Ok(form_data)) => {\n            if let Some((name, value)) = submitter_name_value {\n                form_data\n                    .append_with_str(&name, &value)\n                    .map_err(FromFormDataError::FormData)?;\n            }\n            Ok(form_data)\n        }\n    }\n}\n"
  },
  {
    "path": "leptos/src/from_form_data.rs",
    "content": "\n"
  },
  {
    "path": "leptos/src/hydration/hydration_script.js",
    "content": "(function (root, pkg_path, output_name, wasm_output_name) {\n\timport(`${root}/${pkg_path}/${output_name}.js`)\n\t\t.then(mod => {\n\t\t\tmod.default({module_or_path: `${root}/${pkg_path}/${wasm_output_name}.wasm`}).then(() => {\n\t\t\t\tmod.hydrate();\n\t\t\t});\n\t\t})\n})\n"
  },
  {
    "path": "leptos/src/hydration/island_script.js",
    "content": "((root, pkg_path, output_name, wasm_output_name) => {\n\tlet MOST_RECENT_CHILDREN_CB = [];\n\n\tfunction idle(c) {\n\t\tif (\"requestIdleCallback\" in window) {\n\t\t\twindow.requestIdleCallback(c);\n\t\t} else {\n\t\t\tc();\n\t\t}\n\t}\n\tasync function hydrateIslands(rootNode, mod) {\n\t\tasync function traverse(node) {\n\t\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tconst tag = node.tagName.toLowerCase();\n\t\t\t\tif(tag === 'leptos-island') {\n\t\t\t\t\tconst children = [];\n\t\t\t\t\tconst id = node.dataset.component || null;\n\n\t\t\t\t\tawait hydrateIsland(node, id, mod);\n\t\t\t\t\t\n\t\t\t\t\tfor(const child of node.children) {\n\t\t\t\t\t\tawait traverse(child, children);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (tag === 'leptos-children') {\n\t\t\t\t\t\tMOST_RECENT_CHILDREN_CB.push(node.$$on_hydrate);\n\t\t\t\t\t\tfor(const child of node.children) {\n\t\t\t\t\t\t\tawait traverse(child);\n\t\t\t\t\t\t};\n\t\t\t\t\t\t// un-set the \"most recent children\"\n\t\t\t\t\t\tMOST_RECENT_CHILDREN_CB.pop();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor(const child of node.children) {\n\t\t\t\t\t\t\tawait traverse(child);\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tawait traverse(rootNode);\n\t}\n\tasync function hydrateIsland(el, id, mod) {\n\t\tconst islandFn = mod[id];\n\t\tif (islandFn) {\n\t\t\tconst children_cb = MOST_RECENT_CHILDREN_CB[MOST_RECENT_CHILDREN_CB.length-1];\n\t\t\tif (children_cb) {\n\t\t\t\tchildren_cb();\n\t\t\t}\n\t\t\tconst res = islandFn(el);\n\t\t\tif (res && res.then) {\n\t\t\t\tawait res;\n\t\t\t}\n\t\t} else {\n\t\t\tconsole.warn(`Could not find WASM function for the island ${id}.`);\n\t\t}\n\t}\n\tidle(() => {\n\t\timport(`${root}/${pkg_path}/${output_name}.js`)\n\t\t\t.then(mod => {\n\t\t\t\tmod.default({module_or_path: `${root}/${pkg_path}/${wasm_output_name}.wasm`}).then(() => {\n\t\t\t\t\tmod.hydrate();\n\t\t\t\t\thydrateIslands(document.body, mod);\n\t\t\t\t});\n\n\t\t\t\twindow.__hydrateIsland = (el, id) => hydrateIsland(el, id, mod);\n\t\t\t})\n\t});\n})\n"
  },
  {
    "path": "leptos/src/hydration/islands_routing.js",
    "content": "let NAVIGATION = 0;\n\nwindow.addEventListener(\"click\", async (ev) => {\n\tconst req = clickToReq(ev);\n\tif(!req) {\n\t\treturn;\n\t}\n\n\tev.preventDefault();\n\tawait navigateToPage(req, true);\n});\n\nwindow.addEventListener(\"popstate\", async (ev) => {\n\tconst req = new Request(window.location);\n\tev.preventDefault();\n\tawait navigateToPage(req, true, true);\n});\n\nwindow.addEventListener(\"submit\", async (ev) => {\n\tif (ev.defaultPrevented) {\n\t\treturn;\n\t}\n\t\t\n\tconst req = submitToReq(ev);\n\tif(!req) {\n\t\treturn;\n\t}\n\n\tev.preventDefault();\n\tawait navigateToPage(req, true);\n});\n\nasync function navigateToPage(\n\t/** @type Request */\n\treq, \n\t/** @type bool */\n\tuseViewTransition, \n\t/** @type bool */\n\treplace\n) {\n\tNAVIGATION += 1;\n\tconst currentNav = NAVIGATION;\n\n\t// add a custom header to indicate that we're on a subsequent navigation \n\treq.headers.append(\"Islands-Router\", \"true\");\n\n\t// fetch the new page\n\tconst resp = await fetch(req);\n\tconst redirected = resp.redirected;\n\tconst htmlString = await resp.text();\n\n\tif(NAVIGATION === currentNav) {\n\t\t// The 'doc' variable now contains the parsed DOM\n\t\tconst transition = async () => {\n\t\t\ttry {\n\t\t\t\tdiffPages(htmlString);\n\t\t\t\tfor(const island of document.querySelectorAll(\"leptos-island\")) {\n\t\t\t\t\tif(!island.$$hydrated) {\n\t\t\t\t\t\t__hydrateIsland(island, island.dataset.component);\n\t\t\t\t\t\tisland.$$hydrated = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch(e) {\n\t\t\t\tconsole.error(e);\n\t\t\t}\n\t\t};\n\t\t// Not all browsers support startViewTransition; see https://caniuse.com/?search=startViewTransition\n\t\tif (useViewTransition && document.startViewTransition) {\n\t\t\tawait document.startViewTransition(transition);\n\t\t} else {\n\t\t\tawait transition()\n\t\t}\n\n\t\tconst url = redirected ? resp.url : req.url;\n\n\t\tif(replace) {\n\t\t\twindow.history.replaceState(undefined, null, url);\n\t\t} else {\n\t\t\twindow.history.pushState(undefined, null, url);\n\t\t}\n\t}\n}\n\nfunction clickToReq(ev) {\n\t// confirm that this is an <a> that meets our requirements\n\tif (\n\t\tev.defaultPrevented ||\n\t\tev.button !== 0 ||\n\t\tev.metaKey ||\n\t\tev.altKey ||\n\t\tev.ctrlKey ||\n\t\tev.shiftKey\n\t      )\n        return;\n\n      /** @type HTMLAnchorElement | undefined;*/\n      const a = ev\n        .composedPath()\n        .find(el => el instanceof Node && el.nodeName.toUpperCase() === \"A\");\n\n\tif (!a) return;\n\n     const svg = a.namespaceURI === \"http://www.w3.org/2000/svg\";\n\tconst href = svg ? a.href.baseVal : a.href;\n      const target = svg ? a.target.baseVal : a.target;\n      if (target || (!href && !a.hasAttribute(\"state\"))) return;\n\n      const rel = (a.getAttribute(\"rel\") || \"\").split(/\\s+/);\n      if (a.hasAttribute(\"download\") || (rel?.includes(\"external\"))) return;\n\n      const url = svg ? new URL(href, document.baseURI) : new URL(href);\n      if (\n        url.origin !== window.location.origin // ||  \n\t      // TODO base\n        //(basePath && url.pathname && !url.pathname.toLowerCase().startsWith(basePath.toLowerCase()))\n      )\n        return; \n\n      return new Request(url);\n}\n\nfunction submitToReq(ev) {\n\tevent.preventDefault();\n\n\tconst target = ev.target;\n\t/** @type HTMLFormElement */\n\tlet form;\n\tif(target instanceof HTMLFormElement) {\n\t\tform = target;\n\t} else {\n\t\tif(!target.form) {\n\t\t\treturn;\n\t\t}\n\t\tform = target.form;\n\t}\n\n\tconst method = form.method.toUpperCase();\n\tif(method !== \"GET\" && method !== \"POST\") {\n\t\treturn;\n\t}\n\n\tconst url = new URL(form.action);\n\tlet path = url.pathname;\n\tconst requestInit = {};\n\tconst data = new FormData(form);\n\n\tconst params = new URLSearchParams();\n\tfor (const [key, value] of data.entries()) {\n\t\tparams.append(key, value);\n\t}\n\n\trequestInit.headers = { \n\t\tAccept: \"text/html\"\n\t};\n\tif(method === \"GET\") {\n\t\tpath += `?${params.toString()}`;\n\t}\n\telse {\n\t\trequestInit.method = \"POST\";\n\t\trequestInit.body = params; \n\t}\n\n\treturn new Request(\n\t\tpath,\n\t\trequestInit\n\t);\n}\n\n\nfunction diffPages(htmlString) {\n\t// Use DOMParser to parse the HTML string\n\tconst parser = new DOMParser();\n\tconst doc = parser.parseFromString(htmlString, 'text/html');\n\n\tdiffRange(document, document, doc, doc);\n}\n\nfunction diffRange(oldDocument, oldRoot, newDocument, newRoot, oldEnd, newEnd) {\n\tconst oldDocWalker = oldDocument.createTreeWalker(oldRoot);\n\tconst newDocWalker = newDocument.createTreeWalker(newRoot);\n\tlet oldNode = oldDocWalker.currentNode;\n\tlet newNode = newDocWalker.currentNode;\n\n\twhile (oldDocWalker.nextNode() && newDocWalker.nextNode()) {\n\t\toldNode = oldDocWalker.currentNode;\n\t\tnewNode = newDocWalker.currentNode;\n\n\t\tif (oldNode == oldEnd || newNode == newEnd) {\n\t\t\tbreak;\n\t\t}\n\n\t\t// if the nodes are different, we need to replace the old with the new\n\t\t// because of the typed view tree, this should never actually happen\n\t\tif (oldNode.nodeType !== newNode.nodeType) {\n\t\t\toldNode.replaceWith(newNode);\n\t\t}\n\t\t// if it's a text node, just update the text with the new text\n\t\telse if (oldNode.nodeType === Node.TEXT_NODE) {\n\t\t\toldNode.textContent = newNode.textContent;\n\t\t}\n\t\t// islands should not be diffed on the client, because we do not want to overwrite client-side state \n\t\t// but their children should be diffed still, because they could contain new server content\n\t\telse if (oldNode.nodeType === Node.ELEMENT_NODE && oldNode.tagName === \"LEPTOS-ISLAND\") {\n\t\t\t// TODO: diff the leptos-children \n\n\t\t\t// skip over leptos-island otherwise\n\t\t\toldDocWalker.nextSibling();\n\t\t\tnewDocWalker.nextSibling();\n\t\t}\n\t\t// if it's an element, replace if it's a different tag, or update attributes\n\t\telse if (oldNode.nodeType === Node.ELEMENT_NODE) {\n\t\t\tdiffElement(oldNode, newNode);\n\t\t}\n\t\t// we use comment \"branch marker\" nodes to distinguish between different branches in the statically-typed view tree\n\t\t// if one of these marker is hit, then there are two options\n\t\t// 1) it's the same branch, and we just keep walking until the end \n\t\t// 2) it's a different branch, in which case the old can be replaced with the new wholesale\n\t\telse if (oldNode.nodeType === Node.COMMENT_NODE) {\n\t\t\tconst oldText = oldNode.textContent;\n\t\t\tconst newText = newNode.textContent;\n\t\t\tif(oldText.startsWith(\"bo-for\")) {\n\t\t\t\treplaceFor(oldDocument, oldDocWalker, newDocument, newDocWalker, oldNode, newNode);\n\t\t\t}\n\t\t\telse if (oldText.startsWith(\"bo-item\")) {\n\t\t\t\t// skip, this means we're diffing a new item within a For\n\t\t\t}\n\t\t\telse if(oldText.startsWith(\"bo\") && newText !== oldText) {\n\t\t\t\treplaceBranch(oldDocWalker, newDocWalker, oldNode, newNode);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction replaceFor(oldDocument, oldDocWalker, newDocument, newDocWalker, oldNode, newNode) {\n\toldDocWalker.nextNode();\n\tnewDocWalker.nextNode();\n\tconst oldRange = new Range();\n\tconst newRange = new Range();\n\tlet oldBranches = 1;\n\tlet newBranches = 1;\n\n\tconst oldKeys = {};\n\tconst newKeys = {};\n\n\twhile(oldBranches > 0) {\n\t\tconst c = oldDocWalker.currentNode;\n\t\tif(c.nodeType === Node.COMMENT_NODE) {\n\t\t\tconst t = c.textContent;\n\t\t\tif(t.startsWith(\"bo-for\")) {\n\t\t\t\toldBranches += 1;\n\t\t\t} else if(t.startsWith(\"bc-for\")) {\n\n\t\t\t\toldBranches -= 1;\n\t\t\t} else if (t.startsWith(\"bo-item\")) {\n\t\t\t\tconst k = t.replace(\"bo-item-\", \"\");\n\t\t\t\toldKeys[k] = { open: c, close: null };\n\t\t\t} else if (t.startsWith(\"bc-item\")) {\n\t\t\t\tconst k = t.replace(\"bc-item-\", \"\");\n\t\t\t\toldKeys[k].close = c;\n\t\t\t}\n\t\t}\n\t\toldDocWalker.nextNode();\n\t}\n\twhile(newBranches > 0) {\n\t\tconst c = newDocWalker.currentNode;\n\t\tif(c.nodeType === Node.COMMENT_NODE) {\n\t\t\tconst t = c.textContent;\n\t\t\tif(t.startsWith(\"bo-for\")) {\n\t\t\t\tnewBranches += 1;\n\t\t\t} else if(t.startsWith(\"bc-for\")) {\n\n\t\t\t\tnewBranches -= 1;\n\t\t\t} else if (t.startsWith(\"bo-item\")) {\n\t\t\t\tconst k = t.replace(\"bo-item-\", \"\");\n\t\t\t\tnewKeys[k] = { open: c, close: null };\n\t\t\t} else if (t.startsWith(\"bc-item\")) {\n\t\t\t\tconst k = t.replace(\"bc-item-\", \"\");\n\t\t\t\tnewKeys[k].close = c;\n\t\t\t}\n\t\t}\n\t\tnewDocWalker.nextNode();\n\t}\n\n\tfor(const key in oldKeys) {\n\t\tif(newKeys[key]) {\n\t\t\tconst oldOne = oldKeys[key];\n\t\t\tconst newOne = newKeys[key];\n\t\t\tconst oldRange = new Range();\n\t\t\tconst newRange = new Range();\n\n\t\t\t// then replace the item in the *new* list with the *old* DOM elements \n\t\t\toldRange.setStartAfter(oldOne.open);\n\t\t\toldRange.setEndBefore(oldOne.close);\n\t\t\tnewRange.setStartAfter(newOne.open);\n\t\t\tnewRange.setEndBefore(newOne.close);\n\t\t\tconst oldContents = oldRange.extractContents();\n\t\t\tconst newContents = newRange.extractContents();\n\n\t\t\t// patch the *old* DOM elements with the new ones\n\t\t\tdiffRange(oldDocument, oldContents, newDocument, newContents, oldOne.close, newOne.close);\n\n\t\t\t// then insert the old DOM elements into the new tree \n\t\t\t// this means you'll end up with any new attributes or content from the server, \n\t\t\t// but with any old DOM state (because they are the old elements)\n\t\t\tnewRange.insertNode(oldContents);\n\t\t\tnewOne.open.replaceWith(oldOne.open);\n\t\t\tnewOne.close.replaceWith(oldOne.close);\n\t\t}\n\t}\n\n\ttry {\n\t\toldRange.setStartAfter(oldNode);\n\t\toldRange.setEndBefore(oldDocWalker.currentNode);\n\t\tnewRange.setStartAfter(newNode);\n\t\tnewRange.setEndAfter(newDocWalker.currentNode);\n\t\tconst newContents = newRange.extractContents();\n\t\toldRange.deleteContents();\n\t\toldRange.insertNode(newContents);\n\t\toldNode.replaceWith(newNode);\n\t\toldDocWalker.currentNode.replaceWith(newDocWalker.currentNode);\n\t} catch (e) {\n\t\tconsole.error(e);\n\t}\n}\n\nfunction replaceBranch(oldDocWalker, newDocWalker, oldNode, newNode) {\n\toldDocWalker.nextNode();\n\tnewDocWalker.nextNode();\n\tconst oldRange = new Range();\n\tconst newRange = new Range();\n\tlet oldBranches = 1;\n\tlet newBranches = 1;\n\twhile(oldBranches > 0) {\n\t\tif(oldDocWalker.nextNode()) {\n\t\t\tif(oldDocWalker.currentNode.nodeType === Node.COMMENT_NODE) {\n\t\t\t\tif(oldDocWalker.currentNode.textContent.startsWith(\"bo\")) {\n\t\t\t\t\toldBranches += 1;\n\t\t\t\t} else if(oldDocWalker.currentNode.textContent.startsWith(\"bc\")) {\n\n\t\t\t\t\toldBranches -= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\twhile(newBranches > 0) {\n\t\tif(newDocWalker.nextNode()) {\n\t\t\tif(newDocWalker.currentNode.nodeType === Node.COMMENT_NODE) {\n\t\t\t\tif(newDocWalker.currentNode.textContent.startsWith(\"bo\")) {\n\t\t\t\t\tnewBranches += 1;\n\t\t\t\t} else if(newDocWalker.currentNode.textContent.startsWith(\"bc\")) {\n\n\t\t\t\t\tnewBranches -= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\toldRange.setStartAfter(oldNode);\n\t\toldRange.setEndBefore(oldDocWalker.currentNode);\n\t\tnewRange.setStartAfter(newNode);\n\t\tnewRange.setEndAfter(newDocWalker.currentNode);\n\t\tconst newContents = newRange.extractContents();\n\t\toldRange.deleteContents();\n\t\toldRange.insertNode(newContents);\n\t\toldNode.replaceWith(newNode);\n\t\toldDocWalker.currentNode.replaceWith(newDocWalker.currentNode);\n\t} catch (e) {\n\t\tconsole.error(e);\n\t}\n}\n\nfunction diffElement(oldNode, newNode) {\n\t/** @type Element */\n\tconst oldEl = oldNode;\n\t/** @type Element */\n\tconst newEl = newNode;\n\tif (oldEl.tagName !== newEl.tagName) {\n\t\toldEl.replaceWith(newEl);\n\n\t}\n\telse {\n\t\tfor(const attr of newEl.attributes) {\n\t\t\toldEl.setAttribute(attr.name, attr.value);\n\t\t}\n\t}\n}\n\nfor(const island of document.querySelectorAll(\"leptos-island\")) {\n\tisland.$$hydrated = true;\n}\n"
  },
  {
    "path": "leptos/src/hydration/mod.rs",
    "content": "#![allow(clippy::needless_lifetimes)]\n\nuse crate::{prelude::*, WasmSplitManifest};\nuse leptos_config::LeptosOptions;\nuse leptos_macro::{component, view};\nuse std::{path::PathBuf, sync::OnceLock};\n\n/// Inserts auto-reloading code used in `cargo-leptos`.\n///\n/// This should be included in the `<head>` of your application shell during development.\n#[component]\npub fn AutoReload(\n    /// Whether the file-watching feature should be disabled.\n    #[prop(optional)]\n    disable_watch: bool,\n    /// Configuration options for this project.\n    options: LeptosOptions,\n) -> impl IntoView {\n    (!disable_watch && std::env::var(\"LEPTOS_WATCH\").is_ok()).then(|| {\n        #[cfg(feature = \"nonce\")]\n        let nonce = crate::nonce::use_nonce();\n        #[cfg(not(feature = \"nonce\"))]\n        let nonce = None::<()>;\n\n        let reload_port = match options.reload_external_port {\n            Some(val) => val,\n            None => options.reload_port,\n        };\n        let protocol = match options.reload_ws_protocol {\n            leptos_config::ReloadWSProtocol::WS => \"'ws://'\",\n            leptos_config::ReloadWSProtocol::WSS => \"'wss://'\",\n        };\n\n        let script = format!(\n            \"(function (reload_port, protocol) {{ {} {} }})({reload_port:?}, \\\n             {protocol})\",\n            leptos_hot_reload::HOT_RELOAD_JS,\n            include_str!(\"reload_script.js\")\n        );\n        view! { <script nonce=nonce>{script}</script> }\n    })\n}\n\n/// Inserts hydration scripts that add interactivity to your server-rendered HTML.\n///\n/// This should be included in the `<head>` of your application shell.\n#[component]\npub fn HydrationScripts(\n    /// Configuration options for this project.\n    options: LeptosOptions,\n    /// Should be `true` to hydrate in `islands` mode.\n    #[prop(optional)]\n    islands: bool,\n    /// Should be `true` to add the “islands router,” which enables limited client-side routing\n    /// when running in islands mode.\n    #[prop(optional)]\n    islands_router: bool,\n    /// A base url, not including a trailing slash\n    #[prop(optional, into)]\n    root: Option<String>,\n) -> impl IntoView {\n    static SPLIT_MANIFEST: OnceLock<Option<WasmSplitManifest>> =\n        OnceLock::new();\n\n    if let Some(splits) = SPLIT_MANIFEST.get_or_init(|| {\n        let root = root.clone().unwrap_or_default();\n\n        let (wasm_split_js, wasm_split_manifest) = if options.hash_files {\n            let hash_path = std::env::current_exe()\n                .map(|path| {\n                    path.parent().map(|p| p.to_path_buf()).unwrap_or_default()\n                })\n                .unwrap_or_default()\n                .join(options.hash_file.as_ref());\n            let hashes = std::fs::read_to_string(&hash_path)\n                .expect(\"failed to read hash file\");\n\n            let mut split =\n                \"__wasm_split.______________________.js\".to_string();\n            let mut manifest = \"__wasm_split_manifest.json\".to_string();\n            for line in hashes.lines() {\n                let line = line.trim();\n                if !line.is_empty() {\n                    if let Some((file, hash)) = line.split_once(':') {\n                        if file == \"manifest\" {\n                            manifest.clear();\n                            manifest.push_str(\"__wasm_split_manifest.\");\n                            manifest.push_str(hash.trim());\n                            manifest.push_str(\".json\");\n                        }\n                        if file == \"split\" {\n                            split.clear();\n                            split.push_str(\"__wasm_split.\");\n                            split.push_str(hash.trim());\n                            split.push_str(\".js\");\n                        }\n                    }\n                }\n            }\n            (split, manifest)\n        } else {\n            (\n                \"__wasm_split.______________________.js\".to_string(),\n                \"__wasm_split_manifest.json\".to_string(),\n            )\n        };\n\n        let site_dir = &options.site_root;\n        let pkg_dir = &options.site_pkg_dir;\n        let path = PathBuf::from(site_dir.to_string());\n        let path = path.join(pkg_dir.to_string()).join(wasm_split_manifest);\n        let file = std::fs::read_to_string(path).ok()?;\n\n        let manifest = WasmSplitManifest(ArcStoredValue::new((\n            format!(\"{root}/{pkg_dir}\"),\n            serde_json::from_str(&file).expect(\"could not read manifest file\"),\n            wasm_split_js,\n        )));\n\n        Some(manifest)\n    }) {\n        provide_context(splits.clone());\n    }\n\n    let mut js_file_name = options.output_name.to_string();\n    let mut wasm_file_name = options.output_name.to_string();\n    if options.hash_files {\n        let hash_path = std::env::current_exe()\n            .map(|path| {\n                path.parent().map(|p| p.to_path_buf()).unwrap_or_default()\n            })\n            .unwrap_or_default()\n            .join(options.hash_file.as_ref());\n        if hash_path.exists() {\n            let hashes = std::fs::read_to_string(&hash_path)\n                .expect(\"failed to read hash file\");\n            for line in hashes.lines() {\n                let line = line.trim();\n                if !line.is_empty() {\n                    if let Some((file, hash)) = line.split_once(':') {\n                        if file == \"js\" {\n                            js_file_name.push_str(&format!(\".{}\", hash.trim()));\n                        } else if file == \"wasm\" {\n                            wasm_file_name\n                                .push_str(&format!(\".{}\", hash.trim()));\n                        }\n                    }\n                }\n            }\n        } else {\n            leptos::logging::error!(\n                \"File hashing is active but no hash file was found\"\n            );\n        }\n    } else if std::option_env!(\"LEPTOS_OUTPUT_NAME\").is_none() {\n        wasm_file_name.push_str(\"_bg\");\n    }\n\n    let pkg_path = &options.site_pkg_dir;\n    #[cfg(feature = \"nonce\")]\n    let nonce = crate::nonce::use_nonce();\n    #[cfg(not(feature = \"nonce\"))]\n    let nonce = None::<String>;\n    let script = if islands {\n        if let Some(sc) = Owner::current_shared_context() {\n            sc.set_is_hydrating(false);\n        }\n        include_str!(\"./island_script.js\")\n    } else {\n        include_str!(\"./hydration_script.js\")\n    };\n\n    let islands_router = islands_router\n        .then_some(include_str!(\"./islands_routing.js\"))\n        .unwrap_or_default();\n\n    let root = root.unwrap_or_default();\n    view! {\n        <link rel=\"modulepreload\" href=format!(\"{root}/{pkg_path}/{js_file_name}.js\") crossorigin=nonce.clone()/>\n        <link\n            rel=\"preload\"\n            href=format!(\"{root}/{pkg_path}/{wasm_file_name}.wasm\")\n            r#as=\"fetch\"\n            r#type=\"application/wasm\"\n            crossorigin=nonce.clone().unwrap_or_default()\n        />\n        <script type=\"module\" nonce=nonce>\n            {format!(\"{script}({root:?}, {pkg_path:?}, {js_file_name:?}, {wasm_file_name:?});{islands_router}\")}\n        </script>\n    }\n}\n\n/// If this is provided via context, it means that you are using the islands router and\n/// this is a subsequent navigation, made from the client.\n///\n/// This should be provided automatically by a server integration if it detects that the\n/// header `Islands-Router` is present in the request.\n///\n/// This is used to determine how much of the hydration script to include in the page.\n/// If it is present, then the contents of the `<HydrationScripts>` component will not be\n/// included, as they only need to be sent to the client once.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct IslandsRouterNavigation;\n"
  },
  {
    "path": "leptos/src/hydration/reload_script.js",
    "content": "if (window.location.protocol === \"https:\") {\n\tprotocol = \"wss://\";\n}\n\nlet host = window.location.hostname;\n\nfunction connect() {\n\tlet ws = new WebSocket(`${protocol}${host}:${reload_port}/live_reload`);\n\n\tws.onmessage = (ev) => {\n\t\tlet msg = JSON.parse(ev.data);\n\t\tif (msg.all) window.location.reload();\n\t\tif (msg.css) {\n\t\t\tlet found = false;\n\t\t\tdocument.querySelectorAll(\"link\").forEach((link) => {\n\t\t\t\tif (link.getAttribute(\"href\").includes(msg.css)) {\n\t\t\t\t\tlet newHref = \"/\" + msg.css + \"?version=\" + Date.now();\n\t\t\t\t\tlink.setAttribute(\"href\", newHref);\n\t\t\t\t\tfound = true;\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (!found)\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`CSS hot-reload: Could not find a <link href=/\\\"${msg.css}\\\"> element`,\n\t\t\t\t);\n\t\t}\n\t\tif (msg.view) {\n\t\t\tpatch(msg.view);\n\t\t}\n\t};\n\n\tws.onclose = () => {\n\t\tconsole.warn(\"Live-reload disconnected. Reconnecting...\");\n\t\tsetTimeout(connect, 1000);\n\t};\n\n\tws.onerror = () => {\n\t\tws.close();\n\t};\n}\n\nconnect();\n"
  },
  {
    "path": "leptos/src/into_view.rs",
    "content": "use std::borrow::Cow;\nuse tachys::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    ssr::StreamBuilder,\n    view::{\n        add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,\n        ToTemplate,\n    },\n};\n\n/// A wrapper for any kind of view.\n#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]\npub struct View<T>\nwhere\n    T: Sized,\n{\n    inner: T,\n    #[cfg(debug_assertions)]\n    view_marker: Option<Cow<'static, str>>,\n}\n\nimpl<T> View<T> {\n    /// Wraps the view.\n    pub fn new(inner: T) -> Self {\n        Self {\n            inner,\n            #[cfg(debug_assertions)]\n            view_marker: None,\n        }\n    }\n\n    /// Unwraps the view, returning the inner type.\n    pub fn into_inner(self) -> T {\n        self.inner\n    }\n\n    /// Adds a view marker, which is used for hot-reloading and debug purposes.\n    #[inline(always)]\n    pub fn with_view_marker(\n        #[allow(unused_mut)] // used in debug\n        mut self,\n        #[allow(unused_variables)] // used in debug\n        view_marker: impl Into<Cow<'static, str>>,\n    ) -> Self {\n        #[cfg(debug_assertions)]\n        {\n            self.view_marker = Some(view_marker.into());\n        }\n        self\n    }\n}\n\n/// A trait that is implemented for types that can be rendered.\npub trait IntoView\nwhere\n    Self: Sized + Render + RenderHtml + Send,\n{\n    /// Wraps the inner type.\n    fn into_view(self) -> View<Self>;\n}\n\nimpl<T> IntoView for T\nwhere\n    T: Sized + Render + RenderHtml + Send, //+ AddAnyAttr,\n{\n    fn into_view(self) -> View<Self> {\n        View {\n            inner: self,\n            #[cfg(debug_assertions)]\n            view_marker: None,\n        }\n    }\n}\n\nimpl<T: Render> Render for View<T> {\n    type State = T::State;\n\n    fn build(self) -> Self::State {\n        self.inner.build()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.inner.rebuild(state)\n    }\n}\n\nimpl<T: RenderHtml> RenderHtml for View<T> {\n    type AsyncOutput = T::AsyncOutput;\n    type Owned = View<T::Owned>;\n\n    const MIN_LENGTH: usize = <T as RenderHtml>::MIN_LENGTH;\n    const EXISTS: bool = <T as RenderHtml>::EXISTS;\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self.inner.resolve().await\n    }\n\n    fn dry_resolve(&mut self) {\n        self.inner.dry_resolve();\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        #[cfg(debug_assertions)]\n        let vm = if option_env!(\"LEPTOS_WATCH\").is_some() {\n            self.view_marker.to_owned()\n        } else {\n            None\n        };\n\n        #[cfg(debug_assertions)]\n        if let Some(vm) = vm.as_ref() {\n            buf.push_str(&format!(\"<!--hot-reload|{vm}|open-->\"));\n        }\n\n        self.inner.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n\n        #[cfg(debug_assertions)]\n        if let Some(vm) = vm.as_ref() {\n            buf.push_str(&format!(\"<!--hot-reload|{vm}|close-->\"));\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        #[cfg(debug_assertions)]\n        let vm = if option_env!(\"LEPTOS_WATCH\").is_some() {\n            self.view_marker.to_owned()\n        } else {\n            None\n        };\n\n        #[cfg(debug_assertions)]\n        if let Some(vm) = vm.as_ref() {\n            buf.push_sync(&format!(\"<!--hot-reload|{vm}|open-->\"));\n        }\n\n        self.inner.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n\n        #[cfg(debug_assertions)]\n        if let Some(vm) = vm.as_ref() {\n            buf.push_sync(&format!(\"<!--hot-reload|{vm}|close-->\"));\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        self.inner.hydrate::<FROM_SERVER>(cursor, position)\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        self.inner.hydrate_async(cursor, position).await\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        View {\n            inner: self.inner.into_owned(),\n            #[cfg(debug_assertions)]\n            view_marker: self.view_marker,\n        }\n    }\n}\n\nimpl<T: ToTemplate> ToTemplate for View<T> {\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        T::to_template(buf, class, style, inner_html, position);\n    }\n}\n\nimpl<T: AddAnyAttr> AddAnyAttr for View<T> {\n    type Output<SomeNewAttr: Attribute> = View<T::Output<SomeNewAttr>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let View {\n            inner,\n            #[cfg(debug_assertions)]\n            view_marker,\n        } = self;\n        View {\n            inner: inner.add_any_attr(attr),\n            #[cfg(debug_assertions)]\n            view_marker,\n        }\n    }\n}\n\n/// Collects some iterator of views into a list, so they can be rendered.\n///\n/// This is a shorthand for `.collect::<Vec<_>>()`, and allows any iterator of renderable\n/// items to be collected into a renderable collection.\npub trait CollectView {\n    /// The inner view type.\n    type View: IntoView;\n\n    /// Collects the iterator into a list of views.\n    fn collect_view(self) -> Vec<Self::View>;\n}\n\nimpl<It, V> CollectView for It\nwhere\n    It: IntoIterator<Item = V>,\n    V: IntoView,\n{\n    type View = V;\n\n    fn collect_view(self) -> Vec<Self::View> {\n        self.into_iter().collect()\n    }\n}\n"
  },
  {
    "path": "leptos/src/lib.rs",
    "content": "#![deny(missing_docs)]\n\n//! # About Leptos\n//!\n//! Leptos is a full-stack framework for building web applications in Rust. You can use it to build\n//! - single-page apps (SPAs) rendered entirely in the browser, using client-side routing and loading\n//!   or mutating data via async requests to the server.\n//! - multi-page apps (MPAs) rendered on the server, managing navigation, data, and mutations via\n//!   web-standard `<a>` and `<form>` tags.\n//! - progressively-enhanced single-page apps that are rendered on the server and then hydrated on the client,\n//!   enhancing your `<a>` and `<form>` navigations and mutations seamlessly when WASM is available.\n//!\n//! And you can do all three of these **using the same Leptos code**.\n//!\n//! Take a look at the [Leptos Book](https://leptos-rs.github.io/leptos/) for a walkthrough of the framework.\n//! Join us on our [Discord Channel](https://discord.gg/v38Eef6sWG) to see what the community is building.\n//! Explore our [Examples](https://github.com/leptos-rs/leptos/tree/main/examples) to see Leptos in action.\n//!\n//! # Learning by Example\n//!\n//! If you want to see what Leptos is capable of, check out\n//! the [examples](https://github.com/leptos-rs/leptos/tree/main/examples):\n//!\n//! - **[`counter`]** is the classic counter example, showing the basics of client-side rendering and reactive DOM updates.\n//! - **[`counter_without_macros`]** adapts the counter example to use the builder pattern for the UI and avoids other macros,\n//!   instead showing the code that Leptos generates.\n//! - **[`counters`]** introduces parent-child communication via contexts, and the [`<For/>`](leptos::prelude::For) component\n//!   for efficient keyed list updates.\n//! - **[`error_boundary`]** shows how to use [`Result`] types to handle errors.\n//! - **[`parent_child`]** shows four different ways a parent component can communicate with a child, including passing a closure,\n//!   context, and more.\n//! - **[`fetch`]** introduces [`Resource`](leptos::prelude::Resource)s, which allow you to integrate arbitrary `async` code like an\n//!   HTTP request within your reactive code.\n//! - **[`router`]** shows how to use Leptos’s nested router to enable client-side navigation and route-specific, reactive data loading.\n//! - **[`slots`]** shows how to use slots on components.\n//! - **[`spread`]** shows how the spread syntax can be used to spread data and/or event handlers onto elements.\n//! - **[`counter_isomorphic`]** shows different methods of interaction with a stateful server, including server functions,\n//!   server actions, forms, and server-sent events (SSE).\n//! - **[`todomvc`]** shows the basics of building an isomorphic web app. Both the server and the client import the same app code.\n//!   The server renders the app directly to an HTML string, and the client hydrates that HTML to make it interactive.\n//!   You might also want to see how we use [`Effect::new`](leptos::prelude::Effect) to\n//!   [serialize JSON to `localStorage`](https://github.com/leptos-rs/leptos/blob/20af4928b2fffe017408d3f4e7330db22cf68277/examples/todomvc/src/lib.rs#L191-L209)\n//!   and [reactively call DOM methods](https://github.com/leptos-rs/leptos/blob/16f084a71268ac325fbc4a5e50c260df185eadb6/examples/todomvc/src/lib.rs#L292-L296)\n//!   on [references to elements](https://github.com/leptos-rs/leptos/blob/20af4928b2fffe017408d3f4e7330db22cf68277/examples/todomvc/src/lib.rs#L228).\n//! - **[`hackernews`]** and **[`hackernews_axum`]** integrate calls to a real external REST API, routing, server-side rendering and\n//!   hydration to create a fully-functional application that works as intended even before WASM has loaded and begun to run.\n//! - **[`todo_app_sqlite`]** and **[`todo_app_sqlite_axum`]** show how to build a full-stack app using server functions and\n//!   database connections.\n//! - **[`tailwind`]** shows how to integrate TailwindCSS with `trunk` for CSR.\n//!\n//! [`counter`]: https://github.com/leptos-rs/leptos/tree/main/examples/counter\n//! [`counter_without_macros`]: https://github.com/leptos-rs/leptos/tree/main/examples/counter_without_macros\n//! [`counters`]: https://github.com/leptos-rs/leptos/tree/main/examples/counters\n//! [`error_boundary`]: https://github.com/leptos-rs/leptos/tree/main/examples/error_boundary\n//! [`parent_child`]: https://github.com/leptos-rs/leptos/tree/main/examples/parent_child\n//! [`fetch`]: https://github.com/leptos-rs/leptos/tree/main/examples/fetch\n//! [`router`]: https://github.com/leptos-rs/leptos/tree/main/examples/router\n//! [`slots`]: https://github.com/leptos-rs/leptos/tree/main/examples/slots\n//! [`spread`]: https://github.com/leptos-rs/leptos/tree/main/examples/spread\n//! [`counter_isomorphic`]: https://github.com/leptos-rs/leptos/tree/main/examples/counter_isomorphic\n//! [`todomvc`]: https://github.com/leptos-rs/leptos/tree/main/examples/todomvc\n//! [`hackernews`]: https://github.com/leptos-rs/leptos/tree/main/examples/hackernews\n//! [`hackernews_axum`]: https://github.com/leptos-rs/leptos/tree/main/examples/hackernews_axum\n//! [`todo_app_sqlite`]: https://github.com/leptos-rs/leptos/tree/main/examples/todo_app_sqlite\n//! [`todo_app_sqlite_axum`]: https://github.com/leptos-rs/leptos/tree/main/examples/todo_app_sqlite_axum\n//! [`tailwind`]: https://github.com/leptos-rs/leptos/tree/main/examples/tailwind_csr\n//!\n//! Details on how to run each example can be found in its README.\n//!\n//! # Quick Links\n//!\n//! Here are links to the most important sections of the docs:\n//! - **Reactivity**: the [`reactive_graph`] overview, and more details in\n//!   + signals: [`signal`](leptos::prelude::signal), [`ReadSignal`](leptos::prelude::ReadSignal),\n//!     [`WriteSignal`](leptos::prelude::WriteSignal) and [`RwSignal`](leptos::prelude::RwSignal).\n//!   + computations: [`Memo`](leptos::prelude::Memo).\n//!   + `async` interop: [`Resource`](leptos::prelude::Resource) for loading data using `async` functions\n//!     and [`Action`](leptos::prelude::Action) to mutate data or imperatively call `async` functions.\n//!   + reactions: [`Effect`](leptos::prelude::Effect) and [`RenderEffect`](leptos::prelude::RenderEffect).\n//! - **Templating/Views**: the [`view`] macro and [`IntoView`] trait.\n//! - **Routing**: the [`leptos_router`](https://docs.rs/leptos_router/latest/leptos_router/) crate\n//! - **Server Functions**: the [`server`](macro@leptos::prelude::server) macro and [`ServerAction`](leptos::prelude::ServerAction).\n//!\n//! # Feature Flags\n//!\n//! - **`nightly`**: On `nightly` Rust, enables the function-call syntax for signal getters and setters.\n//!   Also enables some experimental optimizations that improve the handling of static strings and\n//!   the performance of the `template! {}` macro.\n//! - **`csr`** Client-side rendering: Generate DOM nodes in the browser.\n//! - **`ssr`** Server-side rendering: Generate an HTML string (typically on the server).\n//! - **`islands`** Activates “islands mode,” in which components are not made interactive on the\n//!   client unless they use the `#[island]` macro.\n//! - **`hydrate`** Hydration: use this to add interactivity to an SSRed Leptos app.\n//! - **`nonce`** Adds support for nonces to be added as part of a Content Security Policy.\n//! - **`rkyv`** In SSR/hydrate mode, enables using [`rkyv`](https://docs.rs/rkyv/latest/rkyv/) to serialize resources.\n//! - **`tracing`** Adds support for [`tracing`](https://docs.rs/tracing/latest/tracing/).\n//! - **`trace-component-props`** Adds `tracing` support for component props.\n//! - **`delegation`** Uses event delegation rather than the browser’s native event handling\n//!   system. (This improves the performance of creating large numbers of elements simultaneously,\n//!   in exchange for occasional edge cases in which events behave differently from native browser\n//!   events.)\n//! - **`rustls`** Use `rustls` for server functions.\n//!\n//! **Important Note:** You must enable one of `csr`, `hydrate`, or `ssr` to tell Leptos\n//! which mode your app is operating in. You should only enable one of these per build target,\n//! i.e., you should not have both `hydrate` and `ssr` enabled for your server binary, only `ssr`.\n//!\n//! # A Simple Counter\n//!\n//! ```rust\n//! use leptos::prelude::*;\n//!\n//! #[component]\n//! pub fn SimpleCounter(initial_value: i32) -> impl IntoView {\n//!     // create a reactive signal with the initial value\n//!     let (value, set_value) = signal(initial_value);\n//!\n//!     // create event handlers for our buttons\n//!     // note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures\n//!     let clear = move |_| set_value.set(0);\n//!     let decrement = move |_| *set_value.write() -= 1;\n//!     let increment = move |_| *set_value.write() += 1;\n//!\n//!     view! {\n//!         <div>\n//!             <button on:click=clear>\"Clear\"</button>\n//!             <button on:click=decrement>\"-1\"</button>\n//!             <span>\"Value: \" {value} \"!\"</span>\n//!             <button on:click=increment>\"+1\"</button>\n//!         </div>\n//!     }\n//! }\n//! ```\n//!\n//! Leptos is easy to use with [Trunk](https://trunkrs.dev/) (or with a simple wasm-bindgen setup):\n//!\n//! ```rust\n//! use leptos::{mount::mount_to_body, prelude::*};\n//!\n//! #[component]\n//! fn SimpleCounter(initial_value: i32) -> impl IntoView {\n//!     // ...\n//!     # _ = initial_value;\n//! }\n//!\n//! pub fn main() {\n//! # if false { // can't run in doctest\n//!     mount_to_body(|| view! { <SimpleCounter initial_value=3 /> })\n//! # }\n//! }\n//! ```\n\n#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(fn_traits))]\n#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(unboxed_closures))]\n\nextern crate self as leptos;\n\n/// Exports all the core types of the library.\npub mod prelude {\n    // Traits\n    // These should always be exported from the prelude\n    pub use reactive_graph::prelude::*;\n    pub use tachys::prelude::*;\n\n    // Structs\n    // In the future, maybe we should remove this blanket export\n    // However, it is definitely useful relative to looking up every struct etc.\n    mod export_types {\n        #[cfg(feature = \"nonce\")]\n        pub use crate::nonce::*;\n        pub use crate::{\n            callback::*, children::*, component::*, control_flow::*, error::*,\n            form::*, hydration::*, into_view::*, mount::*, suspense::*,\n            text_prop::*,\n        };\n        pub use leptos_config::*;\n        pub use leptos_dom::helpers::*;\n        pub use leptos_macro::*;\n        pub use leptos_server::*;\n        pub use oco_ref::*;\n        pub use reactive_graph::{\n            actions::*,\n            computed::*,\n            effect::*,\n            graph::untrack,\n            owner::*,\n            signal::*,\n            wrappers::{read::*, write::*},\n        };\n        pub use server_fn::{\n            self,\n            error::{FromServerFnError, ServerFnError, ServerFnErrorErr},\n        };\n        pub use tachys::{\n            reactive_graph::{bind::BindAttribute, node_ref::*, Suspend},\n            view::{fragment::Fragment, template::ViewTemplate},\n        };\n    }\n    pub use export_types::*;\n}\n\n/// Components used for working with HTML forms, like `<ActionForm>`.\npub mod form;\n\n/// A standard way to wrap functions and closures to pass them to components.\npub use reactive_graph::callback;\n\n/// Types that can be passed as the `children` prop of a component.\npub mod children;\n\n/// Wrapper for intercepting component attributes.\npub mod attribute_interceptor;\n\n#[doc(hidden)]\n/// Traits used to implement component constructors.\npub mod component;\nmod error_boundary;\n\n/// Tools for handling errors.\npub mod error {\n    pub use crate::error_boundary::*;\n    pub use throw_error::*;\n}\n\n/// Control-flow components like `<Show>`, `<For>`, and `<Await>`.\npub mod control_flow {\n    pub use crate::{\n        animated_show::*, await_::*, for_loop::*, show::*, show_let::*,\n    };\n}\nmod animated_show;\nmod await_;\nmod for_loop;\nmod show;\nmod show_let;\n\n/// A component that allows rendering a component somewhere else.\npub mod portal;\n\n/// Components to enable server-side rendering and client-side hydration.\npub mod hydration;\n\n/// Utilities for exporting nonces to be used for a Content Security Policy.\n#[cfg(feature = \"nonce\")]\npub mod nonce;\n\n/// Components to load asynchronous data.\npub mod suspense {\n    pub use crate::{suspense_component::*, transition::*};\n}\n\n#[macro_use]\nmod suspense_component;\n\n/// Types for reactive string properties for components.\npub mod text_prop;\nmod transition;\npub use leptos_macro::*;\n#[doc(inline)]\npub use server_fn;\n#[doc(hidden)]\npub use typed_builder;\n#[doc(hidden)]\npub use typed_builder_macro;\nmod into_view;\npub use into_view::IntoView;\n#[doc(inline)]\npub use leptos_dom;\nmod provider;\n#[doc(inline)]\npub use tachys;\n/// Tools to mount an application to the DOM, or to hydrate it from server-rendered HTML.\npub mod mount;\n#[doc(inline)]\npub use leptos_config as config;\n#[doc(inline)]\npub use oco_ref as oco;\nmod from_form_data;\n#[doc(inline)]\npub use either_of as either;\n#[doc(inline)]\npub use reactive_graph as reactive;\n\n/// Provide and access data along the reactive graph, sharing data without directly passing arguments.\npub mod context {\n    pub use crate::provider::*;\n    pub use reactive_graph::owner::{provide_context, use_context};\n}\n\n#[doc(inline)]\npub use leptos_server as server;\n/// HTML attribute types.\n#[doc(inline)]\npub use tachys::html::attribute as attr;\n/// HTML element types.\n#[doc(inline)]\npub use tachys::html::element as html;\n/// HTML event types.\n#[doc(no_inline)]\npub use tachys::html::event as ev;\n/// MathML element types.\n#[doc(inline)]\npub use tachys::mathml as math;\n/// SVG element types.\n#[doc(inline)]\npub use tachys::svg;\n\n#[cfg(feature = \"subsecond\")]\n/// Utilities for using binary hot-patching with [`subsecond`].\npub mod subsecond;\n\n/// Utilities for simple isomorphic logging to the console or terminal.\npub mod logging {\n    pub use leptos_dom::{\n        debug_error, debug_log, debug_warn, error, log, warn,\n    };\n}\n\n/// Utilities for working with asynchronous tasks.\npub mod task {\n    use any_spawner::Executor;\n    use reactive_graph::computed::ScopedFuture;\n    use std::future::Future;\n\n    /// Spawns a thread-safe [`Future`].\n    ///\n    /// This will be run with the current reactive owner and observer using a [`ScopedFuture`].\n    #[track_caller]\n    #[inline(always)]\n    pub fn spawn(fut: impl Future<Output = ()> + Send + 'static) {\n        let fut = ScopedFuture::new(fut);\n\n        #[cfg(not(target_family = \"wasm\"))]\n        Executor::spawn(fut);\n\n        #[cfg(target_family = \"wasm\")]\n        Executor::spawn_local(fut);\n    }\n\n    /// Spawns a [`Future`] that cannot be sent across threads.\n    #[track_caller]\n    #[inline(always)]\n    pub fn spawn_local(fut: impl Future<Output = ()> + 'static) {\n        Executor::spawn_local(fut)\n    }\n\n    /// Waits until the next \"tick\" of the current async executor.\n    pub async fn tick() {\n        Executor::tick().await\n    }\n\n    pub use reactive_graph::{\n        spawn_local_scoped, spawn_local_scoped_with_cancellation,\n    };\n}\n\n// these reexports are used in islands\n#[cfg(feature = \"islands\")]\n#[doc(hidden)]\npub use serde;\n#[doc(hidden)]\npub use serde_json;\n#[cfg(feature = \"tracing\")]\n#[doc(hidden)]\npub use tracing;\n#[doc(hidden)]\npub use wasm_bindgen;\n#[doc(hidden)]\npub use wasm_split_helpers as wasm_split;\n#[doc(hidden)]\npub use web_sys;\n\n#[doc(hidden)]\npub mod __reexports {\n    pub use send_wrapper;\n    pub use wasm_bindgen_futures;\n}\n\n#[doc(hidden)]\n#[derive(Clone, Debug, Default)]\npub struct PrefetchLazyFn(\n    pub  reactive_graph::owner::ArcStoredValue<\n        std::collections::HashSet<&'static str>,\n    >,\n);\n\n#[doc(hidden)]\npub fn prefetch_lazy_fn_on_server(id: &'static str) {\n    use crate::context::use_context;\n    use reactive_graph::traits::WriteValue;\n\n    if let Some(prefetches) = use_context::<PrefetchLazyFn>() {\n        prefetches.0.write_value().insert(id);\n    }\n}\n\n#[doc(hidden)]\n#[derive(Clone, Debug, Default)]\npub struct WasmSplitManifest(\n    pub  reactive_graph::owner::ArcStoredValue<(\n        String,                                         // the pkg root\n        std::collections::HashMap<String, Vec<String>>, // preloads\n        String, // the name of the __wasm_split.js file\n    )>,\n);\n"
  },
  {
    "path": "leptos/src/logging.rs",
    "content": "//! Utilities for simple isomorphic logging to the console or terminal.\n\nuse wasm_bindgen::JsValue;\n\n/// Uses `println!()`-style formatting to log something to the console (in the browser)\n/// or via `println!()` (if not in the browser).\n#[macro_export]\nmacro_rules! log {\n    ($($t:tt)*) => ($crate::logging::console_log(&format_args!($($t)*).to_string()))\n}\n\n/// Uses `println!()`-style formatting to log warnings to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser).\n#[macro_export]\nmacro_rules! warn {\n    ($($t:tt)*) => ($crate::logging::console_warn(&format_args!($($t)*).to_string()))\n}\n\n/// Uses `println!()`-style formatting to log errors to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser).\n#[macro_export]\nmacro_rules! error {\n    ($($t:tt)*) => ($crate::logging::console_error(&format_args!($($t)*).to_string()))\n}\n\n/// Uses `println!()`-style formatting to log warnings to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser), but only if it's a debug build.\n#[macro_export]\nmacro_rules! debug_warn {\n    ($($x:tt)*) => {\n        {\n            #[cfg(debug_assertions)]\n            {\n                $crate::warn!($($x)*)\n            }\n            #[cfg(not(debug_assertions))]\n            {\n                ($($x)*)\n            }\n        }\n    }\n}\n\nconst fn log_to_stdout() -> bool {\n    cfg!(not(all(\n        target_arch = \"wasm32\",\n        not(any(target_os = \"emscripten\", target_os = \"wasi\"))\n    )))\n}\n\n/// Log a string to the console (in the browser)\n/// or via `println!()` (if not in the browser).\npub fn console_log(s: &str) {\n    #[allow(clippy::print_stdout)]\n    if log_to_stdout() {\n        println!(\"{s}\");\n    } else {\n        web_sys::console::log_1(&JsValue::from_str(s));\n    }\n}\n\n/// Log a warning to the console (in the browser)\n/// or via `println!()` (if not in the browser).\npub fn console_warn(s: &str) {\n    if log_to_stdout() {\n        eprintln!(\"{s}\");\n    } else {\n        web_sys::console::warn_1(&JsValue::from_str(s));\n    }\n}\n\n/// Log an error to the console (in the browser)\n/// or via `println!()` (if not in the browser).\n#[inline(always)]\npub fn console_error(s: &str) {\n    if log_to_stdout() {\n        eprintln!(\"{s}\");\n    } else {\n        web_sys::console::error_1(&JsValue::from_str(s));\n    }\n}\n\n/// Log an error to the console (in the browser)\n/// or via `println!()` (if not in the browser), but only in a debug build.\n#[inline(always)]\npub fn console_debug_warn(s: &str) {\n    #[cfg(debug_assertions)]\n    {\n        if log_to_stdout() {\n            eprintln!(\"{s}\");\n        } else {\n            web_sys::console::warn_1(&JsValue::from_str(s));\n        }\n    }\n\n    #[cfg(not(debug_assertions))]\n    {\n        let _ = s;\n    }\n}\n\n\n"
  },
  {
    "path": "leptos/src/mount.rs",
    "content": "#[cfg(debug_assertions)]\nuse crate::logging;\nuse crate::IntoView;\nuse any_spawner::Executor;\nuse reactive_graph::owner::Owner;\n#[cfg(debug_assertions)]\nuse std::cell::Cell;\nuse tachys::{\n    dom::body,\n    view::{Mountable, Render},\n};\n#[cfg(feature = \"hydrate\")]\nuse tachys::{\n    hydration::Cursor,\n    view::{PositionState, RenderHtml},\n};\n#[cfg(feature = \"hydrate\")]\nuse wasm_bindgen::JsCast;\nuse web_sys::HtmlElement;\n\n#[cfg(feature = \"hydrate\")]\n/// Hydrates the app described by the provided function, starting at `<body>`.\npub fn hydrate_body<F, N>(f: F)\nwhere\n    F: FnOnce() -> N + 'static,\n    N: IntoView,\n{\n    let owner = hydrate_from(body(), f);\n    owner.forget();\n}\n\n#[cfg(feature = \"hydrate\")]\n/// Hydrates the app described by the provided function, starting at `<body>`, with support\n/// for lazy-loaded routes and components.\npub fn hydrate_lazy<F, N>(f: F)\nwhere\n    F: FnOnce() -> N + 'static,\n    N: IntoView,\n{\n    // use wasm-bindgen-futures to drive the reactive system\n    // we ignore the return value because an Err here just means the wasm-bindgen executor is\n    // already initialized, which is not an issue\n    _ = Executor::init_wasm_bindgen();\n\n    crate::task::spawn_local(async move {\n        let owner = hydrate_from_async(body(), f).await;\n        owner.forget();\n    })\n}\n\n#[cfg(debug_assertions)]\nthread_local! {\n    static FIRST_CALL: Cell<bool> = const { Cell::new(true) };\n}\n\n#[cfg(feature = \"hydrate\")]\n/// Runs the provided closure and mounts the result to the provided element.\npub fn hydrate_from<F, N>(parent: HtmlElement, f: F) -> UnmountHandle<N::State>\nwhere\n    F: FnOnce() -> N + 'static,\n    N: IntoView,\n{\n    use hydration_context::HydrateSharedContext;\n    use std::sync::Arc;\n\n    // use wasm-bindgen-futures to drive the reactive system\n    // we ignore the return value because an Err here just means the wasm-bindgen executor is\n    // already initialized, which is not an issue\n    _ = Executor::init_wasm_bindgen();\n\n    #[cfg(debug_assertions)]\n    {\n        if !cfg!(feature = \"hydrate\") && FIRST_CALL.get() {\n            logging::warn!(\n                \"It seems like you're trying to use Leptos in hydration mode, \\\n                 but the `hydrate` feature is not enabled on the `leptos` \\\n                 crate. Add `features = [\\\"hydrate\\\"]` to your Cargo.toml for \\\n                 the crate to work properly.\\n\\nNote that hydration and \\\n                 client-side rendering now use separate functions from \\\n                 leptos::mount: you are calling a hydration function.\"\n            );\n        }\n        FIRST_CALL.set(false);\n    }\n\n    // create a new reactive owner and use it as the root node to run the app\n    let owner = Owner::new_root(Some(Arc::new(HydrateSharedContext::new())));\n    let mountable = owner.with(move || {\n        let view = f().into_view();\n        view.hydrate::<true>(\n            &Cursor::new(parent.unchecked_into()),\n            &PositionState::default(),\n        )\n    });\n\n    if let Some(sc) = Owner::current_shared_context() {\n        sc.hydration_complete();\n    }\n\n    // returns a handle that owns the owner\n    // when this is dropped, it will clean up the reactive system and unmount the view\n    UnmountHandle { owner, mountable }\n}\n\n#[cfg(feature = \"hydrate\")]\n/// Runs the provided closure and mounts the result to the provided element.\npub async fn hydrate_from_async<F, N>(\n    parent: HtmlElement,\n    f: F,\n) -> UnmountHandle<N::State>\nwhere\n    F: FnOnce() -> N + 'static,\n    N: IntoView,\n{\n    use hydration_context::HydrateSharedContext;\n    use std::sync::Arc;\n\n    // use wasm-bindgen-futures to drive the reactive system\n    // we ignore the return value because an Err here just means the wasm-bindgen executor is\n    // already initialized, which is not an issue\n    _ = Executor::init_wasm_bindgen();\n\n    #[cfg(debug_assertions)]\n    {\n        if !cfg!(feature = \"hydrate\") && FIRST_CALL.get() {\n            logging::warn!(\n                \"It seems like you're trying to use Leptos in hydration mode, \\\n                 but the `hydrate` feature is not enabled on the `leptos` \\\n                 crate. Add `features = [\\\"hydrate\\\"]` to your Cargo.toml for \\\n                 the crate to work properly.\\n\\nNote that hydration and \\\n                 client-side rendering now use separate functions from \\\n                 leptos::mount: you are calling a hydration function.\"\n            );\n        }\n        FIRST_CALL.set(false);\n    }\n\n    // create a new reactive owner and use it as the root node to run the app\n    let owner = Owner::new_root(Some(Arc::new(HydrateSharedContext::new())));\n    let mountable = owner\n        .with(move || {\n            use reactive_graph::computed::ScopedFuture;\n\n            ScopedFuture::new(async move {\n                let view = f().into_view();\n                view.hydrate_async(\n                    &Cursor::new(parent.unchecked_into()),\n                    &PositionState::default(),\n                )\n                .await\n            })\n        })\n        .await;\n\n    if let Some(sc) = Owner::current_shared_context() {\n        sc.hydration_complete();\n    }\n\n    // returns a handle that owns the owner\n    // when this is dropped, it will clean up the reactive system and unmount the view\n    UnmountHandle { owner, mountable }\n}\n\n/// Runs the provided closure and mounts the result to the `<body>`.\npub fn mount_to_body<F, N>(f: F)\nwhere\n    F: FnOnce() -> N + 'static,\n    N: IntoView,\n{\n    let owner = mount_to(body(), f);\n    owner.forget();\n}\n\n/// Runs the provided closure and mounts the result to the provided element.\npub fn mount_to<F, N>(parent: HtmlElement, f: F) -> UnmountHandle<N::State>\nwhere\n    F: FnOnce() -> N + 'static,\n    N: IntoView,\n{\n    // use wasm-bindgen-futures to drive the reactive system\n    // we ignore the return value because an Err here just means the wasm-bindgen executor is\n    // already initialized, which is not an issue\n    _ = Executor::init_wasm_bindgen();\n\n    #[cfg(debug_assertions)]\n    {\n        if !cfg!(feature = \"csr\") && FIRST_CALL.get() {\n            logging::warn!(\n                \"It seems like you're trying to use Leptos in client-side \\\n                 rendering mode, but the `csr` feature is not enabled on the \\\n                 `leptos` crate. Add `features = [\\\"csr\\\"]` to your \\\n                 Cargo.toml for the crate to work properly.\\n\\nNote that \\\n                 hydration and client-side rendering now use different \\\n                 functions from leptos::mount. You are using a client-side \\\n                 rendering mount function.\"\n            );\n        }\n        FIRST_CALL.set(false);\n    }\n\n    // create a new reactive owner and use it as the root node to run the app\n    let owner = Owner::new();\n    let mountable = owner.with(move || {\n        let view = f().into_view();\n        let mut mountable = view.build();\n        mountable.mount(&parent, None);\n        mountable\n    });\n\n    // returns a handle that owns the owner\n    // when this is dropped, it will clean up the reactive system and unmount the view\n    UnmountHandle { owner, mountable }\n}\n\n/// Runs the provided closure and mounts the result to the provided element.\npub fn mount_to_renderer<F, N>(\n    parent: &tachys::renderer::types::Element,\n    f: F,\n) -> UnmountHandle<N::State>\nwhere\n    F: FnOnce() -> N + 'static,\n    N: Render,\n{\n    // use wasm-bindgen-futures to drive the reactive system\n    // we ignore the return value because an Err here just means the wasm-bindgen executor is\n    // already initialized, which is not an issue\n    _ = Executor::init_wasm_bindgen();\n\n    // create a new reactive owner and use it as the root node to run the app\n    let owner = Owner::new();\n    let mountable = owner.with(move || {\n        let view = f();\n        let mut mountable = view.build();\n        mountable.mount(parent, None);\n        mountable\n    });\n\n    // returns a handle that owns the owner\n    // when this is dropped, it will clean up the reactive system and unmount the view\n    UnmountHandle { owner, mountable }\n}\n\n/// Hydrates any islands that are currently present on the page.\n#[cfg(feature = \"hydrate\")]\npub fn hydrate_islands() {\n    use hydration_context::{HydrateSharedContext, SharedContext};\n    use std::sync::Arc;\n\n    // use wasm-bindgen-futures to drive the reactive system\n    // we ignore the return value because an Err here just means the wasm-bindgen executor is\n    // already initialized, which is not an issue\n    _ = Executor::init_wasm_bindgen();\n\n    #[cfg(debug_assertions)]\n    FIRST_CALL.set(false);\n\n    // create a new reactive owner and use it as the root node to run the app\n    let sc = HydrateSharedContext::new();\n    sc.set_is_hydrating(false); // islands mode starts in \"not hydrating\"\n    let owner = Owner::new_root(Some(Arc::new(sc)));\n    owner.set();\n    std::mem::forget(owner);\n}\n\n/// On drop, this will clean up the reactive [`Owner`] and unmount the view created by\n/// [`mount_to`].\n///\n/// If you are using it to create the root of an application, you should use\n/// [`UnmountHandle::forget`] to leak it.\n#[must_use = \"Dropping an `UnmountHandle` will unmount the view and cancel the \\\n              reactive system. You should either call `.forget()` to keep the \\\n              view permanently mounted, or store the `UnmountHandle` somewhere \\\n              and drop it when you'd like to unmount the view.\"]\npub struct UnmountHandle<M>\nwhere\n    M: Mountable,\n{\n    #[allow(dead_code)]\n    owner: Owner,\n    mountable: M,\n}\n\nimpl<M> UnmountHandle<M>\nwhere\n    M: Mountable,\n{\n    /// Leaks the handle, preventing the reactive system from being cleaned up and the view from\n    /// being unmounted. This should always be called when [`mount_to`] is used for the root of an\n    /// application that should live for the long term.\n    pub fn forget(self) {\n        std::mem::forget(self);\n    }\n}\n\nimpl<M> Drop for UnmountHandle<M>\nwhere\n    M: Mountable,\n{\n    fn drop(&mut self) {\n        self.mountable.unmount();\n    }\n}\n"
  },
  {
    "path": "leptos/src/nonce.rs",
    "content": "use crate::context::{provide_context, use_context};\nuse base64::{\n    alphabet,\n    engine::{self, general_purpose},\n    Engine,\n};\nuse rand::{rng, RngCore};\nuse std::{fmt::Display, ops::Deref, sync::Arc};\nuse tachys::html::attribute::AttributeValue;\n\n/// A cryptographic nonce (\"number used once\") which can be\n/// used by Content Security Policy to determine whether or not a given\n/// resource will be allowed to load.\n///\n/// When the `nonce` feature is enabled on one of the server integrations,\n/// a nonce is generated during server rendering and added to all inline\n/// scripts used for HTML streaming and resource loading.\n///\n/// The nonce being used during the current server response can be\n/// accessed using [`use_nonce`].\n///\n/// ```rust,ignore\n/// #[component]\n/// pub fn App() -> impl IntoView {\n///     provide_meta_context;\n///\n///     view! {\n///         // use `leptos_meta` to insert a <meta> tag with the CSP\n///         <Meta\n///             http_equiv=\"Content-Security-Policy\"\n///             content=move || {\n///                 // this will insert the CSP with nonce on the server, be empty on client\n///                 use_nonce()\n///                     .map(|nonce| {\n///                         format!(\n///                             \"default-src 'self'; script-src 'strict-dynamic' 'nonce-{nonce}' \\\n///                             'wasm-unsafe-eval'; style-src 'nonce-{nonce}';\"\n///                         )\n///                     })\n///                     .unwrap_or_default()\n///             }\n///         />\n///         // manually insert nonce during SSR on inline script\n///         <script nonce=use_nonce()>\"console.log('Hello, world!');\"</script>\n///         // leptos_meta <Style/> and <Script/> automatically insert the nonce\n///         <Style>\"body { color: blue; }\"</Style>\n///         <p>\"Test\"</p>\n///     }\n/// }\n/// ```\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub struct Nonce(pub(crate) Arc<str>);\n\nimpl Nonce {\n    /// Returns a reference to the inner reference-counted string slice representing the nonce.\n    pub fn as_inner(&self) -> &Arc<str> {\n        &self.0\n    }\n}\n\nimpl Deref for Nonce {\n    type Target = str;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl Display for Nonce {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\n\nimpl AttributeValue for Nonce {\n    type AsyncOutput = Self;\n    type State = <Arc<str> as AttributeValue>::State;\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        self.0.len()\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        <Arc<str> as AttributeValue>::to_html(self.0, key, buf)\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &tachys::renderer::types::Element,\n    ) -> Self::State {\n        <Arc<str> as AttributeValue>::hydrate::<FROM_SERVER>(self.0, key, el)\n    }\n\n    fn build(\n        self,\n        el: &tachys::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        <Arc<str> as AttributeValue>::build(self.0, el, key)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        <Arc<str> as AttributeValue>::rebuild(self.0, key, state)\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\n/// Accesses the nonce that has been generated during the current\n/// server response. This can be added to inline `<script>` and\n/// `<style>` tags for compatibility with a Content Security Policy.\n///\n/// ```rust,ignore\n/// #[component]\n/// pub fn App() -> impl IntoView {\n///     provide_meta_context;\n///\n///     view! {\n///         // use `leptos_meta` to insert a <meta> tag with the CSP\n///         <Meta\n///             http_equiv=\"Content-Security-Policy\"\n///             content=move || {\n///                 // this will insert the CSP with nonce on the server, be empty on client\n///                 use_nonce()\n///                     .map(|nonce| {\n///                         format!(\n///                             \"default-src 'self'; script-src 'strict-dynamic' 'nonce-{nonce}' \\\n///                             'wasm-unsafe-eval'; style-src 'nonce-{nonce}';\"\n///                         )\n///                     })\n///                     .unwrap_or_default()\n///             }\n///         />\n///         // manually insert nonce during SSR on inline script\n///         <script nonce=use_nonce()>\"console.log('Hello, world!');\"</script>\n///         // leptos_meta <Style/> and <Script/> automatically insert the nonce\n///         <Style>\"body { color: blue; }\"</Style>\n///         <p>\"Test\"</p>\n///     }\n/// }\n/// ```\npub fn use_nonce() -> Option<Nonce> {\n    use_context::<Nonce>()\n}\n\n/// Generates a nonce and provides it via context.\npub fn provide_nonce() {\n    provide_context(Nonce::new())\n}\n\nconst NONCE_ENGINE: engine::GeneralPurpose =\n    engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::NO_PAD);\n\nimpl Nonce {\n    /// Generates a new nonce from 16 bytes (128 bits) of random data.\n    pub fn new() -> Self {\n        let mut rng = rng();\n        let mut bytes = [0; 16];\n        rng.fill_bytes(&mut bytes);\n        Nonce(NONCE_ENGINE.encode(bytes).into())\n    }\n}\n\nimpl Default for Nonce {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "leptos/src/portal.rs",
    "content": "use crate::{children::TypedChildrenFn, mount, IntoView};\nuse leptos_dom::helpers::document;\nuse leptos_macro::component;\nuse reactive_graph::{effect::Effect, graph::untrack, owner::Owner};\nuse std::sync::Arc;\n\n/// Renders components somewhere else in the DOM.\n///\n/// Useful for inserting modals and tooltips outside of a cropping layout.\n/// If no mount point is given, the portal is inserted in `document.body`;\n/// it is wrapped in a `<div>` unless  `is_svg` is `true` in which case it's wrapped in a `<g>`.\n/// Setting `use_shadow` to `true` places the element in a shadow root to isolate styles.\n#[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\", skip_all))]\n#[component]\npub fn Portal<V>(\n    /// Target element where the children will be appended\n    #[prop(into, optional)]\n    mount: Option<web_sys::Element>,\n    /// Whether to use a shadow DOM inside `mount`. Defaults to `false`.\n    #[prop(optional)]\n    use_shadow: bool,\n    /// When using SVG this has to be set to `true`. Defaults to `false`.\n    #[prop(optional)]\n    is_svg: bool,\n    /// The children to teleport into the `mount` element\n    children: TypedChildrenFn<V>,\n) -> impl IntoView\nwhere\n    V: IntoView + 'static,\n{\n    if cfg!(target_arch = \"wasm32\")\n        && Owner::current_shared_context()\n            .map(|sc| sc.is_browser())\n            .unwrap_or(true)\n    {\n        use send_wrapper::SendWrapper;\n        use wasm_bindgen::JsCast;\n\n        let mount = mount.unwrap_or_else(|| {\n            document().body().expect(\"body to exist\").unchecked_into()\n        });\n        let children = children.into_inner();\n\n        Effect::new(move |_| {\n            let container = if is_svg {\n                document()\n                    .create_element_ns(Some(\"http://www.w3.org/2000/svg\"), \"g\")\n                    .expect(\"SVG element creation to work\")\n            } else {\n                document()\n                    .create_element(\"div\")\n                    .expect(\"HTML element creation to work\")\n            };\n\n            let render_root = if use_shadow {\n                container\n                    .attach_shadow(&web_sys::ShadowRootInit::new(\n                        web_sys::ShadowRootMode::Open,\n                    ))\n                    .map(|root| root.unchecked_into())\n                    .unwrap_or(container.clone())\n            } else {\n                container.clone()\n            };\n\n            let _ = mount.append_child(&container);\n            let handle = SendWrapper::new((\n                mount::mount_to(render_root.unchecked_into(), {\n                    let children = Arc::clone(&children);\n                    move || untrack(|| children())\n                }),\n                mount.clone(),\n                container,\n            ));\n\n            Owner::on_cleanup({\n                move || {\n                    let (handle, mount, container) = handle.take();\n                    drop(handle);\n                    let _ = mount.remove_child(&container);\n                }\n            })\n        });\n    }\n}\n"
  },
  {
    "path": "leptos/src/provider.rs",
    "content": "use crate::{children::TypedChildren, component, IntoView};\nuse reactive_graph::owner::{provide_context, Owner};\nuse tachys::reactive_graph::OwnedView;\n\n#[component]\n/// Uses the context API to [`provide_context`] to its children and descendants,\n/// without overwriting any contexts of the same type in its own reactive scope.\n///\n/// This prevents issues related to “context shadowing.”\n///\n/// ```rust\n/// use leptos::{context::Provider, prelude::*};\n///\n/// #[component]\n/// pub fn App() -> impl IntoView {\n///     // each Provider will only provide the value to its children\n///     view! {\n///         <Provider value=1u8>\n///             // correctly gets 1 from context\n///             {use_context::<u8>().unwrap_or(0)}\n///         </Provider>\n///         <Provider value=2u8>\n///             // correctly gets 2 from context\n///             {use_context::<u8>().unwrap_or(0)}\n///         </Provider>\n///         // does not find any u8 in context\n///         {use_context::<u8>().unwrap_or(0)}\n///     }\n/// }\n/// ```\npub fn Provider<T, Chil>(\n    /// The value to be provided via context.\n    value: T,\n    children: TypedChildren<Chil>,\n) -> impl IntoView\nwhere\n    T: Send + Sync + 'static,\n    Chil: IntoView + 'static,\n{\n    let owner = Owner::current()\n        .expect(\"no current reactive Owner found\")\n        .child();\n    let children = children.into_inner();\n    let children = owner.with(|| {\n        provide_context(value);\n        children()\n    });\n    OwnedView::new_with_owner(children, owner)\n}\n"
  },
  {
    "path": "leptos/src/show.rs",
    "content": "use crate::{\n    children::{TypedChildrenFn, ViewFn},\n    IntoView,\n};\nuse leptos_macro::component;\nuse reactive_graph::{computed::ArcMemo, traits::Get};\nuse tachys::either::Either;\n\n#[component(transparent)]\npub fn Show<W, C>(\n    /// The children will be shown whenever the condition in the `when` closure returns `true`.\n    children: TypedChildrenFn<C>,\n    /// A closure that returns a bool that determines whether this thing runs\n    when: W,\n    /// A closure that returns what gets rendered if the when statement is false. By default this is the empty view.\n    #[prop(optional, into)]\n    fallback: ViewFn,\n) -> impl IntoView\nwhere\n    W: Fn() -> bool + Send + Sync + 'static,\n    C: IntoView + 'static,\n{\n    let memoized_when = ArcMemo::new(move |_| when());\n    let children = children.into_inner();\n\n    move || match memoized_when.get() {\n        true => Either::Left(children()),\n        false => Either::Right(fallback.run()),\n    }\n}\n"
  },
  {
    "path": "leptos/src/show_let.rs",
    "content": "use crate::{children::ViewFn, IntoView};\nuse leptos_macro::component;\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\nuse reactive_graph::traits::Get;\nuse std::{marker::PhantomData, sync::Arc};\nuse tachys::either::Either;\n\n/// Like `<Show>` but for `Option`. This is a shortcut for\n///\n/// ```ignore\n/// value.map(|value| {\n///     view! { ... }\n/// })\n/// ```\n///\n/// If you specify a `fallback` it is equvalent to\n///\n/// ```ignore\n/// value\n///     .map(\n///         |value| children(value),\n///     )\n///     .unwrap_or_else(fallback)\n/// ```\n///\n/// ## Example\n///\n/// ```\n/// # use leptos::prelude::*;\n/// #\n/// # #[component]\n/// # pub fn Example() -> impl IntoView {\n/// let (opt_value, set_opt_value) = signal(None::<i32>);\n///\n/// view! {\n///     <ShowLet some=opt_value let:value>\n///         \"We have a value: \" {value}\n///     </ShowLet>\n/// }\n/// # }\n/// ```\n///\n/// You can also specify a fallback:\n/// ```\n/// # use leptos::prelude::*;\n/// #\n/// # #[component]\n/// # pub fn Example() -> impl IntoView {\n/// let (opt_value, set_opt_value) = signal(None::<i32>);\n///\n/// view! {\n///     <ShowLet some=opt_value let:value fallback=|| \"Got nothing\">\n///         \"We have a value: \" {value}\n///     </ShowLet>\n/// }\n/// # }\n/// ```\n///\n/// In addition to signals you can also use a closure that returns an `Option`:\n///\n/// ```\n/// # use leptos::prelude::*;\n/// #\n/// # #[component]\n/// # pub fn Example() -> impl IntoView {\n/// let (opt_value, set_opt_value) = signal(None::<i32>);\n///\n/// view! {\n///     <ShowLet some=move || opt_value.get().map(|v| v * 2) let:value>\n///         \"We have a value: \" {value}\n///     </ShowLet>\n/// }\n/// # }\n/// ```\n#[component(transparent)]\npub fn ShowLet<T, ChFn, V, M>(\n    /// The children will be shown whenever `value` is `Some`.\n    ///\n    /// They take the inner value as an argument. Use `let:` to bind the value to a variable.\n    children: ChFn,\n\n    /// A signal of type `Option` or a closure that returns an `Option`.\n    /// If the value is `Some`, the children will be shown.\n    /// Otherwise the fallback will be shown, if present.\n    some: impl IntoOptionGetter<T, M>,\n\n    /// A closure that returns what gets rendered when the value is `None`.\n    /// By default this is the empty view.\n    ///\n    /// You can think of it as the closure inside `.unwrap_or_else(|| fallback())`.\n    #[prop(optional, into)]\n    fallback: ViewFn,\n\n    /// Marker for generic parameters. Ignore this.\n    #[prop(optional)]\n    _marker: PhantomData<(T, M)>,\n) -> impl IntoView\nwhere\n    ChFn: Fn(T) -> V + Send + Clone + 'static,\n    V: IntoView + 'static,\n    T: 'static,\n{\n    let getter = some.into_option_getter();\n\n    move || {\n        let children = children.clone();\n        let fallback = fallback.clone();\n\n        getter\n            .run()\n            .map(move |t| Either::Left(children(t)))\n            .unwrap_or_else(move || Either::Right(fallback.run()))\n    }\n}\n\n/// Servers as a wrapper for both, an `Option` signal or a closure that returns an `Option`.\npub struct OptionGetter<T>(Arc<dyn Fn() -> Option<T> + Send + Sync + 'static>);\n\nimpl<T> Clone for OptionGetter<T> {\n    fn clone(&self) -> Self {\n        Self(Arc::clone(&self.0))\n    }\n}\n\nimpl<T> OptionGetter<T> {\n    /// Runs the getter and returns the result.\n    pub fn run(&self) -> Option<T> {\n        (self.0)()\n    }\n}\n\n/// Conversion trait for creating an `OptionGetter` from a closure or a signal.\npub trait IntoOptionGetter<T, M> {\n    /// Converts the given value into an `OptionGetter`.\n    fn into_option_getter(self) -> OptionGetter<T>;\n}\n\n/// Marker type for creating an `OptionGetter` from a closure.\n/// Used so that the compiler doesn't complain about double implementations of the trait `IntoOptionGetter`.\npub struct FunctionMarker;\n\nimpl<T, F> IntoOptionGetter<T, FunctionMarker> for F\nwhere\n    F: Fn() -> Option<T> + Send + Sync + 'static,\n{\n    fn into_option_getter(self) -> OptionGetter<T> {\n        OptionGetter(Arc::new(self))\n    }\n}\n\n/// Marker type for creating an `OptionGetter` from a signal.\n/// Used so that the compiler doesn't complain about double implementations of the trait `IntoOptionGetter`.\n///\n/// On nightly, signal types implement `Fn() -> T` directly, so they go through\n/// the `FunctionMarker` impl instead. This impl is only needed on stable where\n/// signals don't implement `Fn()`.\npub struct SignalMarker;\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\nimpl<T, S> IntoOptionGetter<T, SignalMarker> for S\nwhere\n    S: Get<Value = Option<T>> + Clone + Send + Sync + 'static,\n{\n    fn into_option_getter(self) -> OptionGetter<T> {\n        let cloned = self.clone();\n        OptionGetter(Arc::new(move || cloned.get()))\n    }\n}\n\n/// Marker type for creating an `OptionGetter` from a static value.\n/// Used so that the compiler doesn't complain about double implementations of the trait `IntoOptionGetter`.\npub struct StaticMarker;\n\nimpl<T> IntoOptionGetter<T, StaticMarker> for Option<T>\nwhere\n    T: Clone + Send + Sync + 'static,\n{\n    fn into_option_getter(self) -> OptionGetter<T> {\n        OptionGetter(Arc::new(move || self.clone()))\n    }\n}\n"
  },
  {
    "path": "leptos/src/subsecond.rs",
    "content": "use dioxus_devtools::DevserverMsg;\nuse wasm_bindgen::{prelude::Closure, JsCast};\nuse web_sys::{js_sys::JsString, MessageEvent, WebSocket};\n\n/// Sets up a websocket connect to the `dx` CLI, waiting for incoming hot-patching messages\n/// and patching the WASM binary appropriately.\n//\n//  Note: This is a stripped-down version of Dioxus's `make_ws` from `dioxus_web`\n//  It's essentially copy-pasted here because it's not pub there.\n//  Would love to just take a dependency on that to be able to use it and deduplicate.\n//\n//  https://github.com/DioxusLabs/dioxus/blob/main/packages/web/src/devtools.rs#L36\npub fn connect_to_hot_patch_messages() {\n    // Get the location of the devserver, using the current location plus the /_dioxus path\n    // The idea here being that the devserver is always located on the /_dioxus behind a proxy\n    let location = web_sys::window().unwrap().location();\n    let url = format!(\n        \"{protocol}//{host}/_dioxus?build_id={build_id}\",\n        protocol = match location.protocol().unwrap() {\n            prot if prot == \"https:\" => \"wss:\",\n            _ => \"ws:\",\n        },\n        host = location.host().unwrap(),\n        build_id = dioxus_cli_config::build_id(),\n    );\n\n    let ws = WebSocket::new(&url).unwrap();\n\n    ws.set_onmessage(Some(\n        Closure::<dyn FnMut(MessageEvent)>::new(move |e: MessageEvent| {\n            let Ok(text) = e.data().dyn_into::<JsString>() else {\n                return;\n            };\n\n            // The devserver messages have some &'static strs in them, so we need to leak the source string\n            let string: String = text.into();\n            let string = Box::leak(string.into_boxed_str());\n\n            if let Ok(DevserverMsg::HotReload(msg)) =\n                serde_json::from_str::<DevserverMsg>(string)\n            {\n                if let Some(jump_table) = msg.jump_table.as_ref().cloned() {\n                    if msg.for_build_id == Some(dioxus_cli_config::build_id()) {\n                        let our_pid = if cfg!(target_family = \"wasm\") {\n                            None\n                        } else {\n                            Some(std::process::id())\n                        };\n\n                        if msg.for_pid == our_pid {\n                            unsafe { subsecond::apply_patch(jump_table) }\n                                .unwrap();\n                        }\n                    }\n                }\n            }\n        })\n        .into_js_value()\n        .as_ref()\n        .unchecked_ref(),\n    ));\n}\n"
  },
  {
    "path": "leptos/src/suspense_component.rs",
    "content": "use crate::{\n    children::{TypedChildren, ViewFnOnce},\n    error::ErrorBoundarySuspendedChildren,\n    IntoView,\n};\nuse futures::{channel::oneshot, select, FutureExt};\nuse hydration_context::SerializedDataId;\nuse leptos_macro::component;\nuse or_poisoned::OrPoisoned;\nuse reactive_graph::{\n    computed::{\n        suspense::{LocalResourceNotifier, SuspenseContext},\n        ArcMemo, ScopedFuture,\n    },\n    effect::RenderEffect,\n    owner::{provide_context, use_context, Owner},\n    signal::ArcRwSignal,\n    traits::{\n        Dispose, Get, Read, ReadUntracked, Track, With, WithUntracked,\n        WriteValue,\n    },\n};\nuse slotmap::{DefaultKey, SlotMap};\nuse std::sync::{Arc, Mutex};\nuse tachys::{\n    either::Either,\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    reactive_graph::{OwnedView, OwnedViewState},\n    ssr::StreamBuilder,\n    view::{\n        add_attr::AddAnyAttr,\n        either::{EitherKeepAlive, EitherKeepAliveState},\n        Mountable, Position, PositionState, Render, RenderHtml,\n    },\n};\nuse throw_error::ErrorHookFuture;\n\n/// If any [`Resource`](crate::prelude::Resource) is read in the `children` of this\n/// component, it will show the `fallback` while they are loading. Once all are resolved,\n/// it will render the `children`.\n///\n/// Each time one of the resources is loading again, it will fall back. To keep the current\n/// children instead, use [Transition](crate::prelude::Transition).\n///\n/// Note that the `children` will be rendered initially (in order to capture the fact that\n/// those resources are read under the suspense), so you cannot assume that resources read\n/// synchronously have\n/// `Some` value in `children`. However, you can read resources asynchronously by using\n/// [Suspend](crate::prelude::Suspend).\n///\n/// ```\n/// # use leptos::prelude::*;\n/// # if false { // don't run in doctests\n/// async fn fetch_cats(how_many: u32) -> Vec<String> { vec![] }\n///\n/// let (cat_count, set_cat_count) = signal::<u32>(1);\n///\n/// let cats = Resource::new(move || cat_count.get(), |count| fetch_cats(count));\n///\n/// view! {\n///   <div>\n///     <Suspense fallback=move || view! { <p>\"Loading (Suspense Fallback)...\"</p> }>\n///       // you can access a resource synchronously\n///       {move || {\n///           cats.get().map(|data| {\n///             data\n///               .into_iter()\n///               .map(|src| {\n///                   view! {\n///                     <img src={src}/>\n///                   }\n///               })\n///               .collect_view()\n///           })\n///         }\n///       }\n///       // or you can use `Suspend` to read resources asynchronously\n///       {move || Suspend::new(async move {\n///         cats.await\n///               .into_iter()\n///               .map(|src| {\n///                   view! {\n///                     <img src={src}/>\n///                   }\n///               })\n///               .collect_view()\n///       })}\n///     </Suspense>\n///   </div>\n/// }\n/// # ;}\n/// ```\n#[component]\npub fn Suspense<Chil>(\n    /// A function that returns a fallback that will be shown while resources are still loading.\n    /// By default this is an empty view.\n    #[prop(optional, into)]\n    fallback: ViewFnOnce,\n    /// Children will be rendered once initially to catch any resource reads, then hidden until all\n    /// data have loaded.\n    children: TypedChildren<Chil>,\n) -> impl IntoView\nwhere\n    Chil: IntoView + Send + 'static,\n{\n    let error_boundary_parent = use_context::<ErrorBoundarySuspendedChildren>();\n\n    let owner = Owner::new();\n    owner.with(|| {\n        let (starts_local, id) = {\n            Owner::current_shared_context()\n                .map(|sc| {\n                    let id = sc.next_id();\n                    (sc.get_incomplete_chunk(&id), id)\n                })\n                .unwrap_or_else(|| (false, Default::default()))\n        };\n        let fallback = fallback.run();\n        let children = children.into_inner()();\n        let tasks = ArcRwSignal::new(SlotMap::<DefaultKey, ()>::new());\n        provide_context(SuspenseContext {\n            tasks: tasks.clone(),\n        });\n        let none_pending = ArcMemo::new({\n            let tasks = tasks.clone();\n            move |prev: Option<&bool>| {\n                tasks.track();\n                if prev.is_none() && starts_local {\n                    false\n                } else {\n                    tasks.with(SlotMap::is_empty)\n                }\n            }\n        });\n        let has_tasks =\n            Arc::new(move || !tasks.with_untracked(SlotMap::is_empty));\n\n        OwnedView::new(SuspenseBoundary::<false, _, _> {\n            id,\n            none_pending,\n            fallback,\n            children,\n            error_boundary_parent,\n            has_tasks,\n        })\n    })\n}\n\nfn nonce_or_not() -> Option<Arc<str>> {\n    #[cfg(feature = \"nonce\")]\n    {\n        use crate::nonce::Nonce;\n        use_context::<Nonce>().map(|n| n.0)\n    }\n    #[cfg(not(feature = \"nonce\"))]\n    {\n        None\n    }\n}\n\npub(crate) struct SuspenseBoundary<const TRANSITION: bool, Fal, Chil> {\n    pub id: SerializedDataId,\n    pub none_pending: ArcMemo<bool>,\n    pub fallback: Fal,\n    pub children: Chil,\n    pub error_boundary_parent: Option<ErrorBoundarySuspendedChildren>,\n    pub has_tasks: Arc<dyn Fn() -> bool + Send + Sync>,\n}\n\nimpl<const TRANSITION: bool, Fal, Chil> Render\n    for SuspenseBoundary<TRANSITION, Fal, Chil>\nwhere\n    Fal: Render + Send + 'static,\n    Chil: Render + Send + 'static,\n{\n    type State = RenderEffect<\n        OwnedViewState<EitherKeepAliveState<Chil::State, Fal::State>>,\n    >;\n\n    fn build(self) -> Self::State {\n        let mut children = Some(self.children);\n        let mut fallback = Some(self.fallback);\n        let none_pending = self.none_pending;\n        let mut nth_run = 0;\n        let outer_owner = Owner::new();\n\n        RenderEffect::new(move |prev| {\n            // show the fallback if\n            // 1) there are pending futures, and\n            // 2) we are either in a Suspense (not Transition), or it's the first fallback\n            //    (because we initially render the children to register Futures, the \"first\n            //    fallback\" is probably the 2nd run\n            let show_b = !none_pending.get() && (!TRANSITION || nth_run < 2);\n            nth_run += 1;\n            let this = OwnedView::new_with_owner(\n                EitherKeepAlive {\n                    a: children.take(),\n                    b: fallback.take(),\n                    show_b,\n                },\n                outer_owner.clone(),\n            );\n\n            let state = if let Some(mut state) = prev {\n                this.rebuild(&mut state);\n                state\n            } else {\n                this.build()\n            };\n\n            if nth_run == 1 && !(self.has_tasks)() {\n                // if this is the first run, and there are no pending resources at this point,\n                // it means that there were no actually-async resources read while rendering the children\n                // this means that we're effectively on the settled second run: none_pending\n                // won't change false => true and cause this to rerender (and therefore increment nth_run)\n                //\n                // we increment it manually here so that future resource changes won't cause the transition fallback\n                // to be displayed for the first time\n                // see https://github.com/leptos-rs/leptos/issues/3868, https://github.com/leptos-rs/leptos/issues/4492\n                nth_run += 1;\n            }\n\n            state\n        })\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let new = self.build();\n        let mut old = std::mem::replace(state, new);\n        old.insert_before_this(state);\n        old.unmount();\n    }\n}\n\nimpl<const TRANSITION: bool, Fal, Chil> AddAnyAttr\n    for SuspenseBoundary<TRANSITION, Fal, Chil>\nwhere\n    Fal: RenderHtml + Send + 'static,\n    Chil: RenderHtml + Send + 'static,\n{\n    type Output<SomeNewAttr: Attribute> = SuspenseBoundary<\n        TRANSITION,\n        Fal,\n        Chil::Output<SomeNewAttr::CloneableOwned>,\n    >;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let attr = attr.into_cloneable_owned();\n        let SuspenseBoundary {\n            id,\n            none_pending,\n            fallback,\n            children,\n            error_boundary_parent,\n            has_tasks,\n        } = self;\n        SuspenseBoundary {\n            id,\n            none_pending,\n            fallback,\n            children: children.add_any_attr(attr),\n            error_boundary_parent,\n            has_tasks,\n        }\n    }\n}\n\nimpl<const TRANSITION: bool, Fal, Chil> RenderHtml\n    for SuspenseBoundary<TRANSITION, Fal, Chil>\nwhere\n    Fal: RenderHtml + Send + 'static,\n    Chil: RenderHtml + Send + 'static,\n{\n    // i.e., if this is the child of another Suspense during SSR, don't wait for it: it will handle\n    // itself\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = Chil::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        self.fallback.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        mut self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        buf.next_id();\n        let suspense_context = use_context::<SuspenseContext>().unwrap();\n        let owner = Owner::current().unwrap();\n\n        let mut notify_error_boundary =\n            self.error_boundary_parent.map(|children| {\n                let (tx, rx) = oneshot::channel();\n                children.write_value().push(rx);\n                tx\n            });\n\n        // we need to wait for one of two things: either\n        // 1. all tasks are finished loading, or\n        // 2. we read from a local resource, meaning this Suspense can never resolve on the server\n\n        // first, create listener for tasks\n        let tasks = suspense_context.tasks.clone();\n        let (tasks_tx, mut tasks_rx) =\n            futures::channel::oneshot::channel::<()>();\n\n        let mut tasks_tx = Some(tasks_tx);\n\n        // now, create listener for local resources\n        let (local_tx, mut local_rx) =\n            futures::channel::oneshot::channel::<()>();\n        provide_context(LocalResourceNotifier::from(local_tx));\n\n        // walk over the tree of children once to make sure that all resource loads are registered\n        self.children.dry_resolve();\n        let children = Arc::new(Mutex::new(Some(self.children)));\n\n        // check the set of tasks to see if it is empty, now or later\n        let eff = reactive_graph::effect::Effect::new_isomorphic({\n            let children = Arc::clone(&children);\n            move |double_checking: Option<bool>| {\n                // on the first run, always track the tasks\n                if double_checking.is_none() {\n                    tasks.track();\n                }\n\n                if let Some(curr_tasks) = tasks.try_read_untracked() {\n                    if curr_tasks.is_empty() {\n                        if double_checking == Some(true) {\n                            // we have finished loading, and checking the children again told us there are\n                            // no more pending tasks. so we can render both the children and the error boundary\n\n                            if let Some(tx) = tasks_tx.take() {\n                                // If the receiver has dropped, it means the ScopedFuture has already\n                                // dropped, so it doesn't matter if we manage to send this.\n                                _ = tx.send(());\n                            }\n                            if let Some(tx) = notify_error_boundary.take() {\n                                _ = tx.send(());\n                            }\n                        } else {\n                            // release the read guard on tasks, as we'll be updating it again\n                            drop(curr_tasks);\n                            // check the children for additional pending tasks\n                            // the will catch additional resource reads nested inside a conditional depending on initial resource reads\n                            if let Some(children) =\n                                children.lock().or_poisoned().as_mut()\n                            {\n                                children.dry_resolve();\n                            }\n\n                            if tasks\n                                .try_read()\n                                .map(|n| n.is_empty())\n                                .unwrap_or(false)\n                            {\n                                // there are no additional pending tasks, and we can simply return\n                                if let Some(tx) = tasks_tx.take() {\n                                    // If the receiver has dropped, it means the ScopedFuture has already\n                                    // dropped, so it doesn't matter if we manage to send this.\n                                    _ = tx.send(());\n                                }\n                                if let Some(tx) = notify_error_boundary.take() {\n                                    _ = tx.send(());\n                                }\n                            }\n\n                            // tell ourselves that we're just double-checking\n                            return true;\n                        }\n                    } else {\n                        tasks.track();\n                    }\n                }\n                false\n            }\n        });\n\n        let mut fut = Box::pin(ScopedFuture::new(ErrorHookFuture::new(\n            async move {\n                // race the local resource notifier against the set of tasks\n                //\n                // if there are local resources, we just return the fallback immediately\n                //\n                // otherwise, we want to wait for resources to load before trying to resolve the body\n                //\n                // this is *less efficient* than just resolving the body\n                // however, it means that you can use reactive accesses to resources/async derived\n                // inside component props, at any level, and have those picked up by Suspense, and\n                // that it will wait for those to resolve\n                select! {\n                    // if there are local resources, bail\n                    // this will only have fired by this point for local resources accessed\n                    // *synchronously*\n                    _ = local_rx => {\n                        let sc = Owner::current_shared_context().expect(\"no shared context\");\n                        sc.set_incomplete_chunk(self.id);\n                        None\n                    }\n                    _ = tasks_rx => {\n                        let children = {\n                            let mut children_lock = children.lock().or_poisoned();\n                            children_lock.take().expect(\"children should not be removed until we render here\")\n                        };\n\n                        // if we ran this earlier, reactive reads would always be registered as None\n                        // this is fine in the case where we want to use Suspend and .await on some future\n                        // but in situations like a <For each=|| some_resource.snapshot()/> we actually\n                        // want to be able to 1) synchronously read a resource's value, but still 2) wait\n                        // for it to load before we render everything\n                        let mut children = Box::pin(children.resolve().fuse());\n\n                        // we continue racing the children against the \"do we have any local\n                        // resources?\" Future\n                        select! {\n                            _ = local_rx => {\n                                let sc = Owner::current_shared_context().expect(\"no shared context\");\n                                sc.set_incomplete_chunk(self.id);\n                                None\n                            }\n                            children = children => {\n                                // clean up the (now useless) effect\n                                eff.dispose();\n\n                                Some(OwnedView::new_with_owner(children, owner))\n                            }\n                        }\n                    }\n                }\n            },\n        )));\n        match fut.as_mut().now_or_never() {\n            Some(Some(resolved)) => {\n                Either::<Fal, _>::Right(resolved)\n                    .to_html_async_with_buf::<OUT_OF_ORDER>(\n                        buf,\n                        position,\n                        escape,\n                        mark_branches,\n                        extra_attrs,\n                    );\n            }\n            Some(None) => {\n                Either::<_, Chil>::Left(self.fallback)\n                    .to_html_async_with_buf::<OUT_OF_ORDER>(\n                        buf,\n                        position,\n                        escape,\n                        mark_branches,\n                        extra_attrs,\n                    );\n            }\n            None => {\n                let id = buf.clone_id();\n\n                // out-of-order streams immediately push fallback,\n                // wrapped by suspense markers\n                if OUT_OF_ORDER {\n                    let mut fallback_position = *position;\n                    buf.push_fallback(\n                        self.fallback,\n                        &mut fallback_position,\n                        mark_branches,\n                        extra_attrs.clone(),\n                    );\n                    buf.push_async_out_of_order_with_nonce(\n                        fut,\n                        position,\n                        mark_branches,\n                        nonce_or_not(),\n                        extra_attrs,\n                    );\n                } else {\n                    // calling this will walk over the tree, removing all event listeners\n                    // and other single-threaded values from the view tree. this needs to be\n                    // done because the fallback can be shifted to another thread in push_async below.\n                    self.fallback.dry_resolve();\n\n                    buf.push_async({\n                        let mut position = *position;\n                        async move {\n                            let value = match fut.await {\n                                None => Either::Left(self.fallback),\n                                Some(value) => Either::Right(value),\n                            };\n                            let mut builder = StreamBuilder::new(id);\n                            value.to_html_async_with_buf::<OUT_OF_ORDER>(\n                                &mut builder,\n                                &mut position,\n                                escape,\n                                mark_branches,\n                                extra_attrs,\n                            );\n                            builder.finish().take_chunks()\n                        }\n                    });\n                    *position = Position::NextChild;\n                }\n            }\n        };\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let cursor = cursor.to_owned();\n        let position = position.to_owned();\n\n        let mut children = Some(self.children);\n        let mut fallback = Some(self.fallback);\n        let none_pending = self.none_pending;\n        let mut nth_run = 0;\n        let outer_owner = Owner::new();\n\n        RenderEffect::new(move |prev| {\n            // show the fallback if\n            // 1) there are pending futures, and\n            // 2) we are either in a Suspense (not Transition), or it's the first fallback\n            //    (because we initially render the children to register Futures, the \"first\n            //    fallback\" is probably the 2nd run\n            let show_b = !none_pending.get() && (!TRANSITION || nth_run < 1);\n            nth_run += 1;\n            let this = OwnedView::new_with_owner(\n                EitherKeepAlive {\n                    a: children.take(),\n                    b: fallback.take(),\n                    show_b,\n                },\n                outer_owner.clone(),\n            );\n\n            if let Some(mut state) = prev {\n                this.rebuild(&mut state);\n                state\n            } else {\n                this.hydrate::<FROM_SERVER>(&cursor, &position)\n            }\n        })\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\n/// A wrapper that prevents [`Suspense`] from waiting for any resource reads that happen inside\n/// `Unsuspend`.\npub struct Unsuspend<T>(Box<dyn FnOnce() -> T + Send>);\n\nimpl<T> Unsuspend<T> {\n    /// Wraps the given function, such that it is not called until all resources are ready.\n    pub fn new(fun: impl FnOnce() -> T + Send + 'static) -> Self {\n        Self(Box::new(fun))\n    }\n}\n\nimpl<T> Render for Unsuspend<T>\nwhere\n    T: Render,\n{\n    type State = T::State;\n\n    fn build(self) -> Self::State {\n        (self.0)().build()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        (self.0)().rebuild(state);\n    }\n}\n\nimpl<T> AddAnyAttr for Unsuspend<T>\nwhere\n    T: AddAnyAttr + 'static,\n{\n    type Output<SomeNewAttr: Attribute> =\n        Unsuspend<T::Output<SomeNewAttr::CloneableOwned>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let attr = attr.into_cloneable_owned();\n        Unsuspend::new(move || (self.0)().add_any_attr(attr))\n    }\n}\n\nimpl<T> RenderHtml for Unsuspend<T>\nwhere\n    T: RenderHtml + 'static,\n{\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = T::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        (self.0)().to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        (self.0)().to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        (self.0)().hydrate::<FROM_SERVER>(cursor, position)\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n"
  },
  {
    "path": "leptos/src/text_prop.rs",
    "content": "use oco_ref::Oco;\nuse std::sync::Arc;\nuse tachys::prelude::IntoAttributeValue;\n\n/// Describes a value that is either a static or a reactive string, i.e.,\n/// a [`String`], a [`&str`], a `Signal` or a reactive `Fn() -> String`.\n#[derive(Clone)]\npub struct TextProp(Arc<dyn Fn() -> Oco<'static, str> + Send + Sync>);\n\nimpl TextProp {\n    /// Accesses the current value of the property.\n    #[inline(always)]\n    pub fn get(&self) -> Oco<'static, str> {\n        (self.0)()\n    }\n}\n\nimpl core::fmt::Debug for TextProp {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_tuple(\"TextProp\").finish()\n    }\n}\n\nimpl From<String> for TextProp {\n    fn from(s: String) -> Self {\n        let s: Oco<'_, str> = Oco::Counted(Arc::from(s));\n        TextProp(Arc::new(move || s.clone()))\n    }\n}\n\nimpl From<&'static str> for TextProp {\n    fn from(s: &'static str) -> Self {\n        let s: Oco<'_, str> = s.into();\n        TextProp(Arc::new(move || s.clone()))\n    }\n}\n\nimpl From<Arc<str>> for TextProp {\n    fn from(s: Arc<str>) -> Self {\n        let s: Oco<'_, str> = s.into();\n        TextProp(Arc::new(move || s.clone()))\n    }\n}\n\nimpl From<Oco<'static, str>> for TextProp {\n    fn from(s: Oco<'static, str>) -> Self {\n        TextProp(Arc::new(move || s.clone()))\n    }\n}\n\n// TODO\n/*impl<T> From<T> for MaybeProp<TextProp>\nwhere\n    T: Into<Oco<'static, str>>,\n{\n    fn from(s: T) -> Self {\n        Self(Some(MaybeSignal::from(Some(s.into().into()))))\n    }\n}*/\n\nimpl<F, S> From<F> for TextProp\nwhere\n    F: Fn() -> S + 'static + Send + Sync,\n    S: Into<Oco<'static, str>>,\n{\n    #[inline(always)]\n    fn from(s: F) -> Self {\n        TextProp(Arc::new(move || s().into()))\n    }\n}\n\nimpl Default for TextProp {\n    fn default() -> Self {\n        Self(Arc::new(|| Oco::Borrowed(\"\")))\n    }\n}\n\nimpl IntoAttributeValue for TextProp {\n    type Output = Arc<dyn Fn() -> Oco<'static, str> + Send + Sync>;\n\n    fn into_attribute_value(self) -> Self::Output {\n        self.0\n    }\n}\n\n#[allow(unused)]\nmacro_rules! textprop_reactive {\n    ($name:ident, <$($gen:ident),*>, $v:ty, $( $where_clause:tt )*) =>\n    {\n        #[allow(deprecated)]\n        impl<$($gen),*> From<$name<$($gen),*>> for TextProp\n        where\n            $v: Into<Oco<'static, str>>  + Clone + Send + Sync + 'static,\n            $($where_clause)*\n        {\n            #[inline(always)]\n            fn from(s: $name<$($gen),*>) -> Self {\n                TextProp(Arc::new(move || s.get().into()))\n            }\n        }\n    };\n}\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\nmod stable {\n    use super::TextProp;\n    use oco_ref::Oco;\n    #[allow(deprecated)]\n    use reactive_graph::wrappers::read::MaybeSignal;\n    use reactive_graph::{\n        computed::{ArcMemo, Memo},\n        owner::Storage,\n        signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},\n        traits::Get,\n        wrappers::read::{ArcSignal, Signal},\n    };\n    use std::sync::Arc;\n\n    textprop_reactive!(\n        RwSignal,\n        <V, S>,\n        V,\n        RwSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    textprop_reactive!(\n        ReadSignal,\n        <V, S>,\n        V,\n        ReadSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    textprop_reactive!(\n        Memo,\n        <V, S>,\n        V,\n        Memo<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    textprop_reactive!(\n        Signal,\n        <V, S>,\n        V,\n        Signal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    textprop_reactive!(\n        MaybeSignal,\n        <V, S>,\n        V,\n        MaybeSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    textprop_reactive!(ArcRwSignal, <V>, V, ArcRwSignal<V>: Get<Value = V>);\n    textprop_reactive!(ArcReadSignal, <V>, V, ArcReadSignal<V>: Get<Value = V>);\n    textprop_reactive!(ArcMemo, <V>, V, ArcMemo<V>: Get<Value = V>);\n    textprop_reactive!(ArcSignal, <V>, V, ArcSignal<V>: Get<Value = V>);\n}\n\n/// Extension trait for `Option<TextProp>`\npub trait OptionTextPropExt {\n    /// Accesses the current value of the `Option<TextProp>` as an `Option<Oco<'static, str>>`.\n    fn get(&self) -> Option<Oco<'static, str>>;\n}\n\nimpl OptionTextPropExt for Option<TextProp> {\n    fn get(&self) -> Option<Oco<'static, str>> {\n        self.as_ref().map(|text_prop| text_prop.get())\n    }\n}\n"
  },
  {
    "path": "leptos/src/transition.rs",
    "content": "use crate::{\n    children::{TypedChildren, ViewFnOnce},\n    error::ErrorBoundarySuspendedChildren,\n    suspense_component::SuspenseBoundary,\n    IntoView,\n};\nuse leptos_macro::component;\nuse reactive_graph::{\n    computed::{suspense::SuspenseContext, ArcMemo},\n    effect::Effect,\n    owner::{provide_context, use_context, Owner},\n    signal::ArcRwSignal,\n    traits::{Get, Set, Track, With, WithUntracked},\n    wrappers::write::SignalSetter,\n};\nuse slotmap::{DefaultKey, SlotMap};\nuse std::sync::Arc;\nuse tachys::reactive_graph::OwnedView;\n\n/// If any [`Resource`](crate::prelude::Resource) is read in the `children` of this\n/// component, it will show the `fallback` while they are loading. Once all are resolved,\n/// it will render the `children`.\n///\n/// Unlike [`Suspense`](crate::prelude::Suspense), this will not fall\n/// back to the `fallback` state if there are further changes after the initial load.\n///\n/// Note that the `children` will be rendered initially (in order to capture the fact that\n/// those resources are read under the suspense), so you cannot assume that resources read\n/// synchronously have\n/// `Some` value in `children`. However, you can read resources asynchronously by using\n/// [Suspend](crate::prelude::Suspend).\n///\n/// ```\n/// # use leptos::prelude::*;\n/// # if false { // don't run in doctests\n/// async fn fetch_cats(how_many: u32) -> Vec<String> { vec![] }\n///\n/// let (cat_count, set_cat_count) = signal::<u32>(1);\n///\n/// let cats = Resource::new(move || cat_count.get(), |count| fetch_cats(count));\n///\n/// view! {\n///   <div>\n///     <Transition fallback=move || view! { <p>\"Loading (Suspense Fallback)...\"</p> }>\n///       // you can access a resource synchronously\n///       {move || {\n///           cats.get().map(|data| {\n///             data\n///               .into_iter()\n///               .map(|src| {\n///                   view! {\n///                     <img src={src}/>\n///                   }\n///               })\n///               .collect_view()\n///           })\n///         }\n///       }\n///       // or you can use `Suspend` to read resources asynchronously\n///       {move || Suspend::new(async move {\n///         cats.await\n///               .into_iter()\n///               .map(|src| {\n///                   view! {\n///                     <img src={src}/>\n///                   }\n///               })\n///               .collect_view()\n///       })}\n///     </Transition>\n///   </div>\n/// }\n/// # ;}\n/// ```\n#[component]\npub fn Transition<Chil>(\n    /// Will be displayed while resources are pending. By default this is the empty view.\n    #[prop(optional, into)]\n    fallback: ViewFnOnce,\n    /// A function that will be called when the component transitions into or out of\n    /// the `pending` state, with its argument indicating whether it is pending (`true`)\n    /// or not pending (`false`).\n    #[prop(optional, into)]\n    set_pending: Option<SignalSetter<bool>>,\n    children: TypedChildren<Chil>,\n) -> impl IntoView\nwhere\n    Chil: IntoView + Send + 'static,\n{\n    let error_boundary_parent = use_context::<ErrorBoundarySuspendedChildren>();\n\n    let owner = Owner::new();\n    owner.with(|| {\n        let (starts_local, id) = {\n            Owner::current_shared_context()\n                .map(|sc| {\n                    let id = sc.next_id();\n                    (sc.get_incomplete_chunk(&id), id)\n                })\n                .unwrap_or_else(|| (false, Default::default()))\n        };\n        let fallback = fallback.run();\n        let children = children.into_inner()();\n        let tasks = ArcRwSignal::new(SlotMap::<DefaultKey, ()>::new());\n        provide_context(SuspenseContext {\n            tasks: tasks.clone(),\n        });\n        let none_pending = ArcMemo::new({\n            let tasks = tasks.clone();\n            move |prev: Option<&bool>| {\n                tasks.track();\n                if prev.is_none() && starts_local {\n                    false\n                } else {\n                    tasks.with(SlotMap::is_empty)\n                }\n            }\n        });\n        let has_tasks =\n            Arc::new(move || !tasks.with_untracked(SlotMap::is_empty));\n        if let Some(set_pending) = set_pending {\n            Effect::new_isomorphic({\n                let none_pending = none_pending.clone();\n                move |_| {\n                    set_pending.set(!none_pending.get());\n                }\n            });\n        }\n\n        OwnedView::new(SuspenseBoundary::<true, _, _> {\n            id,\n            none_pending,\n            fallback,\n            children,\n            error_boundary_parent,\n            has_tasks,\n        })\n    })\n}\n"
  },
  {
    "path": "leptos/tests/pr_4061.rs",
    "content": "#[cfg(feature = \"ssr\")]\nmod imports {\n    pub use any_spawner::Executor;\n    pub use futures::StreamExt;\n    pub use leptos::prelude::*;\n}\n\n#[cfg(feature = \"ssr\")]\n#[tokio::test]\nasync fn chain_await_resource() {\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let (rs, ws) = signal(0);\n    let source = Resource::new(\n        || (),\n        move |_| async move {\n            #[cfg(feature = \"ssr\")]\n            tokio::time::sleep(std::time::Duration::from_millis(1)).await;\n            1\n        },\n    );\n    let consuming = Resource::new(\n        || (),\n        move |_| async move {\n            let result = source.await;\n            ws.update(|s| *s += 1);\n            result\n        },\n    );\n    let app = view! {\n        <Suspense>{\n            move || {\n                Suspend::new(async move {\n                    consuming.await;\n                    rs.get()\n                })\n            }\n        }</Suspense>\n    };\n\n    assert_eq!(app.to_html_stream_in_order().collect::<String>().await, \"1\");\n}\n\n#[cfg(feature = \"ssr\")]\n#[tokio::test]\nasync fn chain_no_await_resource() {\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let (rs, ws) = signal(0);\n    let source = Resource::new(|| (), move |_| async move { 1 });\n    let consuming = Resource::new(\n        || (),\n        move |_| async move {\n            let result = source.await;\n            ws.update(|s| *s += 1);\n            result\n        },\n    );\n    let app = view! {\n        <Suspense>{\n            move || {\n                Suspend::new(async move {\n                    consuming.await;\n                    rs.get()\n                })\n            }\n        }</Suspense>\n    };\n\n    assert_eq!(app.to_html_stream_in_order().collect::<String>().await, \"1\");\n}\n"
  },
  {
    "path": "leptos/tests/ssr.rs",
    "content": "#[cfg(feature = \"ssr\")]\nuse leptos::html::HtmlElement;\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn simple_ssr_test() {\n    use leptos::prelude::*;\n\n    let (value, set_value) = signal(0);\n    let rendered: View<HtmlElement<_, _, _>> = view! {\n        <div>\n            <button on:click=move |_| set_value.update(|value| *value -= 1)>\"-1\"</button>\n            <span>\"Value: \" {move || value.get().to_string()} \"!\"</span>\n            <button on:click=move |_| set_value.update(|value| *value += 1)>\"+1\"</button>\n        </div>\n    };\n\n    assert_eq!(\n        rendered.to_html(),\n        \"<div><button>-1</button><span>Value: \\\n         <!>0<!>!</span><button>+1</button></div>\"\n    );\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn ssr_test_with_components() {\n    use leptos::prelude::*;\n\n    #[component]\n    fn Counter(initial_value: i32) -> impl IntoView {\n        let (value, set_value) = signal(initial_value);\n        view! {\n            <div>\n                <button on:click=move |_| set_value.update(|value| *value -= 1)>\"-1\"</button>\n                <span>\"Value: \" {move || value.get().to_string()} \"!\"</span>\n                <button on:click=move |_| set_value.update(|value| *value += 1)>\"+1\"</button>\n            </div>\n        }\n    }\n\n    let rendered: View<HtmlElement<_, _, _>> = view! {\n        <div class=\"counters\">\n            <Counter initial_value=1/>\n            <Counter initial_value=2/>\n        </div>\n    };\n\n    assert_eq!(\n        rendered.to_html(),\n        \"<div class=\\\"counters\\\"><div><button>-1</button><span>Value: \\\n         <!>1<!>!</span><button>+1</button></div><div><button>-1</\\\n         button><span>Value: <!>2<!>!</span><button>+1</button></div></div>\"\n    );\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn ssr_test_with_snake_case_components() {\n    use leptos::prelude::*;\n\n    #[component]\n    fn snake_case_counter(initial_value: i32) -> impl IntoView {\n        let (value, set_value) = signal(initial_value);\n        view! {\n            <div>\n                <button on:click=move |_| set_value.update(|value| *value -= 1)>\"-1\"</button>\n                <span>\"Value: \" {move || value.get().to_string()} \"!\"</span>\n                <button on:click=move |_| set_value.update(|value| *value += 1)>\"+1\"</button>\n            </div>\n        }\n    }\n    let rendered: View<HtmlElement<_, _, _>> = view! {\n        <div class=\"counters\">\n            <SnakeCaseCounter initial_value=1/>\n            <SnakeCaseCounter initial_value=2/>\n        </div>\n    };\n\n    assert_eq!(\n        rendered.to_html(),\n        \"<div class=\\\"counters\\\"><div><button>-1</button><span>Value: \\\n         <!>1<!>!</span><button>+1</button></div><div><button>-1</\\\n         button><span>Value: <!>2<!>!</span><button>+1</button></div></div>\"\n    );\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn test_classes() {\n    use leptos::prelude::*;\n\n    let (value, _set_value) = signal(5);\n    let rendered: View<HtmlElement<_, _, _>> = view! {\n        <div\n            class=\"my big\"\n            class:a=move || { value.get() > 10 }\n            class:red=true\n            class:car=move || { value.get() > 1 }\n        ></div>\n    };\n\n    assert_eq!(rendered.to_html(), \"<div class=\\\"my big  red car\\\"></div>\");\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn test_class_with_class_directive_merge() {\n    use leptos::prelude::*;\n\n    // class= followed by class: should merge\n    let rendered: View<HtmlElement<_, _, _>> = view! {\n        <div class=\"foo\" class:bar=true></div>\n    };\n\n    assert_eq!(rendered.to_html(), \"<div class=\\\"foo bar\\\"></div>\");\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn test_solo_class_directive() {\n    use leptos::prelude::*;\n\n    // Solo class: directive should work without class attribute\n    let rendered: View<HtmlElement<_, _, _>> = view! {\n        <div class:foo=true></div>\n    };\n\n    assert_eq!(rendered.to_html(), \"<div class=\\\"foo\\\"></div>\");\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn test_class_directive_with_static_class() {\n    use leptos::prelude::*;\n\n    // class:foo comes after class= due to macro sorting\n    // The class= clears buffer, then class:foo appends\n    let rendered: View<HtmlElement<_, _, _>> = view! {\n        <div class:foo=true class=\"bar\"></div>\n    };\n\n    // After macro sorting: class=\"bar\" class:foo=true\n    // Expected: \"bar foo\"\n    assert_eq!(rendered.to_html(), \"<div class=\\\"bar foo\\\"></div>\");\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn test_global_class_applied() {\n    use leptos::prelude::*;\n\n    // Test that a global class is properly applied\n    let rendered: View<HtmlElement<_, _, _>> = view! { class=\"global\",\n        <div></div>\n    };\n\n    assert_eq!(rendered.to_html(), \"<div class=\\\"global\\\"></div>\");\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn test_multiple_class_attributes_overwrite() {\n    use leptos::prelude::*;\n\n    // When multiple class attributes are applied, the last one should win (browser behavior)\n    // This simulates what happens when attributes are combined programmatically\n    let el = leptos::html::div().class(\"first\").class(\"second\");\n\n    let html = el.to_html();\n\n    // The second class attribute should overwrite the first\n    assert_eq!(html, \"<div class=\\\"second\\\"></div>\");\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn ssr_with_styles() {\n    use leptos::prelude::*;\n\n    let (_, set_value) = signal(0);\n    let styles = \"myclass\";\n    let rendered: View<HtmlElement<_, _, _>> = view! { class=styles,\n        <div>\n            <button class=\"btn\" on:click=move |_| set_value.update(|value| *value -= 1)>\n                \"-1\"\n            </button>\n        </div>\n    };\n\n    assert_eq!(\n        rendered.to_html(),\n        \"<div class=\\\"myclass\\\"><button class=\\\"btn \\\n         myclass\\\">-1</button></div>\"\n    );\n}\n\n#[cfg(feature = \"ssr\")]\n#[test]\nfn ssr_option() {\n    use leptos::prelude::*;\n\n    let (_, _) = signal(0);\n    let rendered: View<HtmlElement<_, _, _>> = view! { <option></option> };\n\n    assert_eq!(rendered.to_html(), \"<option></option>\");\n}\n"
  },
  {
    "path": "leptos_config/Cargo.toml",
    "content": "[package]\nname = \"leptos_config\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Configuration for the Leptos web framework.\"\nreadme = \"../README.md\"\nversion = \"0.8.9\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nconfig = { default-features = false, features = [\n  \"toml\",\n  \"convert-case\",\n], workspace = true }\nregex = { workspace = true, default-features = true }\nserde = { features = [\n  \"derive\",\n  \"rc\",\n], workspace = true, default-features = true }\nthiserror = { workspace = true, default-features = true }\ntyped-builder = { workspace = true, default-features = true }\n\n[dev-dependencies]\ntokio = { features = [\n  \"rt\",\n  \"macros\",\n], workspace = true, default-features = true }\ntempfile = { workspace = true, default-features = true }\ntemp-env = { features = [\n  \"async_closure\",\n], workspace = true, default-features = true }\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(leptos_debuginfo)'] }\n"
  },
  {
    "path": "leptos_config/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "leptos_config/src/errors.rs",
    "content": "use std::{net::AddrParseError, num::ParseIntError, str::ParseBoolError};\nuse thiserror::Error;\n\n#[derive(Debug, Error, Clone)]\npub enum LeptosConfigError {\n    #[error(\"Cargo.toml not found in package root\")]\n    ConfigNotFound,\n    #[error(\"package.metadata.leptos section missing from Cargo.toml\")]\n    ConfigSectionNotFound,\n    #[error(\"Failed to get Leptos Environment. Did you set LEPTOS_ENV?\")]\n    EnvError,\n    #[error(\"Config Error: {0}\")]\n    ConfigError(String),\n    #[error(\"Config Error: {0}\")]\n    EnvVarError(String),\n}\nimpl From<config::ConfigError> for LeptosConfigError {\n    fn from(e: config::ConfigError) -> Self {\n        Self::ConfigError(e.to_string())\n    }\n}\n\nimpl From<ParseIntError> for LeptosConfigError {\n    fn from(e: ParseIntError) -> Self {\n        Self::ConfigError(e.to_string())\n    }\n}\n\nimpl From<AddrParseError> for LeptosConfigError {\n    fn from(e: AddrParseError) -> Self {\n        Self::ConfigError(e.to_string())\n    }\n}\n\nimpl From<ParseBoolError> for LeptosConfigError {\n    fn from(e: ParseBoolError) -> Self {\n        Self::ConfigError(e.to_string())\n    }\n}\n"
  },
  {
    "path": "leptos_config/src/lib.rs",
    "content": "#![forbid(unsafe_code)]\n\npub mod errors;\n\nuse crate::errors::LeptosConfigError;\nuse config::{Case, Config, File, FileFormat};\nuse regex::Regex;\nuse std::{\n    env::VarError,\n    fs,\n    net::SocketAddr,\n    path::{Path, PathBuf},\n    str::FromStr,\n    sync::Arc,\n};\nuse typed_builder::TypedBuilder;\n\n/// A Struct to allow us to parse [`LeptosOptions`] from the file.\n/// Not really needed, most interactions should occur with `LeptosOptions`.\n#[derive(Clone, Debug, serde::Deserialize)]\n#[serde(rename_all = \"kebab-case\")]\n#[non_exhaustive]\npub struct ConfFile {\n    pub leptos_options: LeptosOptions,\n}\n\n/// This struct serves as a convenient place to store details used for configuring Leptos.\n/// It's used in our actix and axum integrations to generate the\n/// correct path for WASM, JS, and Websockets, as well as other configuration tasks.\n/// It shares keys with cargo-leptos, to allow for easy interoperability\n#[derive(TypedBuilder, Debug, Clone, serde::Deserialize)]\n#[serde(rename_all = \"kebab-case\")]\n#[non_exhaustive]\npub struct LeptosOptions {\n    /// The name of the WASM and JS files generated by wasm-bindgen.\n    ///\n    /// This should match the name that will be output when building your application.\n    ///\n    /// You can easily set this using `env!(\"CARGO_CRATE_NAME\")`.\n    #[builder(setter(into))]\n    pub output_name: Arc<str>,\n    /// The path of the all the files generated by cargo-leptos.\n    ///\n    /// This defaults to '.' for convenience when integrating with other tools.\n    #[builder(setter(into), default=default_site_root())]\n    #[serde(default = \"default_site_root\")]\n    pub site_root: Arc<str>,\n    /// The path of the WASM and JS files generated by wasm-bindgen from the root of your app.\n    ///\n    /// By default, wasm-bindgen puts them in `pkg`.\n    #[builder(setter(into), default=default_site_pkg_dir())]\n    #[serde(default = \"default_site_pkg_dir\")]\n    pub site_pkg_dir: Arc<str>,\n    /// Used to configure the running environment of Leptos.\n    /// Can be used to load dev constants and keys v prod,\n    /// or change things based on the deployment environment.\n    ///\n    /// I recommend passing in the result of `env::var(\"LEPTOS_ENV\")`.\n    #[builder(setter(into), default=default_env())]\n    #[serde(default = \"default_env\")]\n    pub env: Env,\n    /// Provides a way to control the address leptos is served from.\n    /// Using an env variable here would allow you to run the same code in dev and prod.\n    ///\n    /// Defaults to `127.0.0.1:3000`.\n    #[builder(setter(into), default=default_site_addr())]\n    #[serde(default = \"default_site_addr\")]\n    pub site_addr: SocketAddr,\n    /// The port the Websocket watcher listens on.\n    /// Should match the `reload_port` in cargo-leptos (if using).\n    ///\n    /// Defaults to `3001`.\n    #[builder(default = default_reload_port())]\n    #[serde(default = \"default_reload_port\")]\n    pub reload_port: u32,\n    /// The port the Websocket watcher listens on when on the client, e.g.,\n    /// when behind a reverse proxy.\n    ///\n    /// Defaults to match `reload_port`.\n    #[builder(default)]\n    #[serde(default)]\n    pub reload_external_port: Option<u32>,\n    /// The protocol the Websocket watcher uses on the client: `ws` in most cases, `wss` when behind\n    /// a reverse https proxy.\n    ///\n    /// Defaults to `ws`\n    #[builder(default)]\n    #[serde(default)]\n    pub reload_ws_protocol: ReloadWSProtocol,\n    /// The path of a custom 404 Not Found page to display when statically serving content.\n    ///\n    /// Defaults to `site_root/404.html`.\n    #[builder(default = default_not_found_path())]\n    #[serde(default = \"default_not_found_path\")]\n    pub not_found_path: Arc<str>,\n    /// The file name of the hash text file generated by cargo-leptos.\n    ///\n    /// Defaults to `hash.txt`.\n    #[builder(default = default_hash_file_name())]\n    #[serde(default = \"default_hash_file_name\")]\n    pub hash_file: Arc<str>,\n    /// If `true`, hashes will be generated for all files\n    /// in the `site_root` and added to their file names.\n    ///\n    /// Defaults to `false`.\n    #[builder(default = default_hash_files())]\n    #[serde(default = \"default_hash_files\")]\n    pub hash_files: bool,\n    /// The default prefix to use for server functions when generating API routes. Can be\n    /// overridden for individual functions using `#[server(prefix = \"...\")]` as usual.\n    ///\n    /// This is useful to override the default prefix (`/api`) for all server functions without\n    /// needing to manually specify via `#[server(prefix = \"...\")]` on every server function.\n    #[builder(default, setter(strip_option))]\n    #[serde(default)]\n    pub server_fn_prefix: Option<String>,\n    /// Whether to disable appending the server functions' hashes to the end of their API names.\n    ///\n    /// This is useful when an app's client side needs a stable server API. For example, shipping\n    /// the CSR WASM binary in a Tauri app. Tauri app releases are dependent on each platform's\n    /// distribution method (e.g., the Apple App Store or the Google Play Store), which typically\n    /// are much slower than the frequency at which a website can be updated. In addition, it's\n    /// common for users to not have the latest app version installed. In these cases, the CSR WASM\n    /// app would need to be able to continue calling the backend server function API, so the API\n    /// path needs to be consistent and not have a hash appended.\n    ///\n    /// Note that the hash suffixes is intended as a way to ensure duplicate API routes are created.\n    /// Without the hash, server functions will need to have unique names to avoid creating\n    /// duplicate routes. Axum will throw an error if a duplicate route is added to the router, but\n    /// Actix will not.\n    #[builder(default)]\n    #[serde(default)]\n    pub disable_server_fn_hash: bool,\n    /// Include the module path of the server function in the API route. This is an alternative\n    /// strategy to prevent duplicate server function API routes (the default strategy is to add\n    /// a hash to the end of the route). Each element of the module path will be separated by a `/`.\n    /// For example, a server function with a fully qualified name of `parent::child::server_fn`\n    /// would have an API route of `/api/parent/child/server_fn` (possibly with a\n    /// different prefix and a hash suffix depending on the values of the other server fn configs).\n    #[builder(default)]\n    #[serde(default)]\n    pub server_fn_mod_path: bool,\n}\n\nimpl LeptosOptions {\n    /// Returns the path on disk to the generated CSS file.  This is done by joining `site_root`,\n    /// `site_pkg_dir`, and the `output_name` with `.css` appended to it.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use leptos_config::LeptosOptions;\n    ///\n    /// let options = LeptosOptions::builder().output_name(\"test\").build();\n    /// let path = options.css_file_path();\n    /// let mut ancestors = path.ancestors();\n    ///\n    /// assert_eq!(path.file_name().unwrap(), \"test.css\");\n    /// ```\n    pub fn css_file_path(&self) -> PathBuf {\n        Path::new(&*self.site_root)\n            .join(&*self.site_pkg_dir)\n            .join(format!(\"{}.css\", self.output_name))\n    }\n\n    /// Returns the path portion of the uri to the generated CSS file.  This is done by combining\n    /// `site_pkg_dir` and the `output_name` with `.css` appended to it.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use leptos_config::LeptosOptions;\n    ///\n    /// let options = LeptosOptions::builder().output_name(\"test\").build();\n    /// let path = options.css_path();\n    /// assert_eq!(path, \"/pkg/test.css\");\n    /// ```\n    pub fn css_path(&self) -> String {\n        let mut path = self.site_pkg_dir_route_base();\n        path.push_str(&self.output_name);\n        path.push_str(\".css\");\n        path\n    }\n\n    /// Returns the base route to the `site_pkg_dir` with a leading and trailing slash added as necessary.\n    pub fn site_pkg_dir_route_base(&self) -> String {\n        let mut path = String::new();\n        // While it shouldn't start with a '/', but check anyway.\n        if !self.site_pkg_dir.starts_with('/') {\n            path.push('/');\n        }\n        path.push_str(&self.site_pkg_dir);\n        if !path.ends_with('/') {\n            path.push('/');\n        }\n        path\n    }\n\n    fn try_from_env() -> Result<Self, LeptosConfigError> {\n        let output_name = env_w_default(\n            \"LEPTOS_OUTPUT_NAME\",\n            std::option_env!(\"LEPTOS_OUTPUT_NAME\",).unwrap_or_default(),\n        )?;\n        if output_name.is_empty() {\n            eprintln!(\n                \"It looks like you're trying to compile Leptos without the \\\n                 LEPTOS_OUTPUT_NAME environment variable being set. There are \\\n                 two options\\n 1. cargo-leptos is not being used, but \\\n                 get_configuration() is being passed None. This needs to be \\\n                 changed to Some(\\\"Cargo.toml\\\")\\n 2. You are compiling \\\n                 Leptos without LEPTOS_OUTPUT_NAME being set with \\\n                 cargo-leptos. This shouldn't be possible!\"\n            );\n        }\n        Ok(LeptosOptions {\n            output_name: output_name.into(),\n            site_root: env_w_default(\"LEPTOS_SITE_ROOT\", \"target/site\")?.into(),\n            site_pkg_dir: env_w_default(\"LEPTOS_SITE_PKG_DIR\", \"pkg\")?.into(),\n            env: env_from_str(env_w_default(\"LEPTOS_ENV\", \"DEV\")?.as_str())?,\n            site_addr: env_w_default(\"LEPTOS_SITE_ADDR\", \"127.0.0.1:3000\")?\n                .parse()?,\n            reload_port: env_w_default(\"LEPTOS_RELOAD_PORT\", \"3001\")?\n                .parse()?,\n            reload_external_port: match env_wo_default(\n                \"LEPTOS_RELOAD_EXTERNAL_PORT\",\n            )? {\n                Some(val) => Some(val.parse()?),\n                None => None,\n            },\n            reload_ws_protocol: ws_from_str(\n                env_w_default(\"LEPTOS_RELOAD_WS_PROTOCOL\", \"ws\")?.as_str(),\n            )?,\n            not_found_path: env_w_default(\"LEPTOS_NOT_FOUND_PATH\", \"/404\")?\n                .into(),\n            hash_file: env_w_default(\"LEPTOS_HASH_FILE_NAME\", \"hash.txt\")?\n                .into(),\n            hash_files: env_w_default(\"LEPTOS_HASH_FILES\", \"false\")?.parse()?,\n            server_fn_prefix: env_wo_default(\"SERVER_FN_PREFIX\")?,\n            disable_server_fn_hash: env_wo_default(\"DISABLE_SERVER_FN_HASH\")?\n                .is_some(),\n            server_fn_mod_path: env_wo_default(\"SERVER_FN_MOD_PATH\")?.is_some(),\n        })\n    }\n}\n\nfn default_site_root() -> Arc<str> {\n    \".\".into()\n}\n\nfn default_site_pkg_dir() -> Arc<str> {\n    \"pkg\".into()\n}\n\nfn default_env() -> Env {\n    Env::DEV\n}\n\nfn default_site_addr() -> SocketAddr {\n    SocketAddr::from(([127, 0, 0, 1], 3000))\n}\n\nfn default_reload_port() -> u32 {\n    3001\n}\n\nfn default_not_found_path() -> Arc<str> {\n    \"/404\".into()\n}\n\nfn default_hash_file_name() -> Arc<str> {\n    \"hash.txt\".into()\n}\n\nfn default_hash_files() -> bool {\n    false\n}\n\nfn env_wo_default(key: &str) -> Result<Option<String>, LeptosConfigError> {\n    match std::env::var(key) {\n        Ok(val) => Ok(Some(val)),\n        Err(VarError::NotPresent) => Ok(None),\n        Err(e) => Err(LeptosConfigError::EnvVarError(format!(\"{key}: {e}\"))),\n    }\n}\nfn env_w_default(\n    key: &str,\n    default: &str,\n) -> Result<String, LeptosConfigError> {\n    match std::env::var(key) {\n        Ok(val) => Ok(val),\n        Err(VarError::NotPresent) => Ok(default.to_string()),\n        Err(e) => Err(LeptosConfigError::EnvVarError(format!(\"{key}: {e}\"))),\n    }\n}\n\npub const ENV_DEV_KEY_SHORT: &str = \"dev\";\npub const ENV_DEV_KEY_LONG: &str = \"development\";\npub const ENV_PROD_KEY_SHORT: &str = \"prod\";\npub const ENV_PROD_KEY_LONG: &str = \"production\";\n\n/// An enum that can be used to define the environment Leptos is running in.\n/// Setting this to the `PROD` variant will not include the WebSocket code for `cargo-leptos` watch mode.\n/// Defaults to `DEV`.\n#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq, Default)]\npub enum Env {\n    PROD,\n    #[default]\n    DEV,\n}\n\nfn env_from_str(input: &str) -> Result<Env, LeptosConfigError> {\n    let sanitized = input.to_lowercase();\n    match sanitized.as_ref() {\n        ENV_DEV_KEY_SHORT | ENV_DEV_KEY_LONG => Ok(Env::DEV),\n        ENV_PROD_KEY_SHORT | ENV_PROD_KEY_LONG => Ok(Env::PROD),\n        _ => Err(LeptosConfigError::EnvVarError(format!(\n            \"{input} is not a supported environment. Use either \\\n             `{ENV_DEV_KEY_SHORT}`, `{ENV_DEV_KEY_LONG}`, \\\n             `{ENV_PROD_KEY_SHORT}`, or `{ENV_PROD_KEY_LONG}`.\",\n        ))),\n    }\n}\n\nimpl FromStr for Env {\n    type Err = ();\n    fn from_str(input: &str) -> Result<Self, Self::Err> {\n        env_from_str(input).or_else(|_| Ok(Self::default()))\n    }\n}\n\nimpl From<&str> for Env {\n    fn from(str: &str) -> Self {\n        env_from_str(str).unwrap_or_else(|err| panic!(\"{}\", err))\n    }\n}\n\nimpl From<&Result<String, VarError>> for Env {\n    fn from(input: &Result<String, VarError>) -> Self {\n        match input {\n            Ok(str) => {\n                env_from_str(str).unwrap_or_else(|err| panic!(\"{}\", err))\n            }\n            Err(_) => Self::default(),\n        }\n    }\n}\n\nimpl TryFrom<String> for Env {\n    type Error = LeptosConfigError;\n\n    fn try_from(s: String) -> Result<Self, Self::Error> {\n        env_from_str(s.as_str())\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]\nstruct EnvVisitor;\n\nimpl<'de> serde::de::Visitor<'de> for EnvVisitor {\n    type Value = Env;\n\n    fn expecting(\n        &self,\n        formatter: &mut std::fmt::Formatter,\n    ) -> std::fmt::Result {\n        write!(\n            formatter,\n            \"a case-insensitive string of either `{ENV_DEV_KEY_SHORT}`, \\\n             `{ENV_DEV_KEY_LONG}`, `{ENV_PROD_KEY_SHORT}`, or \\\n             `{ENV_PROD_KEY_LONG}`\"\n        )\n    }\n\n    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>\n    where\n        E: serde::de::Error,\n    {\n        env_from_str(v).map_err(|err| E::custom(err.to_string()))\n    }\n}\n\nimpl<'de> serde::Deserialize<'de> for Env {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        deserializer.deserialize_any(EnvVisitor)\n    }\n}\n\n/// An enum that can be used to define the websocket protocol Leptos uses for hotreloading\n/// Defaults to `ws`.\n#[derive(\n    Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Default,\n)]\npub enum ReloadWSProtocol {\n    #[default]\n    WS,\n    WSS,\n}\n\nfn ws_from_str(input: &str) -> Result<ReloadWSProtocol, LeptosConfigError> {\n    let sanitized = input.to_lowercase();\n    match sanitized.as_ref() {\n        \"ws\" | \"WS\" => Ok(ReloadWSProtocol::WS),\n        \"wss\" | \"WSS\" => Ok(ReloadWSProtocol::WSS),\n        _ => Err(LeptosConfigError::EnvVarError(format!(\n            \"{input} is not a supported websocket protocol. Use only `ws` or \\\n             `wss`.\",\n        ))),\n    }\n}\n\nimpl FromStr for ReloadWSProtocol {\n    type Err = ();\n    fn from_str(input: &str) -> Result<Self, Self::Err> {\n        ws_from_str(input).or_else(|_| Ok(Self::default()))\n    }\n}\n\nimpl From<&str> for ReloadWSProtocol {\n    fn from(str: &str) -> Self {\n        ws_from_str(str).unwrap_or_else(|err| panic!(\"{}\", err))\n    }\n}\n\nimpl From<&Result<String, VarError>> for ReloadWSProtocol {\n    fn from(input: &Result<String, VarError>) -> Self {\n        match input {\n            Ok(str) => ws_from_str(str).unwrap_or_else(|err| panic!(\"{}\", err)),\n            Err(_) => Self::default(),\n        }\n    }\n}\n\nimpl TryFrom<String> for ReloadWSProtocol {\n    type Error = LeptosConfigError;\n\n    fn try_from(s: String) -> Result<Self, Self::Error> {\n        ws_from_str(s.as_str())\n    }\n}\n\n/// Loads [`LeptosOptions`] from a Cargo.toml text content with layered overrides.\n/// If an env var is specified, like `LEPTOS_ENV`, it will override a setting in the file.\n///\n/// # Errors\n///\n/// This function will return an error if either:\n/// - config section was not found;\n/// - failed to deserialize the config section.\npub fn get_config_from_str(\n    text: &str,\n) -> Result<LeptosOptions, LeptosConfigError> {\n    let re: Regex = Regex::new(r\"(?m)^\\[package.metadata.leptos\\]\").unwrap();\n    let re_workspace: Regex =\n        Regex::new(r\"(?m)^\\[\\[workspace.metadata.leptos\\]\\]\").unwrap();\n\n    let metadata_name;\n    let start;\n    match re.find(text) {\n        Some(found) => {\n            metadata_name = \"[package.metadata.leptos]\";\n            start = found.start();\n        }\n        None => match re_workspace.find(text) {\n            Some(found) => {\n                metadata_name = \"[[workspace.metadata.leptos]]\";\n                start = found.start();\n            }\n            None => return Err(LeptosConfigError::ConfigSectionNotFound),\n        },\n    };\n\n    // so that serde error messages have right line number\n    let newlines = text[..start].matches('\\n').count();\n    let input = \"\\n\".repeat(newlines) + &text[start..];\n    // so the settings will be interpreted as root level settings\n    let toml = input.replace(metadata_name, \"\");\n    let settings = Config::builder()\n        // Read the \"default\" configuration file\n        .add_source(File::from_str(&toml, FileFormat::Toml))\n        // Layer on the environment-specific values.\n        // Add in settings from environment variables (with a prefix of LEPTOS)\n        // E.g. `LEPTOS_RELOAD_PORT=5001 would set `LeptosOptions.reload_port`\n        .add_source(\n            config::Environment::with_prefix(\"LEPTOS\")\n                .convert_case(Case::Kebab),\n        )\n        .build()?;\n\n    settings\n        .try_deserialize()\n        .map_err(|e| LeptosConfigError::ConfigError(e.to_string()))\n}\n\n/// Loads [`LeptosOptions`] from a Cargo.toml with layered overrides. If an env var is specified, like `LEPTOS_ENV`,\n/// it will override a setting in the file. It takes in an optional path to a Cargo.toml file. If None is provided,\n/// you'll need to set the options as environment variables or rely on the defaults. This is the preferred\n/// approach for cargo-leptos. If Some(\"./Cargo.toml\") is provided, Leptos will read in the settings itself. This\n/// option currently does not allow dashes in file or folder names, as all dashes become underscores\n///\n/// # Errors\n///\n/// See [`get_config_from_file`] & [`get_config_from_env`]\npub fn get_configuration(\n    path: Option<&str>,\n) -> Result<ConfFile, LeptosConfigError> {\n    if let Some(path) = path {\n        get_config_from_file(path)\n    } else {\n        get_config_from_env()\n    }\n}\n\n/// Loads [`LeptosOptions`] from a `Cargo.toml` with layered overrides.\n/// Leptos will read in the settings itself.\n///\n/// This option currently does not allow dashes in file or folder names, as all dashes become underscores.\n///\n/// # Errors\n///\n/// This function will return an error if either:\n/// - path was not found, or other I/O error has occured;\n/// - [`get_config_from_str`] returned an error.\npub fn get_config_from_file<P: AsRef<Path>>(\n    path: P,\n) -> Result<ConfFile, LeptosConfigError> {\n    let text = fs::read_to_string(path)\n        .map_err(|_| LeptosConfigError::ConfigNotFound)?;\n    let leptos_options = get_config_from_str(&text)?;\n    Ok(ConfFile { leptos_options })\n}\n\n/// Loads [`LeptosOptions`] from environment variables or rely on the defaults.\n///\n/// # Errors\n///\n/// This function will return an error if it failed to load or parse a relevant environment variable.\npub fn get_config_from_env() -> Result<ConfFile, LeptosConfigError> {\n    Ok(ConfFile {\n        leptos_options: LeptosOptions::try_from_env()?,\n    })\n}\n\n#[path = \"tests.rs\"]\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "leptos_config/src/tests.rs",
    "content": "use crate::{\n    env_from_str, env_w_default, env_wo_default, ws_from_str, Env,\n    LeptosOptions, ReloadWSProtocol,\n};\nuse std::{net::SocketAddr, path::Ancestors, str::FromStr};\n\n#[test]\nfn env_from_str_test() {\n    assert!(matches!(env_from_str(\"dev\").unwrap(), Env::DEV));\n    assert!(matches!(env_from_str(\"development\").unwrap(), Env::DEV));\n    assert!(matches!(env_from_str(\"DEV\").unwrap(), Env::DEV));\n    assert!(matches!(env_from_str(\"DEVELOPMENT\").unwrap(), Env::DEV));\n    assert!(matches!(env_from_str(\"prod\").unwrap(), Env::PROD));\n    assert!(matches!(env_from_str(\"production\").unwrap(), Env::PROD));\n    assert!(matches!(env_from_str(\"PROD\").unwrap(), Env::PROD));\n    assert!(matches!(env_from_str(\"PRODUCTION\").unwrap(), Env::PROD));\n    assert!(env_from_str(\"TEST\").is_err());\n    assert!(env_from_str(\"?\").is_err());\n}\n\n#[test]\nfn ws_from_str_test() {\n    assert!(matches!(ws_from_str(\"ws\").unwrap(), ReloadWSProtocol::WS));\n    assert!(matches!(ws_from_str(\"WS\").unwrap(), ReloadWSProtocol::WS));\n    assert!(matches!(ws_from_str(\"wss\").unwrap(), ReloadWSProtocol::WSS));\n    assert!(matches!(ws_from_str(\"WSS\").unwrap(), ReloadWSProtocol::WSS));\n    assert!(ws_from_str(\"TEST\").is_err());\n    assert!(ws_from_str(\"?\").is_err());\n}\n\n#[test]\nfn env_w_default_test() {\n    temp_env::with_var(\"LEPTOS_CONFIG_ENV_TEST\", Some(\"custom\"), || {\n        assert_eq!(\n            env_w_default(\"LEPTOS_CONFIG_ENV_TEST\", \"default\").unwrap(),\n            String::from(\"custom\")\n        );\n    });\n\n    temp_env::with_var_unset(\"LEPTOS_CONFIG_ENV_TEST\", || {\n        assert_eq!(\n            env_w_default(\"LEPTOS_CONFIG_ENV_TEST\", \"default\").unwrap(),\n            String::from(\"default\")\n        );\n    });\n}\n\n#[test]\nfn env_wo_default_test() {\n    temp_env::with_var(\"LEPTOS_CONFIG_ENV_TEST\", Some(\"custom\"), || {\n        assert_eq!(\n            env_wo_default(\"LEPTOS_CONFIG_ENV_TEST\").unwrap(),\n            Some(String::from(\"custom\"))\n        );\n    });\n\n    temp_env::with_var_unset(\"LEPTOS_CONFIG_ENV_TEST\", || {\n        assert_eq!(env_wo_default(\"LEPTOS_CONFIG_ENV_TEST\").unwrap(), None);\n    });\n}\n\n#[test]\nfn try_from_env_test() {\n    // Test config values from environment variables\n    let config = temp_env::with_vars(\n        [\n            (\"LEPTOS_OUTPUT_NAME\", Some(\"app_test\")),\n            (\"LEPTOS_SITE_ROOT\", Some(\"my_target/site\")),\n            (\"LEPTOS_SITE_PKG_DIR\", Some(\"my_pkg\")),\n            (\"LEPTOS_SITE_ADDR\", Some(\"0.0.0.0:80\")),\n            (\"LEPTOS_RELOAD_PORT\", Some(\"8080\")),\n            (\"LEPTOS_RELOAD_EXTERNAL_PORT\", Some(\"8080\")),\n            (\"LEPTOS_ENV\", Some(\"PROD\")),\n            (\"LEPTOS_RELOAD_WS_PROTOCOL\", Some(\"WSS\")),\n        ],\n        || LeptosOptions::try_from_env().unwrap(),\n    );\n\n    assert_eq!(config.output_name.as_ref(), \"app_test\");\n    assert_eq!(config.site_root.as_ref(), \"my_target/site\");\n    assert_eq!(config.site_pkg_dir.as_ref(), \"my_pkg\");\n    assert_eq!(\n        config.site_addr,\n        SocketAddr::from_str(\"0.0.0.0:80\").unwrap()\n    );\n    assert_eq!(config.reload_port, 8080);\n    assert_eq!(config.reload_external_port, Some(8080));\n    assert_eq!(config.env, Env::PROD);\n    assert_eq!(config.reload_ws_protocol, ReloadWSProtocol::WSS)\n}\n\n#[test]\nfn leptos_options_css_file_path() {\n    fn next_file_name<'a>(a: &'a mut Ancestors) -> Option<&'a str> {\n        a.next()\n            .map(|p| p.file_name())\n            .flatten()\n            .map(|s| s.to_str())\n            .flatten()\n    }\n    let options = LeptosOptions::builder().output_name(\"test\").build();\n    let path = options.css_file_path();\n    let mut ancestors = path.ancestors();\n    assert_eq!(next_file_name(&mut ancestors), Some(\"test.css\"));\n    assert_eq!(next_file_name(&mut ancestors), Some(\"pkg\"));\n    assert_eq!(next_file_name(&mut ancestors), None);\n\n    let options = LeptosOptions::builder()\n        .output_name(\"test\")\n        .site_pkg_dir(\"\")\n        .build();\n    let path = options.css_file_path();\n    let mut ancestors = path.ancestors();\n    assert_eq!(next_file_name(&mut ancestors), Some(\"test.css\"));\n    assert_eq!(next_file_name(&mut ancestors), None);\n\n    let options = LeptosOptions::builder()\n        .output_name(\"test\")\n        .site_pkg_dir(\"my_pkg\")\n        .site_root(\"my_site\")\n        .build();\n    let path = options.css_file_path();\n    let mut ancestors = path.ancestors();\n    assert_eq!(next_file_name(&mut ancestors), Some(\"test.css\"));\n    assert_eq!(next_file_name(&mut ancestors), Some(\"my_pkg\"));\n    assert_eq!(next_file_name(&mut ancestors), Some(\"my_site\"));\n    assert_eq!(next_file_name(&mut ancestors), None);\n}\n\n#[test]\nfn leptos_options_css_path() {\n    let options = LeptosOptions::builder().output_name(\"test\").build();\n    assert_eq!(options.css_path(), \"/pkg/test.css\");\n\n    let options = LeptosOptions::builder()\n        .output_name(\"test.css\")\n        .site_pkg_dir(\"my/pkg\")\n        .build();\n    assert_eq!(options.css_path(), \"/my/pkg/test.css.css\");\n\n    let options = LeptosOptions::builder()\n        .output_name(\"test\")\n        .site_pkg_dir(\"/pkg/\")\n        .build();\n    assert_eq!(options.css_path(), \"/pkg/test.css\");\n\n    let options = LeptosOptions::builder()\n        .output_name(\"test\")\n        .site_pkg_dir(\"\")\n        .build();\n    assert_eq!(options.css_path(), \"/test.css\");\n}\n"
  },
  {
    "path": "leptos_config/tests/config.rs",
    "content": "use leptos_config::{\n    get_config_from_env, get_config_from_file, get_config_from_str,\n    get_configuration, Env, LeptosOptions,\n};\nuse std::{fs::File, io::Write, net::SocketAddr, path::Path, str::FromStr};\nuse tempfile::NamedTempFile;\n\n#[test]\nfn env_default() {\n    assert!(matches!(Env::default(), Env::DEV));\n}\n\nconst CARGO_TOML_CONTENT_OK: &str = r#\"\\\n[package.metadata.leptos]\noutput-name = \"app-test\"\nsite-root = \"my_target/site\"\nsite-pkg-dir = \"my_pkg\"\nsite-addr = \"0.0.0.0:80\"\nreload-port = \"8080\"\nreload-external-port = \"8080\"\nenv = \"PROD\"\n\"#;\n\nconst CARGO_TOML_CONTENT_ERR: &str = r#\"\\\n[package.metadata.leptos]\n- invalid toml -\n\"#;\n\n#[tokio::test]\nasync fn get_configuration_from_file_ok() {\n    let cargo_tmp = NamedTempFile::new().unwrap();\n    {\n        let mut output = File::create(&cargo_tmp).unwrap();\n        write!(output, \"{CARGO_TOML_CONTENT_OK}\").unwrap();\n    }\n\n    let path: &Path = cargo_tmp.as_ref();\n    let path_s = path.to_string_lossy().to_string();\n\n    let config = temp_env::async_with_vars(\n        [\n            (\"LEPTOS_OUTPUT_NAME\", None::<&str>),\n            (\"LEPTOS_SITE_ROOT\", None::<&str>),\n            (\"LEPTOS_SITE_PKG_DIR\", None::<&str>),\n            (\"LEPTOS_SITE_ADDR\", None::<&str>),\n            (\"LEPTOS_RELOAD_PORT\", None::<&str>),\n            (\"LEPTOS_RELOAD_EXTERNAL_PORT\", None::<&str>),\n        ],\n        async { get_configuration(Some(&path_s)).unwrap().leptos_options },\n    )\n    .await;\n\n    assert_eq!(config.output_name.as_ref(), \"app-test\");\n    assert_eq!(config.site_root.as_ref(), \"my_target/site\");\n    assert_eq!(config.site_pkg_dir.as_ref(), \"my_pkg\");\n    assert_eq!(\n        config.site_addr,\n        SocketAddr::from_str(\"0.0.0.0:80\").unwrap()\n    );\n    assert_eq!(config.reload_port, 8080);\n    assert_eq!(config.reload_external_port, Some(8080));\n}\n\n#[tokio::test]\nasync fn get_configuration_from_invalid_file() {\n    let cargo_tmp = NamedTempFile::new().unwrap();\n    {\n        let mut output = File::create(&cargo_tmp).unwrap();\n        write!(output, \"{CARGO_TOML_CONTENT_ERR}\").unwrap();\n    }\n    let path: &Path = cargo_tmp.as_ref();\n    let path_s = path.to_string_lossy().to_string();\n    assert!(get_configuration(Some(&path_s)).is_err());\n}\n\n#[tokio::test]\nasync fn get_configuration_from_empty_file() {\n    let cargo_tmp = NamedTempFile::new().unwrap();\n    {\n        let mut output = File::create(&cargo_tmp).unwrap();\n        write!(output, \"\").unwrap();\n    }\n    let path: &Path = cargo_tmp.as_ref();\n    let path_s = path.to_string_lossy().to_string();\n    assert!(get_configuration(Some(&path_s)).is_err());\n}\n\n#[tokio::test]\nasync fn get_config_from_file_ok() {\n    let cargo_tmp = NamedTempFile::new().unwrap();\n    {\n        let mut output = File::create(&cargo_tmp).unwrap();\n        write!(output, \"{CARGO_TOML_CONTENT_OK}\").unwrap();\n    }\n\n    let config = temp_env::async_with_vars(\n        [\n            (\"LEPTOS_OUTPUT_NAME\", None::<&str>),\n            (\"LEPTOS_SITE_ROOT\", None::<&str>),\n            (\"LEPTOS_SITE_PKG_DIR\", None::<&str>),\n            (\"LEPTOS_SITE_ADDR\", None::<&str>),\n            (\"LEPTOS_RELOAD_PORT\", None::<&str>),\n            (\"LEPTOS_RELOAD_EXTERNAL_PORT\", None::<&str>),\n        ],\n        async { get_config_from_file(&cargo_tmp).unwrap().leptos_options },\n    )\n    .await;\n\n    assert_eq!(config.output_name.as_ref(), \"app-test\");\n    assert_eq!(config.site_root.as_ref(), \"my_target/site\");\n    assert_eq!(config.site_pkg_dir.as_ref(), \"my_pkg\");\n    assert_eq!(\n        config.site_addr,\n        SocketAddr::from_str(\"0.0.0.0:80\").unwrap()\n    );\n    assert_eq!(config.reload_port, 8080);\n    assert_eq!(config.reload_external_port, Some(8080));\n}\n\n#[tokio::test]\nasync fn get_config_from_file_invalid() {\n    let cargo_tmp = NamedTempFile::new().unwrap();\n    {\n        let mut output = File::create(&cargo_tmp).unwrap();\n        write!(output, \"{CARGO_TOML_CONTENT_ERR}\").unwrap();\n    }\n    assert!(get_config_from_file(&cargo_tmp).is_err());\n}\n\n#[tokio::test]\nasync fn get_config_from_file_empty() {\n    let cargo_tmp = NamedTempFile::new().unwrap();\n    {\n        let mut output = File::create(&cargo_tmp).unwrap();\n        write!(output, \"\").unwrap();\n    }\n    assert!(get_config_from_file(&cargo_tmp).is_err());\n}\n\n#[test]\nfn get_config_from_str_content() {\n    let config = temp_env::with_vars_unset(\n        [\n            \"LEPTOS_OUTPUT_NAME\",\n            \"LEPTOS_SITE_ROOT\",\n            \"LEPTOS_SITE_PKG_DIR\",\n            \"LEPTOS_SITE_ADDR\",\n            \"LEPTOS_RELOAD_PORT\",\n            \"LEPTOS_RELOAD_EXTERNAL_PORT\",\n        ],\n        || get_config_from_str(CARGO_TOML_CONTENT_OK).unwrap(),\n    );\n\n    assert_eq!(config.output_name.as_ref(), \"app-test\");\n    assert_eq!(config.site_root.as_ref(), \"my_target/site\");\n    assert_eq!(config.site_pkg_dir.as_ref(), \"my_pkg\");\n    assert_eq!(\n        config.site_addr,\n        SocketAddr::from_str(\"0.0.0.0:80\").unwrap()\n    );\n    assert_eq!(config.reload_port, 8080);\n    assert_eq!(config.reload_external_port, Some(8080));\n}\n\n#[tokio::test]\nasync fn get_configuration_from_env() {\n    // Test config values from environment variables\n    let config = temp_env::async_with_vars(\n        [\n            (\"LEPTOS_OUTPUT_NAME\", Some(\"app-test\")),\n            (\"LEPTOS_SITE_ROOT\", Some(\"my_target/site\")),\n            (\"LEPTOS_SITE_PKG_DIR\", Some(\"my_pkg\")),\n            (\"LEPTOS_SITE_ADDR\", Some(\"0.0.0.0:80\")),\n            (\"LEPTOS_RELOAD_PORT\", Some(\"8080\")),\n            (\"LEPTOS_RELOAD_EXTERNAL_PORT\", Some(\"8080\")),\n        ],\n        async { get_configuration(None).unwrap().leptos_options },\n    )\n    .await;\n\n    assert_eq!(config.output_name.as_ref(), \"app-test\");\n    assert_eq!(config.site_root.as_ref(), \"my_target/site\");\n    assert_eq!(config.site_pkg_dir.as_ref(), \"my_pkg\");\n    assert_eq!(\n        config.site_addr,\n        SocketAddr::from_str(\"0.0.0.0:80\").unwrap()\n    );\n    assert_eq!(config.reload_port, 8080);\n    assert_eq!(config.reload_external_port, Some(8080));\n\n    // Test default config values\n    let config = temp_env::async_with_vars(\n        [\n            (\"LEPTOS_OUTPUT_NAME\", None::<&str>),\n            (\"LEPTOS_SITE_ROOT\", None::<&str>),\n            (\"LEPTOS_SITE_PKG_DIR\", None::<&str>),\n            (\"LEPTOS_SITE_ADDR\", None::<&str>),\n            (\"LEPTOS_RELOAD_PORT\", None::<&str>),\n            (\"LEPTOS_RELOAD_EXTERNAL_PORT\", None::<&str>),\n        ],\n        async { get_configuration(None).unwrap().leptos_options },\n    )\n    .await;\n\n    assert_eq!(config.site_root.as_ref(), \"target/site\");\n    assert_eq!(config.site_pkg_dir.as_ref(), \"pkg\");\n    assert_eq!(\n        config.site_addr,\n        SocketAddr::from_str(\"127.0.0.1:3000\").unwrap()\n    );\n    assert_eq!(config.reload_port, 3001);\n    assert_eq!(config.reload_external_port, None);\n}\n\n#[test]\nfn leptos_options_builder_default() {\n    let conf = LeptosOptions::builder().output_name(\"app-test\").build();\n    assert_eq!(conf.output_name.as_ref(), \"app-test\");\n    assert!(matches!(conf.env, Env::DEV));\n    assert_eq!(conf.site_pkg_dir.as_ref(), \"pkg\");\n    assert_eq!(conf.site_root.as_ref(), \".\");\n    assert_eq!(\n        conf.site_addr,\n        SocketAddr::from_str(\"127.0.0.1:3000\").unwrap()\n    );\n    assert_eq!(conf.reload_port, 3001);\n    assert_eq!(conf.reload_external_port, None);\n}\n\n#[test]\nfn environment_variable_override() {\n    // first check without variables set\n    let config = temp_env::with_vars_unset(\n        [\n            \"LEPTOS_OUTPUT_NAME\",\n            \"LEPTOS_SITE_ROOT\",\n            \"LEPTOS_SITE_PKG_DIR\",\n            \"LEPTOS_SITE_ADDR\",\n            \"LEPTOS_RELOAD_PORT\",\n            \"LEPTOS_RELOAD_EXTERNAL_PORT\",\n        ],\n        || get_config_from_str(CARGO_TOML_CONTENT_OK).unwrap(),\n    );\n\n    assert_eq!(config.output_name.as_ref(), \"app-test\");\n    assert_eq!(config.site_root.as_ref(), \"my_target/site\");\n    assert_eq!(config.site_pkg_dir.as_ref(), \"my_pkg\");\n    assert_eq!(\n        config.site_addr,\n        SocketAddr::from_str(\"0.0.0.0:80\").unwrap()\n    );\n    assert_eq!(config.reload_port, 8080);\n    assert_eq!(config.reload_external_port, Some(8080));\n\n    // check the override\n    let config = temp_env::with_vars(\n        [\n            (\"LEPTOS_OUTPUT_NAME\", Some(\"app-test2\")),\n            (\"LEPTOS_SITE_ROOT\", Some(\"my_target/site2\")),\n            (\"LEPTOS_SITE_PKG_DIR\", Some(\"my_pkg2\")),\n            (\"LEPTOS_SITE_ADDR\", Some(\"0.0.0.0:82\")),\n            (\"LEPTOS_RELOAD_PORT\", Some(\"8082\")),\n            (\"LEPTOS_RELOAD_EXTERNAL_PORT\", Some(\"8082\")),\n        ],\n        || get_config_from_str(CARGO_TOML_CONTENT_OK).unwrap(),\n    );\n\n    assert_eq!(config.output_name.as_ref(), \"app-test2\");\n    assert_eq!(config.site_root.as_ref(), \"my_target/site2\");\n    assert_eq!(config.site_pkg_dir.as_ref(), \"my_pkg2\");\n    assert_eq!(\n        config.site_addr,\n        SocketAddr::from_str(\"0.0.0.0:82\").unwrap()\n    );\n    assert_eq!(config.reload_port, 8082);\n    assert_eq!(config.reload_external_port, Some(8082));\n}\n\n// See https://github.com/leptos-rs/leptos/issues/4511\n#[test]\nfn env_consistent_deserialization() {\n    let env_value = \"PrOdUcTiOn\";\n\n    let cargo_tmp = NamedTempFile::new().unwrap();\n    {\n        let mut output = File::create(&cargo_tmp).unwrap();\n        write!(\n            output,\n            r#\"\n[package.metadata.leptos]\noutput-name = \"app-test\"\nenv = \"{env_value}\"\n            \"#\n        )\n        .unwrap();\n    }\n\n    let path: &Path = cargo_tmp.as_ref();\n    let path_s = path.to_string_lossy().to_string();\n\n    let config_from_file =\n        get_config_from_file(&path_s).unwrap().leptos_options;\n\n    let config_from_env =\n        temp_env::with_vars([(\"LEPTOS_ENV\", Some(env_value))], || {\n            get_config_from_env().unwrap().leptos_options\n        });\n\n    assert_eq!(config_from_file.env, config_from_env.env);\n}\n"
  },
  {
    "path": "leptos_dom/.cargo/config.toml",
    "content": "# [build]\n# target = \"wasm32-unknown-unknown\""
  },
  {
    "path": "leptos_dom/Cargo.toml",
    "content": "[package]\nname = \"leptos_dom\"\nversion = \"0.8.8\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"DOM operations for the Leptos web framework.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\ntachys = { workspace = true }\nreactive_graph = { workspace = true }\nor_poisoned = { workspace = true }\njs-sys = { workspace = true, default-features = true }\nsend_wrapper = { workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\nwasm-bindgen = { workspace = true, default-features = true }\nserde_json = { optional = true, workspace = true, default-features = true }\nserde = { optional = true, workspace = true, default-features = true }\n\n[dev-dependencies]\nleptos = { path = \"../leptos\" }\n\n[dependencies.web-sys]\nfeatures = [\"Location\"]\nworkspace = true\ndefault-features = true\n\n[features]\ndefault = []\ntracing = [\"dep:tracing\"]\ntrace-component-props = [\"dep:serde\", \"dep:serde_json\"]\nhydration = [\"reactive_graph/hydration\"]\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\"]\nmax_combination_size = 2\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(leptos_debuginfo)'] }\n"
  },
  {
    "path": "leptos_dom/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "leptos_dom/examples/hydration-test/Cargo.toml",
    "content": "[package]\nname = \"hydration-test\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nleptos = { path = \"../../../leptos\", features = [\"nightly\"] }\nactix-web = { version = \"4\", optional = true }\nactix-files = { version = \"0.6.0\", optional = true }\nwasm-bindgen = { version = \"0.2.0\", optional = true }\ngloo = { version = \"0.11.0\", optional = true }\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.0\"\n\n[features]\ndefault = [\"ssr\"]\nssr = [\"leptos/ssr\", \"dep:actix-files\", \"dep:actix-web\"]\nhydrate = [\"leptos/hydrate\", \"dep:wasm-bindgen\", \"dep:gloo\"]\n\n[package.metadata.cargo-all-features]\nmax_combination_size = 2\n"
  },
  {
    "path": "leptos_dom/examples/hydration-test/src/lib.rs",
    "content": "#![allow(warnings)]\n\nuse leptos::prelude::*;\n\n#[component]\npub fn App() -> impl IntoView {\n    let pending_thing = create_resource(\n        || false,\n        |_| async {\n            if cfg!(feature = \"ssr\") {\n                let (tx, rx) = futures::channel::oneshot::channel();\n                spawn_local(async {\n                    std::thread::sleep(std::time::Duration::from_millis(10));\n                    tx.send(());\n                });\n                rx.await;\n            } else {\n            }\n            true\n        },\n    );\n\n    view! {\n      <div>\n        <div>\n          \"This is some text\"\n        </div>\n        // <Suspense fallback=move || view! { <p>\"Loading...\"</p> }>\n          {move || pending_thing.read().map(|n| view! { <ComponentA/> })}\n        // </Suspense>\n      </div>\n    }\n}\n\n#[component]\npub fn ComponentA() -> impl IntoView {\n    let (value, set_value) = create_signal(\"Hello?\".to_string());\n    let (counter, set_counter) = create_signal(0);\n\n    // Test to make sure hydration isn't broken by\n    // something like this\n    //let _ = [div()].into_view();\n\n    div()\n        .id(\"the-div\")\n        .child(\n            input()\n                .attr(\"type\", \"text\")\n                .prop(\"value\", (value))\n                .on(ev::input, move |e| set_value(event_target_value(&e))),\n        )\n        .child(input().attr(\"type\", \"text\").prop(\"value\", value))\n        .child(p().child(\"Value: \").child(value))\n        .into_view()\n}\n\n#[cfg(feature = \"hydrate\")]\nuse wasm_bindgen::prelude::wasm_bindgen;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n\n    gloo::console::debug!(\"starting WASM\");\n\n    leptos::mount_to_body(move || {\n        view! { <App/> }\n    });\n}\n"
  },
  {
    "path": "leptos_dom/examples/hydration-test/src/main.rs",
    "content": "use actix_files::Files;\nuse actix_web::*;\nuse futures::StreamExt;\nuse hydration_test::*;\nuse leptos::prelude::*;\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n  HttpServer::new(|| App::new()\n      .service(Files::new(\"/pkg\", \"./pkg\"))\n      .route(\"/\", web::get().to(\n        || async {\n          let pkg_path = \"/pkg/hydration_test\";\n\n          let head = format!(\n            r#\"<!DOCTYPE html>\n            <html lang=\"en\">\n                <head>\n                    <meta charset=\"utf-8\"/>\n                    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                    <link rel=\"modulepreload\" href=\"{pkg_path}.js\">\n                    <link rel=\"preload\" href=\"{pkg_path}_bg.wasm\" as=\"fetch\" type=\"application/wasm\" crossorigin=\"\">\n                    <script type=\"module\">import init, {{ hydrate }} from '{pkg_path}.js'; init('{pkg_path}_bg.wasm').then(hydrate);</script>\n                </head>\n                <body>\"#\n        );\n\n        let tail = \"</body></html>\";\n\n        HttpResponse::Ok().content_type(\"text/html\").streaming(\n            futures::stream::once(async move { head.clone() })\n            .chain(render_to_stream( \n                || view! { <App/> }.into_view(),\n            ))\n            .chain(futures::stream::once(async { tail.to_string() }))\n            .inspect(|html| println!(\"{html}\"))\n            .map(|html| Ok(web::Bytes::from(html)) as Result<web::Bytes>),\n      )})\n    ))\n    .bind((\"127.0.0.1\", 8080))?\n    .run()\n    .await\n}\n"
  },
  {
    "path": "leptos_dom/examples/test-bench/Cargo.toml",
    "content": "[package]\nname = \"test-bench\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.7\"\ngloo = { version = \"0.11.0\", features = [\"futures\"] }\nleptos = { path = \"../../../leptos\", features = [\"nightly\", \"csr\", \"tracing\"] }\ntracing = \"0.1.40\"\ntracing-subscriber = \"0.3.18\"\ntracing-subscriber-wasm = \"0.1.0\"\n\n[workspace]\n"
  },
  {
    "path": "leptos_dom/examples/test-bench/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Leptos DOM v2 Test Bench</title>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "leptos_dom/examples/test-bench/src/main.rs",
    "content": "#![feature(iter_intersperse)]\n#![allow(warnings)]\n\n#[macro_use]\nextern crate tracing;\n\nuse leptos::prelude::*;\nuse tracing::field::debug;\nuse tracing_subscriber::util::SubscriberInitExt;\n\nfn main() {\n    console_error_panic_hook::set_once();\n\n    tracing_subscriber::fmt()\n        .with_max_level(tracing::Level::TRACE)\n        .without_time()\n        .with_file(true)\n        .with_line_number(true)\n        .with_target(false)\n        .with_writer(tracing_subscriber_wasm::MakeConsoleWriter::default())\n        .with_ansi(false)\n        .pretty()\n        .finish()\n        .init();\n\n    mount_to_body(view_fn);\n}\n\nfn view_fn() -> impl IntoView {\n    view! {\n        <h2>\"Passing Tests\"</h2>\n        <ul>\n            <Test from=[1] to=[]/>\n            <Test from=[1, 2] to=[3, 2] then=vec![2]/>\n            <Test from=[1, 2] to=[]/>\n            <Test from=[1, 2, 3] to=[]/>\n            <hr/>\n            <Test from=[] to=[1]/>\n            <Test from=[1, 2] to=[1]/>\n            <Test from=[2, 1] to=[1]/>\n            <hr/>\n            <Test from=[1, 2, 3] to=[1, 2]/>\n            <Test from=[2] to=[1, 2]/>\n            <Test from=[1] to=[1, 2]/>\n            <Test from=[] to=[1, 2, 3]/>\n            <Test from=[2] to=[1, 2, 3]/>\n            <Test from=[1] to=[1, 2, 3]/>\n            <Test from=[1, 3, 2] to=[1, 2, 3]/>\n            <Test from=[2, 1, 3] to=[1, 2, 3]/>\n            <Test from=[3] to=[1, 2, 3]/>\n            <Test from=[3, 1] to=[1, 2, 3]/>\n            <Test from=[3, 2, 1] to=[1, 2, 3]/>\n            <hr/>\n            <Test from=[1, 4, 2, 3] to=[1, 2, 3, 4]/>\n            <hr/>\n            <Test from=[1, 4, 3, 2, 5] to=[1, 2, 3, 4, 5]/>\n            <Test from=[4, 5, 3, 1, 2] to=[1, 2, 3, 4, 5]/>\n        </ul>\n    }\n}\n\n#[component]\nfn Test<From, To>(\n    from: From,\n    to: To,\n    #[prop(optional)] then: Option<Vec<usize>>,\n) -> impl IntoView\nwhere\n    From: IntoIterator<Item = usize>,\n    To: IntoIterator<Item = usize>,\n{\n    let from = from.into_iter().collect::<Vec<_>>();\n    let to = to.into_iter().collect::<Vec<_>>();\n\n    let (list, set_list) = create_signal(from.clone());\n    request_animation_frame({\n        let to = to.clone();\n        let then = then.clone();\n        move || {\n            set_list(to);\n\n            if let Some(then) = then {\n                request_animation_frame({\n                    move || {\n                        set_list(then);\n                    }\n                });\n            }\n        }\n    });\n\n    view! {\n        <li>\n            \"from: [\" {move || {\n                from\n                    .iter()\n                    .map(ToString::to_string)\n                    .intersperse(\", \".to_string())\n                    .collect::<String>()\n            }} \"]\" <br/> \"to: [\" {\n                let then = then.clone();\n                move || {\n                    then\n                        .clone()\n                        .unwrap_or(to.iter().copied().collect())\n                        .iter()\n                        .map(ToString::to_string)\n                        .intersperse(\", \".to_string())\n                        .collect::<String>()\n                }\n            } \"]\" <br/> \"result: [\"\n            <For\n                each=list\n                key=|i| *i\n                view=|i| {\n                    view! { <span>{i} \", \"</span> }\n                }\n            /> \"]\"\n        </li>\n    }\n}\n\n// fn view_fn(cx: Scope) -> impl IntoView {\n//     let (should_show_a, sett_should_show_a) = create_signal(cx, true);\n\n//     let a = vec![2];\n//     let b = vec![1, 2, 3];\n\n//     view! { cx,\n//       <button on:click=move |_| sett_should_show_a.update(|show| *show = !*show)>\"Toggle\"</button>\n\n//       <For\n//         each={move || if should_show_a.get() {\n//           a.clone()\n//         } else {\n//           b.clone()\n//         }}\n//         key=|i| *i\n//         view=|cx, i| view! { cx, <h1>{i}</h1> }\n//       />\n//     }\n// }\n"
  },
  {
    "path": "leptos_dom/examples/view-tests/Cargo.toml",
    "content": "[package]\nname = \"view-tests\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\", \"nightly\"] }\nconsole_log = \"1.0\"\nlog = \"0.4.0\"\nconsole_error_panic_hook = \"0.1.7\"\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.0\"\n"
  },
  {
    "path": "leptos_dom/examples/view-tests/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "leptos_dom/examples/view-tests/src/main.rs",
    "content": "use leptos::prelude::*;\n\npub fn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(|| view! { <Tests/> })\n}\n\n#[component]\nfn SelfUpdatingEffect() -> Element {\n    let (a, set_a) = create_signal(false);\n\n    create_effect(move |_| {\n        if !a() {\n            set_a(true);\n        }\n    });\n\n    view! {\n      <h1>\"Hello \" {move || a().to_string()}</h1>\n    }\n}\n\n#[component]\nfn Tests() -> Element {\n    view! {\n\n        <div>\n            <div><SelfUpdatingEffect/></div>\n            <div><BlockOrders/></div>\n            //<div><TemplateConsumer/></div>\n        </div>\n    }\n}\n\n#[component]\nfn BlockOrders() -> Element {\n    let a = \"A\";\n    let b = \"B\";\n    let c = \"C\";\n\n    view! {\n\n        <div>\n            <div>\"A\"</div>\n            <div>{a}</div>\n            <div><span>\"A\"</span></div>\n            <div><span>{a}</span></div>\n            <hr/>\n            <div>\"A\" {b}</div>\n            <div>{a} \"B\"</div>\n            <div>{a} {b}</div>\n            <div>{\"A\"} {\"B\"}</div>\n            <div><span style=\"color: red\">{a}</span> {b}</div>\n            <hr/>\n            <div>{a} \"B\" {c}</div>\n            <div>\"A\" {b} \"C\"</div>\n            <div>{a} {b} \"C\"</div>\n            <div>{a} {b} {c}</div>\n            <div>\"A\" {b} {c}</div>\n            <hr/>\n            <div>\"A\" {b} <span style=\"color: red\">\"C\"</span></div>\n            <div>\"A\" {b} <span style=\"color: red\">{c}</span></div>\n            <div>\"A\" <span style=\"color: red\">\"B\"</span> \"C\"</div>\n            <div>\"A\" <span style=\"color: red\">\"B\"</span> {c}</div>\n            <div>{a} <span style=\"color: red\">{b}</span> {c}</div>\n            <div>\"A\" {b} <span style=\"color: red\">{c}</span></div>\n            <div><span style=\"color: red\">\"A\"</span> {b} {c}</div>\n            <div><span style=\"color: red\">{a}</span> \"B\" {c}</div>\n            <div><span style=\"color: red\">\"A\"</span> {b} \"C\"</div>\n            <hr/>\n            <div><span style=\"color: red\">\"A\"</span> <span style=\"color: blue\">{b}</span> {c}</div>\n            <div><span style=\"color: red\">{a}</span> \"B\" <span style=\"color: blue\">{c}</span></div>\n            <div><span style=\"color: red\">\"A\"</span> {b} <span style=\"color: blue\">\"C\"</span></div>\n            <hr/>\n            <div><A/></div>\n            <div>\"A\" <B/></div>\n            <div>{a} <B/></div>\n            <div><A/> \"B\"</div>\n            <div><A/> {b}</div>\n            <div><A/><B/></div>\n            <hr/>\n            <div><A/> \"B\" <C/></div>\n            <div><A/> {b} <C/></div>\n            <div><A/> {b} \"C\"</div>\n        </div>\n    }\n}\n\n#[component]\nfn A() -> Element {\n    view! { <span style=\"color: red\">\"A\"</span> }\n}\n\n#[component]\nfn B() -> Element {\n    view! { <span style=\"color: red\">\"B\"</span> }\n}\n\n#[component]\nfn C() -> Element {\n    view! { <span style=\"color: red\">\"C\"</span> }\n}\n\n#[component]\nfn TemplateConsumer() -> Element {\n    let tpl = view! { <TemplateExample/> };\n    let cloned_tpl = tpl\n        .unchecked_ref::<web_sys::HtmlTemplateElement>()\n        .content()\n        .clone_node_with_deep(true)\n        .expect(\"couldn't clone template node\");\n\n    view! {\n\n        <div id=\"template\">\n            /* <h1>\"Template Consumer\"</h1>\n            {cloned_tpl} */\n        </div>\n    }\n}\n\n#[component]\nfn TemplateExample() -> Element {\n    view! {\n\n        <template>\n            <div>\"Template contents\"</div>\n        </template>\n    }\n}\n"
  },
  {
    "path": "leptos_dom/src/helpers.rs",
    "content": "//! A variety of DOM utility functions.\n\nuse or_poisoned::OrPoisoned;\n#[cfg(debug_assertions)]\nuse reactive_graph::diagnostics::SpecialNonReactiveZone;\nuse reactive_graph::owner::Owner;\nuse send_wrapper::SendWrapper;\nuse std::time::Duration;\nuse tachys::html::event::EventDescriptor;\n#[cfg(feature = \"tracing\")]\nuse tracing::instrument;\nuse wasm_bindgen::{prelude::Closure, JsCast, JsValue, UnwrapThrowExt};\n\nthread_local! {\n    pub(crate) static WINDOW: web_sys::Window = web_sys::window().unwrap_throw();\n\n    pub(crate) static DOCUMENT: web_sys::Document = web_sys::window().unwrap_throw().document().unwrap_throw();\n}\n\n/// Returns the [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window).\n///\n/// This is cached as a thread-local variable, so calling `window()` multiple times\n/// requires only one call out to JavaScript.\npub fn window() -> web_sys::Window {\n    WINDOW.with(Clone::clone)\n}\n\n/// Returns the [`Document`](https://developer.mozilla.org/en-US/docs/Web/API/Document).\n///\n/// This is cached as a thread-local variable, so calling `document()` multiple times\n/// requires only one call out to JavaScript.\npub fn document() -> web_sys::Document {\n    DOCUMENT.with(Clone::clone)\n}\n\n/// Sets a property on a DOM element.\npub fn set_property(\n    el: &web_sys::Element,\n    prop_name: &str,\n    value: &Option<JsValue>,\n) {\n    let key = JsValue::from_str(prop_name);\n    match value {\n        Some(value) => _ = js_sys::Reflect::set(el, &key, value),\n        None => _ = js_sys::Reflect::delete_property(el, &key),\n    };\n}\n\n/// Gets the value of a property set on a DOM element.\n#[doc(hidden)]\npub fn get_property(\n    el: &web_sys::Element,\n    prop_name: &str,\n) -> Result<JsValue, JsValue> {\n    let key = JsValue::from_str(prop_name);\n    js_sys::Reflect::get(el, &key)\n}\n\n/// Returns the current [`window.location`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location).\npub fn location() -> web_sys::Location {\n    window().location()\n}\n\n/// Current [`window.location.hash`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location)\n/// without the beginning #.\npub fn location_hash() -> Option<String> {\n    if is_server() {\n        None\n    } else {\n        location()\n            .hash()\n            .ok()\n            .map(|hash| match hash.chars().next() {\n                Some('#') => hash[1..].to_string(),\n                _ => hash,\n            })\n    }\n}\n\n/// Current [`window.location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location).\npub fn location_pathname() -> Option<String> {\n    location().pathname().ok()\n}\n\n/// Helper function to extract [`Event.target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target)\n/// from any event.\npub fn event_target<T>(event: &web_sys::Event) -> T\nwhere\n    T: JsCast,\n{\n    event.target().unwrap_throw().unchecked_into::<T>()\n}\n\n/// Helper function to extract `event.target.value` from an event.\n///\n/// This is useful in the `on:input` or `on:change` listeners for an `<input>` element.\npub fn event_target_value<T>(event: &T) -> String\nwhere\n    T: JsCast,\n{\n    event\n        .unchecked_ref::<web_sys::Event>()\n        .target()\n        .unwrap_throw()\n        .unchecked_into::<web_sys::HtmlInputElement>()\n        .value()\n}\n\n/// Helper function to extract `event.target.checked` from an event.\n///\n/// This is useful in the `on:change` listeners for an `<input type=\"checkbox\">` element.\npub fn event_target_checked(ev: &web_sys::Event) -> bool {\n    ev.target()\n        .unwrap()\n        .unchecked_into::<web_sys::HtmlInputElement>()\n        .checked()\n}\n\n/// Handle that is generated by [request_animation_frame_with_handle] and can\n/// be used to cancel the animation frame request.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct AnimationFrameRequestHandle(i32);\n\nimpl AnimationFrameRequestHandle {\n    /// Cancels the animation frame request to which this refers.\n    /// See [`cancelAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame)\n    pub fn cancel(&self) {\n        _ = window().cancel_animation_frame(self.0);\n    }\n}\n\n/// Runs the given function between the next repaint using\n/// [`Window.requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(feature = \"tracing\", instrument(level = \"trace\", skip_all))]\n#[inline(always)]\npub fn request_animation_frame(cb: impl FnOnce() + 'static) {\n    _ = request_animation_frame_with_handle(cb);\n}\n\n// Closure::once_into_js only frees the callback when it's actually\n// called, so this instead uses into_js_value, which can be freed by\n// the host JS engine's GC if it supports weak references (which all\n// modern browser engines do).  The way this works is that the provided\n// callback's captured data is dropped immediately after being called,\n// as before, but it leaves behind a small stub closure rust-side that\n// will be freed \"eventually\" by the JS GC.  If the function is never\n// called (e.g., it's a cancelled timeout or animation frame callback)\n// then it will also be freed eventually.\nfn closure_once(cb: impl FnOnce() + 'static) -> JsValue {\n    let mut wrapped_cb: Option<Box<dyn FnOnce()>> = Some(Box::new(cb));\n    let closure = Closure::new(move || {\n        if let Some(cb) = wrapped_cb.take() {\n            cb()\n        }\n    });\n    closure.into_js_value()\n}\n\n/// Runs the given function between the next repaint using\n/// [`Window.requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame),\n/// returning a cancelable handle.\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(feature = \"tracing\", instrument(level = \"trace\", skip_all))]\n#[inline(always)]\npub fn request_animation_frame_with_handle(\n    cb: impl FnOnce() + 'static,\n) -> Result<AnimationFrameRequestHandle, JsValue> {\n    #[cfg(feature = \"tracing\")]\n    let span = ::tracing::Span::current();\n    #[cfg(feature = \"tracing\")]\n    let cb = move || {\n        let _guard = span.enter();\n        cb();\n    };\n\n    #[inline(never)]\n    fn raf(cb: JsValue) -> Result<AnimationFrameRequestHandle, JsValue> {\n        window()\n            .request_animation_frame(cb.as_ref().unchecked_ref())\n            .map(AnimationFrameRequestHandle)\n    }\n\n    raf(closure_once(cb))\n}\n\n/// Handle that is generated by [request_idle_callback_with_handle] and can be\n/// used to cancel the idle callback.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct IdleCallbackHandle(u32);\n\nimpl IdleCallbackHandle {\n    /// Cancels the idle callback to which this refers.\n    /// See [`cancelAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelIdleCallback)\n    pub fn cancel(&self) {\n        window().cancel_idle_callback(self.0);\n    }\n}\n\n/// Queues the given function during an idle period using\n/// [`Window.requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestIdleCallback).\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(feature = \"tracing\", instrument(level = \"trace\", skip_all))]\n#[inline(always)]\npub fn request_idle_callback(cb: impl Fn() + 'static) {\n    _ = request_idle_callback_with_handle(cb);\n}\n\n/// Queues the given function during an idle period using\n/// [`Window.requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestIdleCallback),\n/// returning a cancelable handle.\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(feature = \"tracing\", instrument(level = \"trace\", skip_all))]\n#[inline(always)]\npub fn request_idle_callback_with_handle(\n    cb: impl Fn() + 'static,\n) -> Result<IdleCallbackHandle, JsValue> {\n    #[cfg(feature = \"tracing\")]\n    let span = ::tracing::Span::current();\n    #[cfg(feature = \"tracing\")]\n    let cb = move || {\n        let _guard = span.enter();\n        cb();\n    };\n\n    #[inline(never)]\n    fn ric(cb: Box<dyn Fn()>) -> Result<IdleCallbackHandle, JsValue> {\n        let cb = Closure::wrap(cb).into_js_value();\n\n        window()\n            .request_idle_callback(cb.as_ref().unchecked_ref())\n            .map(IdleCallbackHandle)\n    }\n\n    ric(Box::new(cb))\n}\n\n/// A microtask is a short function which will run after the current task has\n/// completed its work and when there is no other code waiting to be run before\n/// control of the execution context is returned to the browser's event loop.\n///\n/// Microtasks are especially useful for libraries and frameworks that need\n/// to perform final cleanup or other just-before-rendering tasks.\n///\n/// [MDN queueMicrotask](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)\n///\n/// <div class=\"warning\">The task is called outside of the ownership tree, this means that if you want to access for example the context you need to reestablish the owner.</div>\npub fn queue_microtask(task: impl FnOnce() + 'static) {\n    tachys::renderer::dom::queue_microtask(task);\n}\n\n/// Handle that is generated by [set_timeout_with_handle] and can be used to clear the timeout.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct TimeoutHandle(i32);\n\nimpl TimeoutHandle {\n    /// Cancels the timeout to which this refers.\n    /// See [`clearTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout)\n    pub fn clear(&self) {\n        window().clear_timeout_with_handle(self.0);\n    }\n}\n\n/// Executes the given function after the given duration of time has passed.\n/// [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout).\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(\n  feature = \"tracing\",\n  instrument(level = \"trace\", skip_all, fields(duration = ?duration))\n)]\npub fn set_timeout(cb: impl FnOnce() + 'static, duration: Duration) {\n    _ = set_timeout_with_handle(cb, duration);\n}\n\n/// Executes the given function after the given duration of time has passed, returning a cancelable handle.\n/// [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout).\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(\n  feature = \"tracing\",\n  instrument(level = \"trace\", skip_all, fields(duration = ?duration))\n)]\n#[inline(always)]\npub fn set_timeout_with_handle(\n    cb: impl FnOnce() + 'static,\n    duration: Duration,\n) -> Result<TimeoutHandle, JsValue> {\n    #[cfg(debug_assertions)]\n    let cb = || {\n        let _z = SpecialNonReactiveZone::enter();\n        cb();\n    };\n\n    #[cfg(feature = \"tracing\")]\n    let span = ::tracing::Span::current();\n    #[cfg(feature = \"tracing\")]\n    let cb = move || {\n        let _guard = span.enter();\n        cb();\n    };\n\n    #[inline(never)]\n    fn st(cb: JsValue, duration: Duration) -> Result<TimeoutHandle, JsValue> {\n        window()\n            .set_timeout_with_callback_and_timeout_and_arguments_0(\n                cb.as_ref().unchecked_ref(),\n                duration.as_millis().try_into().unwrap_throw(),\n            )\n            .map(TimeoutHandle)\n    }\n\n    st(closure_once(cb), duration)\n}\n\n/// \"Debounce\" a callback function. This will cause it to wait for a period of `delay`\n/// after it is called. If it is called again during that period, it will wait\n/// `delay` before running, and so on. This can be used, for example, to wrap event\n/// listeners to prevent them from firing constantly as you type.\n///\n/// ```\n/// use leptos::{leptos_dom::helpers::debounce, logging::log, prelude::*, *};\n///\n/// #[component]\n/// fn DebouncedButton() -> impl IntoView {\n///     let delay = std::time::Duration::from_millis(250);\n///     let on_click = debounce(delay, move |_| {\n///         log!(\"...so many clicks!\");\n///     });\n///\n///     view! {\n///       <button on:click=on_click>\"Click me\"</button>\n///     }\n/// }\n/// ```\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\npub fn debounce<T: 'static>(\n    delay: Duration,\n    mut cb: impl FnMut(T) + 'static,\n) -> impl FnMut(T) {\n    use std::sync::{Arc, RwLock};\n\n    #[cfg(debug_assertions)]\n    #[allow(unused_mut)]\n    let mut cb = move |value| {\n        let _z = SpecialNonReactiveZone::enter();\n        cb(value);\n    };\n\n    #[cfg(feature = \"tracing\")]\n    let span = ::tracing::Span::current();\n    #[cfg(feature = \"tracing\")]\n    #[allow(unused_mut)]\n    let mut cb = move |value| {\n        let _guard = span.enter();\n        cb(value);\n    };\n\n    let cb = Arc::new(RwLock::new(cb));\n    let timer = Arc::new(RwLock::new(None::<TimeoutHandle>));\n\n    Owner::on_cleanup({\n        let timer = Arc::clone(&timer);\n        move || {\n            if let Some(timer) = timer.write().or_poisoned().take() {\n                timer.clear();\n            }\n        }\n    });\n\n    move |arg| {\n        if let Some(timer) = timer.write().unwrap().take() {\n            timer.clear();\n        }\n        let handle = set_timeout_with_handle(\n            {\n                let cb = Arc::clone(&cb);\n                move || {\n                    cb.write().unwrap()(arg);\n                }\n            },\n            delay,\n        );\n        if let Ok(handle) = handle {\n            *timer.write().or_poisoned() = Some(handle);\n        }\n    }\n}\n\n/// Handle that is generated by [set_interval] and can be used to clear the interval.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct IntervalHandle(i32);\n\nimpl IntervalHandle {\n    /// Cancels the repeating event to which this refers.\n    /// See [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval)\n    pub fn clear(&self) {\n        window().clear_interval_with_handle(self.0);\n    }\n}\n\n/// Repeatedly calls the given function, with a delay of the given duration between calls.\n/// See [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval).\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(\n  feature = \"tracing\",\n  instrument(level = \"trace\", skip_all, fields(duration = ?duration))\n)]\npub fn set_interval(cb: impl Fn() + 'static, duration: Duration) {\n    _ = set_interval_with_handle(cb, duration);\n}\n\n/// Repeatedly calls the given function, with a delay of the given duration between calls,\n/// returning a cancelable handle.\n/// See [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval).\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(\n  feature = \"tracing\",\n  instrument(level = \"trace\", skip_all, fields(duration = ?duration))\n)]\n#[inline(always)]\npub fn set_interval_with_handle(\n    cb: impl Fn() + 'static,\n    duration: Duration,\n) -> Result<IntervalHandle, JsValue> {\n    #[cfg(debug_assertions)]\n    let cb = move || {\n        let _z = SpecialNonReactiveZone::enter();\n        cb();\n    };\n    #[cfg(feature = \"tracing\")]\n    let span = ::tracing::Span::current();\n    #[cfg(feature = \"tracing\")]\n    let cb = move || {\n        let _guard = span.enter();\n        cb();\n    };\n\n    #[inline(never)]\n    fn si(\n        cb: Box<dyn FnMut()>,\n        duration: Duration,\n    ) -> Result<IntervalHandle, JsValue> {\n        let cb = Closure::wrap(cb).into_js_value();\n\n        window()\n            .set_interval_with_callback_and_timeout_and_arguments_0(\n                cb.as_ref().unchecked_ref(),\n                duration.as_millis().try_into().unwrap_throw(),\n            )\n            .map(IntervalHandle)\n    }\n\n    si(Box::new(cb), duration)\n}\n\n/// Adds an event listener to the `Window`, typed as a generic `Event`,\n/// returning a cancelable handle.\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\n#[cfg_attr(\n  feature = \"tracing\",\n  instrument(level = \"trace\", skip_all, fields(event_name = %event_name))\n)]\n#[inline(always)]\npub fn window_event_listener_untyped(\n    event_name: &str,\n    cb: impl Fn(web_sys::Event) + 'static,\n) -> WindowListenerHandle {\n    #[cfg(debug_assertions)]\n    let cb = move |e| {\n        let _z = SpecialNonReactiveZone::enter();\n        cb(e);\n    };\n    #[cfg(feature = \"tracing\")]\n    let span = ::tracing::Span::current();\n    #[cfg(feature = \"tracing\")]\n    let cb = move |e| {\n        let _guard = span.enter();\n        cb(e);\n    };\n\n    if !is_server() {\n        #[inline(never)]\n        fn wel(\n            cb: Box<dyn FnMut(web_sys::Event)>,\n            event_name: &str,\n        ) -> WindowListenerHandle {\n            let cb = Closure::wrap(cb).into_js_value();\n            _ = window().add_event_listener_with_callback(\n                event_name,\n                cb.unchecked_ref(),\n            );\n            let event_name = event_name.to_string();\n            let cb = SendWrapper::new(cb);\n            WindowListenerHandle(Box::new(move || {\n                _ = window().remove_event_listener_with_callback(\n                    &event_name,\n                    cb.unchecked_ref(),\n                );\n            }))\n        }\n\n        wel(Box::new(cb), event_name)\n    } else {\n        WindowListenerHandle(Box::new(|| ()))\n    }\n}\n\n/// Creates a window event listener from a typed event, returning a\n/// cancelable handle.\n/// ```\n/// use leptos::{\n///     ev, leptos_dom::helpers::window_event_listener, logging::log,\n///     prelude::*,\n/// };\n///\n/// #[component]\n/// fn App() -> impl IntoView {\n///     let handle = window_event_listener(ev::keypress, |ev| {\n///         // ev is typed as KeyboardEvent automatically,\n///         // so .code() can be called\n///         let code = ev.code();\n///         log!(\"code = {code:?}\");\n///     });\n///     on_cleanup(move || handle.remove());\n/// }\n/// ```\n///\n/// ### Note about Context\n///\n/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).\npub fn window_event_listener<E: EventDescriptor + 'static>(\n    event: E,\n    cb: impl Fn(E::EventType) + 'static,\n) -> WindowListenerHandle\nwhere\n    E::EventType: JsCast,\n{\n    window_event_listener_untyped(&event.name(), move |e| {\n        cb(e.unchecked_into::<E::EventType>())\n    })\n}\n\n/// A handle that can be called to remove a global event listener.\npub struct WindowListenerHandle(Box<dyn FnOnce() + Send + Sync>);\n\nimpl core::fmt::Debug for WindowListenerHandle {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_tuple(\"WindowListenerHandle\").finish()\n    }\n}\n\nimpl WindowListenerHandle {\n    /// Removes the event listener.\n    pub fn remove(self) {\n        (self.0)()\n    }\n}\n\n/// Returns `true` if the current environment is a server.\npub fn is_server() -> bool {\n    #[cfg(feature = \"hydration\")]\n    {\n        Owner::current_shared_context()\n            .map(|sc| !sc.is_browser())\n            .unwrap_or(false)\n    }\n    #[cfg(not(feature = \"hydration\"))]\n    {\n        false\n    }\n}\n\n/// Returns `true` if the current environment is a browser.\npub fn is_browser() -> bool {\n    !is_server()\n}\n"
  },
  {
    "path": "leptos_dom/src/lib.rs",
    "content": "#![deny(missing_docs)]\n#![forbid(unsafe_code)]\n\n//! DOM helpers for Leptos.\n\npub mod helpers;\n#[doc(hidden)]\npub mod macro_helpers;\n\n/// Utilities for simple isomorphic logging to the console or terminal.\npub mod logging;\n"
  },
  {
    "path": "leptos_dom/src/logging.rs",
    "content": "//! Utilities for simple isomorphic logging to the console or terminal.\n\nuse wasm_bindgen::JsValue;\n\n/// Uses `println!()`-style formatting to log something to the console (in the browser)\n/// or via `println!()` (if not in the browser).\n#[macro_export]\nmacro_rules! log {\n    ($($t:tt)*) => ($crate::logging::console_log(&format_args!($($t)*).to_string()))\n}\n\n/// Uses `println!()`-style formatting to log warnings to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser).\n#[macro_export]\nmacro_rules! warn {\n    ($($t:tt)*) => ($crate::logging::console_warn(&format_args!($($t)*).to_string()))\n}\n\n/// Uses `println!()`-style formatting to log errors to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser).\n#[macro_export]\nmacro_rules! error {\n    ($($t:tt)*) => ($crate::logging::console_error(&format_args!($($t)*).to_string()))\n}\n\n/// Uses `println!()`-style formatting to log something to the console (in the browser)\n/// or via `println!()` (if not in the browser), but only if it's a debug build.\n#[macro_export]\nmacro_rules! debug_log {\n    ($($x:tt)*) => {\n        {\n            if cfg!(debug_assertions) {\n                $crate::log!($($x)*)\n            }\n        }\n    }\n}\n\n/// Uses `println!()`-style formatting to log warnings to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser), but only if it's a debug build.\n#[macro_export]\nmacro_rules! debug_warn {\n    ($($x:tt)*) => {\n        {\n            if cfg!(debug_assertions) {\n                $crate::warn!($($x)*)\n            }\n        }\n    }\n}\n\n/// Uses `println!()`-style formatting to log errors to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser), but only if it's a debug build.\n#[macro_export]\nmacro_rules! debug_error {\n    ($($x:tt)*) => {\n        {\n            if cfg!(debug_assertions) {\n                $crate::error!($($x)*)\n            }\n        }\n    }\n}\n\nconst fn log_to_stdout() -> bool {\n    cfg!(not(all(\n        target_arch = \"wasm32\",\n        not(any(target_os = \"emscripten\", target_os = \"wasi\"))\n    )))\n}\n\n/// Log a string to the console (in the browser)\n/// or via `println!()` (if not in the browser).\npub fn console_log(s: &str) {\n    #[allow(clippy::print_stdout)]\n    if log_to_stdout() {\n        println!(\"{s}\");\n    } else {\n        web_sys::console::log_1(&JsValue::from_str(s));\n    }\n}\n\n/// Log a warning to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser).\npub fn console_warn(s: &str) {\n    if log_to_stdout() {\n        eprintln!(\"{s}\");\n    } else {\n        web_sys::console::warn_1(&JsValue::from_str(s));\n    }\n}\n\n/// Log an error to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser).\n#[inline(always)]\npub fn console_error(s: &str) {\n    if log_to_stdout() {\n        eprintln!(\"{s}\");\n    } else {\n        web_sys::console::error_1(&JsValue::from_str(s));\n    }\n}\n\n/// Log a string to the console (in the browser)\n/// or via `println!()` (if not in the browser), but only in a debug build.\n#[inline(always)]\npub fn console_debug_log(s: &str) {\n    if cfg!(debug_assertions) {\n        console_log(s)\n    }\n}\n\n/// Log a warning to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser), but only in a debug build.\n#[inline(always)]\npub fn console_debug_warn(s: &str) {\n    if cfg!(debug_assertions) {\n        console_warn(s)\n    }\n}\n\n/// Log an error to the console (in the browser)\n/// or via `eprintln!()` (if not in the browser), but only in a debug build.\n#[inline(always)]\npub fn console_debug_error(s: &str) {\n    if cfg!(debug_assertions) {\n        console_error(s)\n    }\n}\n"
  },
  {
    "path": "leptos_dom/src/macro_helpers/mod.rs",
    "content": "#[cfg(feature = \"trace-component-props\")]\n#[doc(hidden)]\npub mod tracing_property;\n"
  },
  {
    "path": "leptos_dom/src/macro_helpers/tracing_property.rs",
    "content": "use wasm_bindgen::UnwrapThrowExt;\n\n#[macro_export]\n/// Use for tracing property\nmacro_rules! tracing_props {\n    () => {\n        ::leptos::tracing::span!(\n            ::leptos::tracing::Level::TRACE,\n            \"leptos_dom::tracing_props\",\n            props = String::from(\"[]\")\n        );\n    };\n    ($($prop:tt),+ $(,)?) => {\n        {\n            use ::leptos::leptos_dom::macro_helpers::tracing_property::{Match, SerializeMatch, DefaultMatch};\n            let mut props = String::from('[');\n            $(\n                let prop = (&&Match {\n                    name: stringify!{$prop},\n                    value: std::cell::Cell::new(Some(&$prop))\n                }).spez();\n                props.push_str(&format!(\"{prop},\"));\n            )*\n            props.pop();\n            props.push(']');\n            ::leptos::tracing::span!(\n                ::leptos::tracing::Level::TRACE,\n                \"leptos_dom::tracing_props\",\n                props\n            );\n        }\n    };\n}\n\n// Implementation based on spez\n// see https://github.com/m-ou-se/spez\n\npub struct Match<T> {\n    pub name: &'static str,\n    pub value: std::cell::Cell<Option<T>>,\n}\n\npub trait SerializeMatch {\n    type Return;\n    fn spez(&self) -> Self::Return;\n}\nimpl<T: serde::Serialize> SerializeMatch for &Match<&T> {\n    type Return = String;\n    fn spez(&self) -> Self::Return {\n        let name = self.name;\n\n        // suppresses warnings when serializing signals into props\n        #[cfg(debug_assertions)]\n        let _z = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n\n        serde_json::to_string(self.value.get().unwrap_throw()).map_or_else(\n            |err| format!(r#\"{{\"name\": \"{name}\", \"error\": \"{err}\"}}\"#),\n            |value| format!(r#\"{{\"name\": \"{name}\", \"value\": {value}}}\"#),\n        )\n    }\n}\n\npub trait DefaultMatch {\n    type Return;\n    fn spez(&self) -> Self::Return;\n}\nimpl<T> DefaultMatch for Match<&T> {\n    type Return = String;\n    fn spez(&self) -> Self::Return {\n        let name = self.name;\n        format!(r#\"{{\"name\": \"{name}\", \"value\": \"[unserializable value]\"}}\"#)\n    }\n}\n\n#[test]\nfn match_primitive() {\n    // String\n    let test = String::from(\"string\");\n    let prop = (&&Match {\n        name: stringify! {test},\n        value: std::cell::Cell::new(Some(&test)),\n    })\n        .spez();\n    assert_eq!(prop, r#\"{\"name\": \"test\", \"value\": \"string\"}\"#);\n\n    // &str\n    let test = \"string\";\n    let prop = (&&Match {\n        name: stringify! {test},\n        value: std::cell::Cell::new(Some(&test)),\n    })\n        .spez();\n    assert_eq!(prop, r#\"{\"name\": \"test\", \"value\": \"string\"}\"#);\n\n    // u128\n    let test: u128 = 1;\n    let prop = (&&Match {\n        name: stringify! {test},\n        value: std::cell::Cell::new(Some(&test)),\n    })\n        .spez();\n    assert_eq!(prop, r#\"{\"name\": \"test\", \"value\": 1}\"#);\n\n    // i128\n    let test: i128 = -1;\n    let prop = (&&Match {\n        name: stringify! {test},\n        value: std::cell::Cell::new(Some(&test)),\n    })\n        .spez();\n    assert_eq!(prop, r#\"{\"name\": \"test\", \"value\": -1}\"#);\n\n    // f64\n    let test = 3.25;\n    let prop = (&&Match {\n        name: stringify! {test},\n        value: std::cell::Cell::new(Some(&test)),\n    })\n        .spez();\n    assert_eq!(prop, r#\"{\"name\": \"test\", \"value\": 3.25}\"#);\n\n    // bool\n    let test = true;\n    let prop = (&&Match {\n        name: stringify! {test},\n        value: std::cell::Cell::new(Some(&test)),\n    })\n        .spez();\n    assert_eq!(prop, r#\"{\"name\": \"test\", \"value\": true}\"#);\n}\n\n#[test]\nfn match_serialize() {\n    use serde::Serialize;\n    #[derive(Serialize)]\n    struct CustomStruct {\n        field: &'static str,\n    }\n\n    let test = CustomStruct { field: \"field\" };\n    let prop = (&&Match {\n        name: stringify! {test},\n        value: std::cell::Cell::new(Some(&test)),\n    })\n        .spez();\n    assert_eq!(prop, r#\"{\"name\": \"test\", \"value\": {\"field\":\"field\"}}\"#);\n    // Verification of ownership\n    assert_eq!(test.field, \"field\");\n}\n\n#[test]\n#[allow(clippy::needless_borrow)]\nfn match_no_serialize() {\n    struct CustomStruct {\n        field: &'static str,\n    }\n\n    let test = CustomStruct { field: \"field\" };\n    let prop = (&&Match {\n        name: stringify! {test},\n        value: std::cell::Cell::new(Some(&test)),\n    })\n        .spez();\n    assert_eq!(\n        prop,\n        r#\"{\"name\": \"test\", \"value\": \"[unserializable value]\"}\"#\n    );\n    // Verification of ownership\n    assert_eq!(test.field, \"field\");\n}\n"
  },
  {
    "path": "leptos_hot_reload/Cargo.toml",
    "content": "[package]\nname = \"leptos_hot_reload\"\nversion = \"0.8.6\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Utility types used for dev mode and hot-reloading for the Leptos web framework.\"\nreadme = \"../README.md\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nanyhow = { workspace = true, default-features = true }\nserde = { features = [\"derive\"], workspace = true, default-features = true }\nsyn = { features = [\n  \"full\",\n  \"parsing\",\n  \"extra-traits\",\n  \"visit\",\n  \"printing\",\n], workspace = true, default-features = true }\nquote = { workspace = true, default-features = true }\nrstml = { workspace = true, default-features = true }\nproc-macro2 = { features = [\n  \"span-locations\",\n  \"nightly\",\n], workspace = true, default-features = true }\nwalkdir = { workspace = true, default-features = true }\ncamino = { workspace = true, default-features = true }\nindexmap = { workspace = true, default-features = true }\nor_poisoned = { workspace = true, default-features = true }\n"
  },
  {
    "path": "leptos_hot_reload/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "leptos_hot_reload/src/diff.rs",
    "content": "use crate::node::{LAttributeValue, LNode};\nuse indexmap::IndexMap;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Debug, Default)]\nstruct OldChildren(IndexMap<LNode, Vec<usize>>);\n\nimpl LNode {\n    #[must_use]\n    pub fn diff(&self, other: &LNode) -> Vec<Patch> {\n        let mut old_children = OldChildren::default();\n        self.add_old_children(vec![], &mut old_children);\n        self.diff_at(other, &[], &old_children)\n    }\n\n    fn to_replacement_node(\n        &self,\n        old_children: &OldChildren,\n    ) -> ReplacementNode {\n        match old_children.0.get(self) {\n            // if the child already exists in the DOM, we can pluck it out\n            // and move it around\n            Some(path) => ReplacementNode::Path(path.to_owned()),\n            // otherwise, we should generate some HTML\n            // but we need to do this recursively in case we're replacing an element\n            // with children who need to be plucked out\n            None => match self {\n                LNode::Fragment(fragment) => ReplacementNode::Fragment(\n                    fragment\n                        .iter()\n                        .map(|node| node.to_replacement_node(old_children))\n                        .collect(),\n                ),\n                LNode::Element {\n                    name,\n                    attrs,\n                    children,\n                } => ReplacementNode::Element {\n                    name: name.to_owned(),\n                    attrs: attrs\n                        .iter()\n                        .filter_map(|(name, value)| match value {\n                            LAttributeValue::Boolean => {\n                                Some((name.to_owned(), name.to_owned()))\n                            }\n                            LAttributeValue::Static(value) => {\n                                Some((name.to_owned(), value.to_owned()))\n                            }\n                            _ => None,\n                        })\n                        .collect(),\n                    children: children\n                        .iter()\n                        .map(|node| node.to_replacement_node(old_children))\n                        .collect(),\n                },\n                LNode::Text(_)\n                | LNode::Component { .. }\n                | LNode::DynChild(_) => ReplacementNode::Html(self.to_html()),\n            },\n        }\n    }\n\n    fn add_old_children(&self, path: Vec<usize>, positions: &mut OldChildren) {\n        match self {\n            LNode::Fragment(frag) => {\n                for (idx, child) in frag.iter().enumerate() {\n                    let mut new_path = path.clone();\n                    new_path.push(idx);\n                    child.add_old_children(new_path, positions);\n                }\n            }\n            LNode::Element { children, .. } => {\n                for (idx, child) in children.iter().enumerate() {\n                    let mut new_path = path.clone();\n                    new_path.push(idx);\n                    child.add_old_children(new_path, positions);\n                }\n            }\n            // need to insert dynamic content and children, as these might change\n            LNode::DynChild(_) => {\n                positions.0.insert(self.clone(), path);\n            }\n            LNode::Component { children, .. } => {\n                positions.0.insert(self.clone(), path.clone());\n\n                for (idx, child) in children.iter().enumerate() {\n                    let mut new_path = path.clone();\n                    new_path.push(idx);\n                    child.add_old_children(new_path, positions);\n                }\n            }\n            // can just create text nodes, whatever\n            LNode::Text(_) => {}\n        }\n    }\n\n    fn diff_at(\n        &self,\n        other: &LNode,\n        path: &[usize],\n        orig_children: &OldChildren,\n    ) -> Vec<Patch> {\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return vec![Patch {\n                path: path.to_owned(),\n                action: PatchAction::ReplaceWith(\n                    other.to_replacement_node(orig_children),\n                ),\n            }];\n        }\n        match (self, other) {\n            // fragment: diff children\n            (LNode::Fragment(old), LNode::Fragment(new)) => {\n                LNode::diff_children(path, old, new, orig_children)\n            }\n            // text node: replace text\n            (LNode::Text(_), LNode::Text(new)) => vec![Patch {\n                path: path.to_owned(),\n                action: PatchAction::SetText(new.to_owned()),\n            }],\n            // elements\n            (\n                LNode::Element {\n                    name: old_name,\n                    attrs: old_attrs,\n                    children: old_children,\n                },\n                LNode::Element {\n                    name: new_name,\n                    attrs: new_attrs,\n                    children: new_children,\n                },\n            ) => {\n                let tag_patch = (old_name != new_name).then(|| Patch {\n                    path: path.to_owned(),\n                    action: PatchAction::ChangeTagName(new_name.to_owned()),\n                });\n\n                let attrs_patch = LNode::diff_attrs(path, old_attrs, new_attrs);\n\n                let children_patch = LNode::diff_children(\n                    path,\n                    old_children,\n                    new_children,\n                    orig_children,\n                );\n\n                attrs_patch\n                    .into_iter()\n                    // tag patch comes second so we remove old attrs before copying them over\n                    .chain(tag_patch)\n                    .chain(children_patch)\n                    .collect()\n            }\n            // components + dynamic context: no patches\n            (\n                LNode::Component {\n                    name: old_name,\n                    children: old_children,\n                    ..\n                },\n                LNode::Component {\n                    name: new_name,\n                    children: new_children,\n                    ..\n                },\n            ) if old_name == new_name => {\n                let mut path = path.to_vec();\n                path.push(0);\n                path.push(0);\n                LNode::diff_children(\n                    &path,\n                    old_children,\n                    new_children,\n                    orig_children,\n                )\n            }\n            _ => vec![],\n        }\n    }\n\n    fn diff_attrs<'a>(\n        path: &'a [usize],\n        old: &'a [(String, LAttributeValue)],\n        new: &'a [(String, LAttributeValue)],\n    ) -> impl Iterator<Item = Patch> + 'a {\n        let additions = new\n            .iter()\n            .filter_map(|(name, new_value)| {\n                let old_attr = old.iter().find(|(o_name, _)| o_name == name);\n                let replace = match old_attr {\n                    None => true,\n                    Some((_, old_value)) if old_value != new_value => true,\n                    _ => false,\n                };\n                if replace {\n                    match &new_value {\n                        LAttributeValue::Boolean => {\n                            Some((name.to_owned(), String::new()))\n                        }\n                        LAttributeValue::Static(s) => {\n                            Some((name.to_owned(), s.to_owned()))\n                        }\n                        _ => None,\n                    }\n                } else {\n                    None\n                }\n            })\n            .map(|(name, value)| Patch {\n                path: path.to_owned(),\n                action: PatchAction::SetAttribute(name, value),\n            });\n\n        let removals = old.iter().filter_map(|(name, _)| {\n            if new.iter().any(|(new_name, _)| new_name == name) {\n                None\n            } else {\n                Some(Patch {\n                    path: path.to_owned(),\n                    action: PatchAction::RemoveAttribute(name.to_owned()),\n                })\n            }\n        });\n\n        additions.chain(removals)\n    }\n\n    fn diff_children(\n        path: &[usize],\n        old: &[LNode],\n        new: &[LNode],\n        old_children: &OldChildren,\n    ) -> Vec<Patch> {\n        if old.is_empty() && new.is_empty() {\n            vec![]\n        } else if old.is_empty() {\n            vec![Patch {\n                path: path.to_owned(),\n                action: PatchAction::AppendChildren(\n                    new.iter()\n                        .map(LNode::to_html)\n                        .map(ReplacementNode::Html)\n                        .collect(),\n                ),\n            }]\n        } else if new.is_empty() {\n            vec![Patch {\n                path: path.to_owned(),\n                action: PatchAction::ClearChildren,\n            }]\n        } else {\n            let width = old.len() + 1;\n            let height = new.len() + 1;\n            let mut mat = vec![0; width * height];\n            #[allow(clippy::needless_range_loop)]\n            for i in 1..width {\n                mat[i] = i;\n            }\n            for i in 1..height {\n                mat[i * width] = i;\n            }\n            for j in 1..height {\n                for i in 1..width {\n                    if old[i - 1] == new[j - 1] {\n                        mat[j * width + i] = mat[(j - 1) * width + (i - 1)];\n                    } else {\n                        mat[j * width + i] = (mat[(j - 1) * width + i] + 1)\n                            .min(mat[j * width + (i - 1)] + 1)\n                            .min(mat[(j - 1) * width + (i - 1)] + 1)\n                    }\n                }\n            }\n            let (mut i, mut j) = (old.len(), new.len());\n            let mut patches = vec![];\n            while i > 0 || j > 0 {\n                if i > 0 && j > 0 && old[i - 1] == new[j - 1] {\n                    i -= 1;\n                    j -= 1;\n                } else {\n                    let current = mat[j * width + i];\n                    if i > 0\n                        && j > 0\n                        && mat[(j - 1) * width + i - 1] + 1 == current\n                    {\n                        let mut new_path = path.to_owned();\n                        new_path.push(i - 1);\n                        let diffs = old[i - 1].diff_at(\n                            &new[j - 1],\n                            &new_path,\n                            old_children,\n                        );\n                        patches.extend(&mut diffs.into_iter());\n                        i -= 1;\n                        j -= 1;\n                    } else if i > 0 && mat[j * width + i - 1] + 1 == current {\n                        patches.push(Patch {\n                            path: path.to_owned(),\n                            action: PatchAction::RemoveChild { at: i - 1 },\n                        });\n                        i -= 1;\n                    } else if j > 0 && mat[(j - 1) * width + i] + 1 == current {\n                        patches.push(Patch {\n                            path: path.to_owned(),\n                            action: PatchAction::InsertChild {\n                                before: i,\n                                child: new[j - 1]\n                                    .to_replacement_node(old_children),\n                            },\n                        });\n                        j -= 1;\n                    } else {\n                        unreachable!();\n                    }\n                }\n            }\n\n            patches\n        }\n    }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct Patches(pub Vec<(String, Vec<Patch>)>);\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct Patch {\n    path: Vec<usize>,\n    action: PatchAction,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub enum PatchAction {\n    ReplaceWith(ReplacementNode),\n    ChangeTagName(String),\n    RemoveAttribute(String),\n    SetAttribute(String, String),\n    SetText(String),\n    ClearChildren,\n    AppendChildren(Vec<ReplacementNode>),\n    RemoveChild {\n        at: usize,\n    },\n    InsertChild {\n        before: usize,\n        child: ReplacementNode,\n    },\n    InsertChildAfter {\n        after: usize,\n        child: ReplacementNode,\n    },\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub enum ReplacementNode {\n    Html(String),\n    Path(Vec<usize>),\n    Fragment(Vec<ReplacementNode>),\n    Element {\n        name: String,\n        attrs: Vec<(String, String)>,\n        children: Vec<ReplacementNode>,\n    },\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{\n        diff::{Patch, PatchAction, ReplacementNode},\n        node::LAttributeValue,\n        LNode,\n    };\n\n    #[test]\n    fn patches_text() {\n        let a = LNode::Text(\"foo\".into());\n        let b = LNode::Text(\"bar\".into());\n        let delta = a.diff(&b);\n        assert_eq!(\n            delta,\n            vec![Patch {\n                path: vec![],\n                action: PatchAction::SetText(\"bar\".into())\n            }]\n        );\n    }\n\n    #[test]\n    fn patches_attrs() {\n        let a = LNode::Element {\n            name: \"button\".into(),\n            attrs: vec![\n                (\"class\".into(), LAttributeValue::Static(\"a\".into())),\n                (\"type\".into(), LAttributeValue::Static(\"button\".into())),\n            ],\n            children: vec![],\n        };\n        let b = LNode::Element {\n            name: \"button\".into(),\n            attrs: vec![\n                (\"class\".into(), LAttributeValue::Static(\"a b\".into())),\n                (\"id\".into(), LAttributeValue::Static(\"button\".into())),\n            ],\n            children: vec![],\n        };\n        let delta = a.diff(&b);\n        assert_eq!(\n            delta,\n            vec![\n                Patch {\n                    path: vec![],\n                    action: PatchAction::SetAttribute(\n                        \"class\".into(),\n                        \"a b\".into()\n                    )\n                },\n                Patch {\n                    path: vec![],\n                    action: PatchAction::SetAttribute(\n                        \"id\".into(),\n                        \"button\".into()\n                    )\n                },\n                Patch {\n                    path: vec![],\n                    action: PatchAction::RemoveAttribute(\"type\".into())\n                },\n            ]\n        );\n    }\n\n    #[test]\n    fn patches_child_text() {\n        let a = LNode::Element {\n            name: \"button\".into(),\n            attrs: vec![],\n            children: vec![\n                LNode::Text(\"foo\".into()),\n                LNode::Text(\"bar\".into()),\n            ],\n        };\n        let b = LNode::Element {\n            name: \"button\".into(),\n            attrs: vec![],\n            children: vec![\n                LNode::Text(\"foo\".into()),\n                LNode::Text(\"baz\".into()),\n            ],\n        };\n        let delta = a.diff(&b);\n        assert_eq!(\n            delta,\n            vec![Patch {\n                path: vec![1],\n                action: PatchAction::SetText(\"baz\".into())\n            },]\n        );\n    }\n\n    #[test]\n    fn inserts_child() {\n        let a = LNode::Element {\n            name: \"div\".into(),\n            attrs: vec![],\n            children: vec![LNode::Element {\n                name: \"button\".into(),\n                attrs: vec![],\n                children: vec![LNode::Text(\"bar\".into())],\n            }],\n        };\n        let b = LNode::Element {\n            name: \"div\".into(),\n            attrs: vec![],\n            children: vec![\n                LNode::Element {\n                    name: \"button\".into(),\n                    attrs: vec![],\n                    children: vec![LNode::Text(\"foo\".into())],\n                },\n                LNode::Element {\n                    name: \"button\".into(),\n                    attrs: vec![],\n                    children: vec![LNode::Text(\"bar\".into())],\n                },\n            ],\n        };\n        let delta = a.diff(&b);\n        assert_eq!(\n            delta,\n            vec![Patch {\n                path: vec![],\n                action: PatchAction::InsertChild {\n                    before: 0,\n                    child: ReplacementNode::Element {\n                        name: \"button\".into(),\n                        attrs: vec![],\n                        children: vec![ReplacementNode::Html(\"foo\".into())]\n                    }\n                }\n            }]\n        );\n    }\n\n    #[test]\n    fn removes_child() {\n        let a = LNode::Element {\n            name: \"div\".into(),\n            attrs: vec![],\n            children: vec![\n                LNode::Element {\n                    name: \"button\".into(),\n                    attrs: vec![],\n                    children: vec![LNode::Text(\"foo\".into())],\n                },\n                LNode::Element {\n                    name: \"button\".into(),\n                    attrs: vec![],\n                    children: vec![LNode::Text(\"bar\".into())],\n                },\n            ],\n        };\n        let b = LNode::Element {\n            name: \"div\".into(),\n            attrs: vec![],\n            children: vec![LNode::Element {\n                name: \"button\".into(),\n                attrs: vec![],\n                children: vec![LNode::Text(\"foo\".into())],\n            }],\n        };\n        let delta = a.diff(&b);\n        assert_eq!(\n            delta,\n            vec![Patch {\n                path: vec![],\n                action: PatchAction::RemoveChild { at: 1 }\n            },]\n        );\n    }\n}\n"
  },
  {
    "path": "leptos_hot_reload/src/lib.rs",
    "content": "extern crate proc_macro;\n\nuse anyhow::Result;\nuse camino::Utf8PathBuf;\nuse diff::Patches;\nuse node::LNode;\nuse or_poisoned::OrPoisoned;\nuse serde::{Deserialize, Serialize};\nuse std::{\n    collections::HashMap,\n    fs::File,\n    io::Read,\n    path::{Path, PathBuf},\n    sync::{Arc, RwLock},\n};\nuse syn::{\n    spanned::Spanned,\n    visit::{self, Visit},\n    Macro,\n};\nuse walkdir::WalkDir;\n\npub mod diff;\npub mod node;\npub mod parsing;\n\npub const HOT_RELOAD_JS: &str = include_str!(\"patch.js\");\n\n#[derive(Debug, Clone, Default)]\npub struct ViewMacros {\n    // keyed by original location identifier\n    views: Arc<RwLock<HashMap<Utf8PathBuf, Vec<MacroInvocation>>>>,\n}\n\nimpl ViewMacros {\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// # Errors\n    ///\n    /// Will return `Err` if the path is not UTF-8 path or the contents of the file cannot be parsed.\n    pub fn update_from_paths<T: AsRef<Path>>(&self, paths: &[T]) -> Result<()> {\n        let mut views = HashMap::new();\n\n        for path in paths {\n            for entry in WalkDir::new(path).into_iter().flatten() {\n                if entry.file_type().is_file() {\n                    let path: PathBuf = entry.path().into();\n                    let path = Utf8PathBuf::try_from(path)?;\n                    if path.extension() == Some(\"rs\") || path.ends_with(\".rs\") {\n                        let macros = Self::parse_file(&path)?;\n                        let entry = views.entry(path.clone()).or_default();\n                        *entry = macros;\n                    }\n                }\n            }\n        }\n\n        *self.views.write().or_poisoned() = views;\n\n        Ok(())\n    }\n\n    /// # Errors\n    ///\n    /// Will return `Err` if the contents of the file cannot be parsed.\n    pub fn parse_file(path: &Utf8PathBuf) -> Result<Vec<MacroInvocation>> {\n        let mut file = File::open(path)?;\n        let mut content = String::new();\n        file.read_to_string(&mut content)?;\n        let ast = syn::parse_file(&content)?;\n\n        let mut visitor = ViewMacroVisitor::default();\n        visitor.visit_file(&ast);\n        let mut views = Vec::new();\n        for view in visitor.views {\n            let span = view.span();\n            let id = span_to_stable_id(path, span.start().line);\n            if view.tokens.is_empty() {\n                views.push(MacroInvocation {\n                    id,\n                    template: LNode::Fragment(Vec::new()),\n                });\n            } else {\n                let tokens = view.tokens.clone().into_iter();\n                // TODO handle class = ...\n                let rsx = rstml::parse2(\n                    tokens.collect::<proc_macro2::TokenStream>(),\n                )?;\n                let template = LNode::parse_view(rsx)?;\n                views.push(MacroInvocation { id, template });\n            }\n        }\n        Ok(views)\n    }\n\n    /// # Errors\n    ///\n    /// Will return `Err` if the contents of the file cannot be parsed.\n    pub fn patch(&self, path: &Utf8PathBuf) -> Result<Option<Patches>> {\n        let new_views = Self::parse_file(path)?;\n        let mut lock = self.views.write().or_poisoned();\n        let diffs = match lock.get(path) {\n            None => return Ok(None),\n            Some(current_views) => {\n                if current_views.len() == new_views.len() {\n                    let mut diffs = Vec::new();\n                    for (current_view, new_view) in\n                        current_views.iter().zip(&new_views)\n                    {\n                        if current_view.id == new_view.id\n                            && current_view.template != new_view.template\n                        {\n                            diffs.push((\n                                current_view.id.clone(),\n                                current_view.template.diff(&new_view.template),\n                            ));\n                        }\n                    }\n                    diffs\n                } else {\n                    // TODO: instead of simply returning no patches, when number of views differs,\n                    // we can compare views content to determine which views were shifted\n                    // or come up with another idea that will allow to send patches when views were shifted/removed/added\n                    lock.insert(path.clone(), new_views);\n                    return Ok(None);\n                }\n            }\n        };\n\n        // update the status to the new views\n        lock.insert(path.clone(), new_views);\n\n        Ok(Some(Patches(diffs)))\n    }\n}\n\n#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\npub struct MacroInvocation {\n    id: String,\n    template: LNode,\n}\n\nimpl core::fmt::Debug for MacroInvocation {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"MacroInvocation\")\n            .field(\"id\", &self.id)\n            .finish_non_exhaustive()\n    }\n}\n\n#[derive(Default, Debug)]\npub struct ViewMacroVisitor<'a> {\n    views: Vec<&'a Macro>,\n}\n\nimpl<'ast> Visit<'ast> for ViewMacroVisitor<'ast> {\n    fn visit_macro(&mut self, node: &'ast Macro) {\n        let ident = node.path.get_ident().map(ToString::to_string);\n        if ident == Some(\"view\".to_string()) {\n            self.views.push(node);\n        }\n\n        // Delegate to the default impl to visit any nested functions.\n        visit::visit_macro(self, node);\n    }\n}\n\npub fn span_to_stable_id(path: impl AsRef<Path>, line: usize) -> String {\n    let file = path\n        .as_ref()\n        .to_str()\n        .unwrap_or_default()\n        .replace(['/', '\\\\'], \"-\");\n    format!(\"{file}-{line}\")\n}\n"
  },
  {
    "path": "leptos_hot_reload/src/node.rs",
    "content": "use crate::parsing::is_component_node;\nuse anyhow::Result;\nuse quote::ToTokens;\nuse rstml::node::{Node, NodeAttribute};\nuse serde::{Deserialize, Serialize};\n\n// A lightweight virtual DOM structure we can use to hold\n// the state of a Leptos view macro template. This is because\n// `syn` types are `!Send` so we can't store them as we might like.\n// This is only used to diff view macros for hot reloading so it's very minimal\n// and ignores many of the data types.\n#[allow(clippy::module_name_repetitions)]\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\npub enum LNode {\n    Fragment(Vec<LNode>),\n    Text(String),\n    Element {\n        name: String,\n        attrs: Vec<(String, LAttributeValue)>,\n        children: Vec<LNode>,\n    },\n    // don't need anything; skipped during patching because it should\n    // contain its own view macros\n    Component {\n        name: String,\n        props: Vec<(String, String)>,\n        children: Vec<LNode>,\n    },\n    DynChild(String),\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\npub enum LAttributeValue {\n    Boolean,\n    Static(String),\n    // safely ignored\n    Dynamic,\n    Noop,\n}\n\nimpl LNode {\n    /// # Errors\n    ///\n    /// Will return `Err` if parsing the view fails.\n    pub fn parse_view(nodes: Vec<Node>) -> Result<LNode> {\n        let mut out = Vec::new();\n        for node in nodes {\n            LNode::parse_node(node, &mut out)?;\n        }\n        if out.len() == 1 {\n            out.pop().ok_or_else(|| {\n                unreachable!(\"The last element should not be None.\")\n            })\n        } else {\n            Ok(LNode::Fragment(out))\n        }\n    }\n\n    /// # Errors\n    ///\n    /// Will return `Err` if parsing the node fails.\n    pub fn parse_node(node: Node, views: &mut Vec<LNode>) -> Result<()> {\n        match node {\n            Node::Fragment(frag) => {\n                for child in frag.children {\n                    LNode::parse_node(child, views)?;\n                }\n            }\n            Node::RawText(text) => {\n                views.push(LNode::Text(text.to_string_best()));\n            }\n            Node::Text(text) => {\n                views.push(LNode::Text(text.value_string()));\n            }\n            Node::Block(block) => {\n                views.push(LNode::DynChild(\n                    block.into_token_stream().to_string(),\n                ));\n            }\n            Node::Element(el) => {\n                if is_component_node(&el) {\n                    let name = el.name().to_string();\n                    let mut children = Vec::new();\n                    for child in el.children {\n                        LNode::parse_node(child, &mut children)?;\n                    }\n                    views.push(LNode::Component {\n                        name,\n                        props: el\n                            .open_tag\n                            .attributes\n                            .into_iter()\n                            .filter_map(|attr| match attr {\n                                NodeAttribute::Attribute(attr) => Some((\n                                    attr.key.to_string(),\n                                    format!(\"{:#?}\", attr.value()),\n                                )),\n                                NodeAttribute::Block(_) => None,\n                            })\n                            .collect(),\n                        children,\n                    });\n                } else {\n                    let name = el.name().to_string();\n                    let mut attrs = Vec::new();\n\n                    for attr in el.open_tag.attributes {\n                        if let NodeAttribute::Attribute(attr) = attr {\n                            let name = attr.key.to_string();\n                            if let Some(value) = attr.value_literal_string() {\n                                attrs.push((\n                                    name,\n                                    LAttributeValue::Static(value),\n                                ));\n                            } else {\n                                attrs.push((name, LAttributeValue::Dynamic));\n                            }\n                        }\n                    }\n\n                    let mut children = Vec::new();\n                    for child in el.children {\n                        LNode::parse_node(child, &mut children)?;\n                    }\n\n                    views.push(LNode::Element {\n                        name,\n                        attrs,\n                        children,\n                    });\n                }\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    pub fn to_html(&self) -> String {\n        match self {\n            LNode::Fragment(frag) => frag.iter().map(LNode::to_html).collect(),\n            LNode::Text(text) => text.to_owned(),\n            LNode::Component { name, .. } => format!(\n                \"<!--<{name}>--><pre>&lt;{name}/&gt; will load once Rust code \\\n                 has been compiled.</pre><!--</{name}>-->\"\n            ),\n            LNode::DynChild(_) => \"<!--<DynChild>--><pre>Dynamic content will \\\n                                   load once Rust code has been \\\n                                   compiled.</pre><!--</DynChild>-->\"\n                .to_string(),\n            LNode::Element {\n                name,\n                attrs,\n                children,\n            } => {\n                // this is naughty, but the browsers are tough and can handle it\n                // I wouldn't do this for real code, but this is just for dev mode\n                let is_self_closing = children.is_empty();\n\n                let attrs = attrs\n                    .iter()\n                    .filter_map(|(name, value)| match value {\n                        LAttributeValue::Boolean => Some(format!(\"{name} \")),\n                        LAttributeValue::Static(value) => {\n                            Some(format!(\"{name}=\\\"{value}\\\" \"))\n                        }\n                        LAttributeValue::Dynamic | LAttributeValue::Noop => {\n                            None\n                        }\n                    })\n                    .collect::<String>();\n\n                let children =\n                    children.iter().map(LNode::to_html).collect::<String>();\n\n                if is_self_closing {\n                    format!(\"<{name} {attrs}/>\")\n                } else {\n                    format!(\"<{name} {attrs}>{children}</{name}>\")\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "leptos_hot_reload/src/parsing.rs",
    "content": "use rstml::node::{CustomNode, NodeElement, NodeName};\n\n/// Converts `syn::Block` to simple expression\n///\n/// For example:\n/// ```no_build\n/// // \"string literal\" in\n/// {\"string literal\"}\n/// // number literal\n/// {0x12}\n/// // boolean literal\n/// {true}\n/// // variable\n/// {path::x}\n/// ```\n#[must_use]\npub fn block_to_primitive_expression(block: &syn::Block) -> Option<&syn::Expr> {\n    // its empty block, or block with multi lines\n    if block.stmts.len() != 1 {\n        return None;\n    }\n    match &block.stmts[0] {\n        syn::Stmt::Expr(e, None) => Some(e),\n        _ => None,\n    }\n}\n\n/// Converts simple literals to its string representation.\n///\n/// This function doesn't convert literal wrapped inside block\n/// like: `{\"string\"}`.\n#[must_use]\npub fn value_to_string(value: &syn::Expr) -> Option<String> {\n    match &value {\n        syn::Expr::Lit(lit) => match &lit.lit {\n            syn::Lit::Str(s) => Some(s.value()),\n            syn::Lit::Char(c) => Some(c.value().to_string()),\n            syn::Lit::Int(i) => Some(i.base10_digits().to_string()),\n            syn::Lit::Float(f) => Some(f.base10_digits().to_string()),\n            _ => None,\n        },\n        _ => None,\n    }\n}\n\n/// # Panics\n///\n/// Will panic if the last element does not exist in the path.\n#[must_use]\npub fn is_component_tag_name(name: &NodeName) -> bool {\n    match name {\n        NodeName::Path(path) => {\n            !path.path.segments.is_empty()\n                && path\n                    .path\n                    .segments\n                    .last()\n                    .unwrap()\n                    .ident\n                    .to_string()\n                    .starts_with(|c: char| c.is_ascii_uppercase())\n        }\n        NodeName::Block(_) | NodeName::Punctuated(_) => false,\n    }\n}\n\n#[must_use]\npub fn is_component_node(node: &NodeElement<impl CustomNode>) -> bool {\n    is_component_tag_name(node.name())\n}\n"
  },
  {
    "path": "leptos_hot_reload/src/patch.js",
    "content": "console.log(\"[HOT RELOADING] Connected to server.\");\nfunction patch(json) {\n  try {\n    const views = JSON.parse(json);\n    for (const [id, patches] of views) {\n      console.log(\"[HOT RELOAD]\", id, patches);\n      const walker = document.createTreeWalker(\n          document.body,\n          NodeFilter.SHOW_COMMENT,\n        ),\n        open = `hot-reload|${id}|open`,\n        close = `hot-reload|${id}|close`;\n      let start, end;\n      const instances = [];\n      while (walker.nextNode()) {\n        if (walker.currentNode.textContent == open) {\n          start = walker.currentNode;\n        } else if (walker.currentNode.textContent == close) {\n          end = walker.currentNode;\n          instances.push([start, end]);\n          start = undefined;\n          end = undefined;\n        }\n      }\n\n      for (const [start, end] of instances) {\n        for (const patch of patches) {\n          const actualChildren = childrenFromRange(\n            start.parentElement,\n            start,\n            end,\n          );\n          const child = childAtPath(\n            actualChildren.length > 1\n              ? { children: actualChildren }\n              : actualChildren[0],\n            patch.path,\n          );\n          const action = patch.action;\n          if (action == \"ClearChildren\") {\n            console.log(\"[HOT RELOAD] > ClearChildren\", child.node);\n            if (child.node) {\n              child.node.textContent = \"\";\n            } else {\n              for (const existingChild of child.children) {\n                let parent = existingChild.node.parentElement;\n                parent.removeChild(existingChild.node);\n              }\n            }\n          } else if (action.ReplaceWith) {\n            console.log(\n              \"[HOT RELOAD] > ReplaceWith\",\n              child,\n              action.ReplaceWith,\n            );\n            const replacement = fromReplacementNode(\n              action.ReplaceWith,\n              actualChildren,\n            );\n            if (child.node) {\n              child.node.replaceWith(replacement);\n            } else {\n              if (child.children) {\n                child.children[0].node.parentElement.insertBefore(\n                  replacement,\n                  child.children[0].node,\n                );\n                for (const existingChild of child.children) {\n                  existingChild.node.parentElement.removeChild(\n                    existingChild.node,\n                  );\n                }\n              }\n            }\n          } else if (action.ChangeTagName) {\n            const oldNode = child.node;\n            console.log(\n              \"[HOT RELOAD] > ChangeTagName\",\n              child.node,\n              action.ChangeTagName,\n            );\n            const newElement = document.createElement(action.ChangeTagName);\n            for (const attr of oldNode.attributes) {\n              newElement.setAttribute(attr.name, attr.value);\n            }\n            for (const childNode of child.node.childNodes) {\n              newElement.appendChild(childNode);\n            }\n\n            child.node.replaceWith(newElement);\n          } else if (action.RemoveAttribute) {\n            console.log(\n              \"[HOT RELOAD] > RemoveAttribute\",\n              child.node,\n              action.RemoveAttribute,\n            );\n            child.node.removeAttribute(action.RemoveAttribute);\n          } else if (action.SetAttribute) {\n            const [name, value] = action.SetAttribute;\n            console.log(\n              \"[HOT RELOAD] > SetAttribute\",\n              child.node,\n              action.SetAttribute,\n            );\n            child.node.setAttribute(name, value);\n          } else if (action.SetText) {\n            const node = child.node;\n            console.log(\"[HOT RELOAD] > SetText\", child.node, action.SetText);\n            node.textContent = action.SetText;\n          } else if (action.AppendChildren) {\n            console.log(\n              \"[HOT RELOAD] > AppendChildren\",\n              child.node,\n              action.AppendChildren,\n            );\n            const newChildren = action.AppendChildren.map((x) =>\n              fromReplacementNode(x, actualChildren),\n            );\n            child.node.append(...newChildren);\n          } else if (action.RemoveChild) {\n            console.log(\n              \"[HOT RELOAD] > RemoveChild\",\n              child.node,\n              child.children,\n              action.RemoveChild,\n            );\n            const toRemove = child.children[action.RemoveChild.at];\n            let toRemoveNode = toRemove.node;\n            if (!toRemoveNode) {\n              const range = new Range();\n              range.setStartBefore(toRemove.start);\n              range.setEndAfter(toRemove.end);\n              toRemoveNode = range.deleteContents();\n            } else {\n              toRemoveNode.parentNode.removeChild(toRemoveNode);\n            }\n          } else if (action.InsertChild) {\n            const newChild = fromReplacementNode(\n              action.InsertChild.child,\n              actualChildren,\n            );\n            let children = [];\n            if (child.children) {\n              children = child.children;\n            } else if (child.start && child.end) {\n              children = childrenFromRange(\n                child.node || child.start.parentElement,\n                start,\n                end,\n              );\n            } else {\n              console.warn(\"InsertChildAfter could not build children.\");\n            }\n            const beforeNode = children[action.InsertChild.before];\n            console.log(\n              \"[HOT RELOAD] > InsertChild\",\n              child,\n              child.node,\n              action.InsertChild,\n              \" before \",\n              beforeNode,\n            );\n            if (beforeNode) {\n              let node = beforeNode.node || beforeNode.start.previousSibling;\n              node.parentElement.insertBefore(newChild, node);\n            } else if (child.node) {\n              child.node.appendChild(newChild);\n            } else if (children) {\n              let lastNode = children[children.length - 1];\n              let afterNode = lastNode.node || lastNode.end.nextSibling;\n              afterNode.after(newChild);\n            }\n          } else if (action.InsertChildAfter) {\n            const newChild = fromReplacementNode(\n              action.InsertChildAfter.child,\n              actualChildren,\n            );\n            let children = [];\n            if (child.children) {\n              children = child.children;\n            } else if (child.start && child.end) {\n              children = childrenFromRange(\n                child.node || child.start.parentElement,\n                start,\n                end,\n              );\n            } else {\n              console.warn(\"InsertChildAfter could not build children.\");\n            }\n            const after = children[action.InsertChildAfter.after];\n            console.log(\n              \"[HOT RELOAD] > InsertChildAfter\",\n              child,\n              child.node,\n              action.InsertChildAfter,\n              \" after \",\n              after,\n            );\n            if (\n              child.node &&\n              (!after || !(after.node || after.start).nextSibling)\n            ) {\n              child.node.appendChild(newChild);\n            } else {\n              const node = child.node || child.end;\n              const parent =\n                node.nodeType === Node.COMMENT_NODE ? node.parentNode : node;\n              if (!after) {\n                parent.appendChild(newChild);\n              } else {\n                parent.insertBefore(\n                  newChild,\n                  (after.node || after.start).nextSibling,\n                );\n              }\n            }\n          } else {\n            console.warn(\"[HOT RELOADING] Unmatched action\", action);\n          }\n        }\n      }\n    }\n  } catch (e) {\n    console.warn(\"[HOT RELOADING] Error: \", e);\n  }\n\n  function fromReplacementNode(node, actualChildren) {\n    if (node.Html) {\n      return fromHTML(node.Html);\n    } else if (node.Fragment) {\n      const frag = document.createDocumentFragment();\n      for (const child of node.Fragment) {\n        frag.appendChild(fromReplacementNode(child, actualChildren));\n      }\n      return frag;\n    } else if (node.Element) {\n      const element = document.createElement(node.Element.name);\n      for (const [name, value] of node.Element.attrs) {\n        element.setAttribute(name, value);\n      }\n      for (const child of node.Element.children) {\n        element.appendChild(fromReplacementNode(child, actualChildren));\n      }\n      return element;\n    } else {\n      const child = childAtPath(\n        actualChildren.length > 1\n          ? { children: actualChildren }\n          : actualChildren[0],\n        node.Path,\n      );\n      if (child) {\n        let childNode = child.node;\n        if (!childNode) {\n          const range = new Range();\n          range.setStartBefore(child.start);\n          range.setEndAfter(child.end);\n          // okay this is somewhat silly\n          // if we do cloneContents() here to return it,\n          // we strip away the event listeners\n          // if we're moving just one object, this is less than ideal\n          // so I'm actually going to *extract* them, then clone and reinsert\n          /* const toReinsert = range.cloneContents();\n\t\t\t\t\tif (child.end.nextSibling) {\n\t\t\t\t\t\tchild.end.parentNode.insertBefore(toReinsert, child.end.nextSibling);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchild.end.parentNode.appendChild(toReinsert);\n\t\t\t\t\t} */\n          childNode = range.cloneContents();\n        }\n        return childNode;\n      } else {\n        console.warn(\n          \"[HOT RELOADING] Could not find replacement node at \",\n          node.Path,\n        );\n        return undefined;\n      }\n    }\n  }\n\n  function buildActualChildren(element, range) {\n    const walker = document.createTreeWalker(\n      element,\n      NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT,\n      {\n        acceptNode(node) {\n          if (\n            node.parentNode == element &&\n            (!range || range.isPointInRange(node, 0))\n          ) {\n            return NodeFilter.FILTER_ACCEPT;\n          } else {\n            return NodeFilter.FILTER_REJECT;\n          }\n        },\n      },\n    );\n    const actualChildren = [],\n      elementCount = {};\n    while (walker.nextNode()) {\n      if (walker.currentNode.nodeType == Node.ELEMENT_NODE) {\n        if (elementCount[walker.currentNode.nodeName]) {\n          elementCount[walker.currentNode.nodeName] += 1;\n        } else {\n          elementCount[walker.currentNode.nodeName] = 0;\n        }\n        elementCount[walker.currentNode.nodeName];\n\n        actualChildren.push({\n          type: \"element\",\n          name: walker.currentNode.nodeName,\n          number: elementCount[walker.currentNode.nodeName],\n          node: walker.currentNode,\n          children: buildActualChildren(walker.currentNode),\n        });\n      } else if (walker.currentNode.nodeType == Node.TEXT_NODE) {\n        actualChildren.push({\n          type: \"text\",\n          node: walker.currentNode,\n        });\n      } else if (walker.currentNode.nodeType == Node.COMMENT_NODE) {\n        if (walker.currentNode.textContent.trim().startsWith(\"hot-reload|\")) {\n          if (walker.currentNode.textContent.trim().endsWith(\"|open\")) {\n            const startingName = walker.currentNode.textContent.trim();\n            const componentName = startingName\n              .replace(\"|open\", \"\")\n              .replace(\"hot-reload|\", \"\");\n            const endingName = `hot-reload|${componentName}|close`;\n            let start = walker.currentNode;\n            let depth = 1;\n\n            while (walker.nextNode()) {\n              if (walker.currentNode.textContent.trim() == endingName) {\n                depth--;\n              } else if (\n                walker.currentNode.textContent.trim() == startingName\n              ) {\n                depth++;\n              }\n\n              if (depth == 0) {\n                break;\n              }\n            }\n            let end = walker.currentNode;\n            actualChildren.push({\n              type: \"fragment\",\n              start: start.nextSibling,\n              end: end.previousSibling,\n              children: childrenFromRange(\n                start.parentElement,\n                start.nextSibling,\n                end.previousSibling,\n              ),\n            });\n          }\n        } else if (walker.currentNode.textContent.trim() == \"<() />\") {\n          actualChildren.push({\n            type: \"unit\",\n            node: walker.currentNode,\n          });\n        } else if (walker.currentNode.textContent.trim() == \"<DynChild>\") {\n          let start = walker.currentNode;\n          let depth = 1;\n\n          while (walker.nextNode()) {\n            if (walker.currentNode.textContent.trim() == \"</DynChild>\") {\n              depth--;\n            } else if (walker.currentNode.textContent.trim() == \"<DynChild>\") {\n              depth++;\n            }\n\n            if (depth == 0) {\n              break;\n            }\n          }\n          let end = walker.currentNode;\n          actualChildren.push({\n            type: \"dyn-child\",\n            start,\n            end,\n          });\n        } else if (walker.currentNode.textContent.trim() == \"<>\") {\n          let start = walker.currentNode;\n          let depth = 1;\n\n          while (walker.nextNode()) {\n            if (walker.currentNode.textContent.trim() == \"</>\") {\n              depth--;\n            } else if (walker.currentNode.textContent.trim() == \"<>\") {\n              depth++;\n            }\n\n            if (depth == 0) {\n              break;\n            }\n          }\n          let end = walker.currentNode;\n          actualChildren.push({\n            type: \"fragment\",\n            children: childrenFromRange(start.parentElement, start, end),\n            start,\n            end,\n          });\n        } else if (walker.currentNode.textContent.trim().startsWith(\"<\")) {\n          let componentName = walker.currentNode.textContent.trim();\n          let endMarker = componentName.replace(\"<\", \"</\");\n          let depth = 1;\n          let start = walker.currentNode;\n          while (walker.nextNode()) {\n            if (walker.currentNode.textContent.trim() == endMarker) {\n              depth--;\n            } else if (walker.currentNode.textContent.trim() == componentName) {\n              depth++;\n            }\n\n            if (depth == 0) {\n              break;\n            }\n          }\n          let end = walker.currentNode;\n          actualChildren.push({\n            type: \"component\",\n            start,\n            end,\n          });\n        }\n      } else {\n        console.warn(\n          \"[HOT RELOADING] Building children, encountered\",\n          walker.currentNode,\n        );\n      }\n    }\n    return actualChildren;\n  }\n\n  function childAtPath(element, path) {\n    if (path.length == 0) {\n      return element;\n    } else if (element.children) {\n      const next = element.children[path[0]],\n        rest = path.slice(1);\n      return childAtPath(next, rest);\n    } else if (path == [0]) {\n      return element;\n    } else if (element.start && element.end) {\n      const actualChildren = childrenFromRange(\n        element.node || element.start.parentElement,\n        element.start,\n        element.end,\n      );\n      return childAtPath({ children: actualChildren }, path);\n    } else {\n      console.warn(\"[HOT RELOADING] Child at \", path, \"not found in \", element);\n      return element;\n    }\n  }\n\n  function childrenFromRange(parent, start, end) {\n    const range = new Range();\n    range.setStartAfter(start);\n    range.setEndBefore(end);\n    return buildActualChildren(parent, range);\n  }\n\n  function fromHTML(html) {\n    const template = document.createElement(\"template\");\n    template.innerHTML = html;\n    return template.content.cloneNode(true);\n  }\n}\n"
  },
  {
    "path": "leptos_macro/.gitignore",
    "content": "target\nCargo.lock"
  },
  {
    "path": "leptos_macro/Cargo.toml",
    "content": "[package]\nname = \"leptos_macro\"\nversion = \"0.8.15\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"view macro for the Leptos web framework.\"\nreadme = \"../README.md\"\nrust-version.workspace = true\nedition.workspace = true\n\n[lib]\nproc-macro = true\n\n[dependencies]\nattribute-derive = { features = [\n  \"syn-full\",\n], workspace = true, default-features = true }\ncfg-if = { workspace = true, default-features = true }\nhtml-escape = { workspace = true, default-features = true }\nitertools = { workspace = true, default-features = true }\nprettyplease = { workspace = true, default-features = true }\nproc-macro-error2 = { default-features = false, workspace = true }\nproc-macro2 = { workspace = true, default-features = true }\nquote = { workspace = true, default-features = true }\nsyn = { features = [\"full\"], workspace = true, default-features = true }\nrstml = { workspace = true, default-features = true }\nleptos_hot_reload = { workspace = true }\nserver_fn_macro = { workspace = true }\nconvert_case = { workspace = true, default-features = true }\nconvert_case_extras = { workspace = true, default-features = true }\nuuid = { features = [\"v4\"], workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\n\n[dev-dependencies]\nlog = { workspace = true, default-features = true }\ntyped-builder = { workspace = true, default-features = true }\ntrybuild = { workspace = true, default-features = true }\nleptos = { path = \"../leptos\" }\nleptos_router = { path = \"../router\", features = [\"ssr\"] }\nserver_fn = { path = \"../server_fn\", features = [\"cbor\"] }\ninsta = { workspace = true, default-features = true }\nserde = { workspace = true, default-features = true }\n\n[build-dependencies]\nrustc_version = { workspace = true, default-features = true }\n\n[features]\ncsr = []\nhydrate = []\nssr = [\"server_fn_macro/ssr\"]\nnightly = [\"server_fn_macro/nightly\"]\ntracing = [\"dep:tracing\"]\nislands = []\ntrace-components = []\ntrace-component-props = []\nactix = [\"server_fn_macro/actix\"]\naxum = [\"server_fn_macro/axum\"]\ngeneric = [\"server_fn_macro/generic\"]\n# Having an erasure feature rather than normal --cfg erase_components for the proc macro crate is a workaround for this rust issue:\n# https://github.com/rust-lang/cargo/issues/4423\n# TLDR proc macros will ignore RUSTFLAGS when --target is specified on the cargo command.\n# This works around the issue by the non proc-macro crate which does see RUSTFLAGS enabling the replacement feature on the proc-macro crate, which wouldn't.\n# This is automatic as long as the leptos crate is depended upon,\n# downstream usage should never manually enable this feature.\n__internal_erase_components = []\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\", \"trace-component-props\", \"trace-components\"]\nskip_feature_sets = [\n  [\n    \"csr\",\n    \"hydrate\",\n  ],\n  [\n    \"hydrate\",\n    \"csr\",\n  ],\n  [\n    \"hydrate\",\n    \"ssr\",\n  ],\n  [\n    \"actix\",\n    \"axum\",\n  ],\n  [\n    \"actix\",\n    \"generic\",\n  ],\n  [\n    \"generic\",\n    \"axum\",\n  ],\n  [\n    \"nightly\",\n  ],\n]\nmax_combination_size = 2\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(rustc_nightly)'] }\n"
  },
  {
    "path": "leptos_macro/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n\n[tasks.test]\nclear = true\ndependencies = [\n  \"test-each-feature\",\n  \"test-leptos_macro-example\",\n  \"doc-leptos_macro-example\",\n]\n\n[tasks.test-leptos_macro-example]\ndescription = \"Tests the leptos_macro/example to check if macro handles doc comments correctly\"\ncommand = \"cargo\"\nargs = [\"test\", \"--doc\"]\ncwd = \"example\"\ninstall_crate = false\n\n[tasks.doc-leptos_macro-example]\ndescription = \"Docs the leptos_macro/example to check if macro handles doc comments correctly\"\ncommand = \"cargo\"\nargs = [\"doc\"]\ncwd = \"example\"\ninstall_crate = false\n"
  },
  {
    "path": "leptos_macro/build.rs",
    "content": "use rustc_version::{version_meta, Channel};\n\nfn main() {\n    // Set cfg flags depending on release channel\n    if matches!(version_meta().unwrap().channel, Channel::Nightly) {\n        println!(\"cargo:rustc-cfg=rustc_nightly\");\n    }\n}\n"
  },
  {
    "path": "leptos_macro/example/Cargo.toml",
    "content": "[package]\nname = \"example\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nleptos.path = \"../../leptos\"\n\n[workspace]\n"
  },
  {
    "path": "leptos_macro/example/src/lib.rs",
    "content": "use leptos::prelude::*;\n\n#[component]\npub fn TestComponent(\n    /// Rust code\n    /// ```\n    /// assert_eq!(\"hello\", stringify!(hello));\n    /// ```\n    /// View containing rust code\n    /// ```view\n    /// assert!(true);\n    /// ```\n    /// View containing rsx\n    /// ```view\n    /// # use example::TestComponent;\n    /// <TestComponent key=\"hello\"/>\n    /// ```\n    /// View containing rsx\n    /// ```view compile_fail\n    /// # use example::TestComponent;\n    /// <TestComponent/>\n    /// ```\n    #[prop(into)]\n    key: String,\n    /// rsx unclosed\n    /// ```view\n    /// # use example::TestComponent;\n    /// <TestComponent key=\"hello\"/>\n    #[prop(optional)]\n    another: usize,\n    /// rust unclosed\n    /// ```view\n    /// use example::TestComponent;\n    #[prop(optional)]\n    and_another: usize,\n) -> impl IntoView {\n    _ = (key, another, and_another);\n}\n\n#[component]\npub fn TestMutCallback<F>(mut callback: F, value: &'static str) -> impl IntoView\nwhere\n    F: FnMut(u32) + 'static,\n{\n    let value = value.to_owned();\n    view! {\n        <button on:click=move |_| {\n            callback(5);\n        }>{value}</button>\n        <TestComponent key=\"test\"/>\n    }\n}\n"
  },
  {
    "path": "leptos_macro/src/component.rs",
    "content": "use attribute_derive::FromAttr;\nuse convert_case::{\n    Case::{Pascal, Snake},\n    Casing,\n};\nuse convert_case_extras::is_case;\nuse itertools::Itertools;\nuse leptos_hot_reload::parsing::value_to_string;\nuse proc_macro2::{Ident, Span, TokenStream};\nuse proc_macro_error2::abort;\nuse quote::{format_ident, quote, quote_spanned, ToTokens, TokenStreamExt};\nuse std::hash::DefaultHasher;\nuse syn::{\n    parse::Parse, parse_quote, spanned::Spanned, token::Colon,\n    visit_mut::VisitMut, AngleBracketedGenericArguments, Attribute, FnArg,\n    GenericArgument, GenericParam, Item, ItemFn, LitStr, Meta, Pat, PatIdent,\n    Path, PathArguments, ReturnType, Signature, Stmt, Type, TypeImplTrait,\n    TypeParam, TypePath, Visibility,\n};\n\npub struct Model {\n    is_transparent: bool,\n    is_lazy: bool,\n    island: Option<String>,\n    docs: Docs,\n    unknown_attrs: UnknownAttrs,\n    vis: Visibility,\n    name: Ident,\n    props: Vec<Prop>,\n    body: ItemFn,\n    ret: ReturnType,\n}\n\nimpl Parse for Model {\n    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {\n        let mut item = ItemFn::parse(input)?;\n        maybe_modify_return_type(&mut item.sig.output);\n\n        convert_impl_trait_to_generic(&mut item.sig);\n\n        let docs = Docs::new(&item.attrs);\n        let unknown_attrs = UnknownAttrs::new(&item.attrs);\n\n        let props = item\n            .sig\n            .inputs\n            .clone()\n            .into_iter()\n            .map(Prop::new)\n            .collect::<Vec<_>>();\n\n        // We need to remove the `#[doc = \"\"]` and `#[builder(_)]`\n        // attrs from the function signature\n        drain_filter(&mut item.attrs, |attr| match &attr.meta {\n            Meta::NameValue(attr) => attr.path == parse_quote!(doc),\n            Meta::List(attr) => attr.path == parse_quote!(prop),\n            _ => false,\n        });\n        item.sig.inputs.iter_mut().for_each(|arg| {\n            if let FnArg::Typed(ty) = arg {\n                drain_filter(&mut ty.attrs, |attr| match &attr.meta {\n                    Meta::NameValue(attr) => attr.path == parse_quote!(doc),\n                    Meta::List(attr) => attr.path == parse_quote!(prop),\n                    _ => false,\n                });\n            }\n        });\n\n        Ok(Self {\n            is_transparent: false,\n            is_lazy: false,\n            island: None,\n            docs,\n            unknown_attrs,\n            vis: item.vis.clone(),\n            name: convert_from_snake_case(&item.sig.ident),\n            props,\n            ret: item.sig.output.clone(),\n            body: item,\n        })\n    }\n}\n\n/// Exists to fix nested routes defined in a separate component in erased mode,\n/// by replacing the return type with AnyNestedRoute, which is what it'll be, but is required as the return type for compiler inference.\nfn maybe_modify_return_type(ret: &mut ReturnType) {\n    #[cfg(feature = \"__internal_erase_components\")]\n    {\n        if let ReturnType::Type(_, ty) = ret {\n            if let Type::ImplTrait(TypeImplTrait { bounds, .. }) = ty.as_ref() {\n                // If one of the bounds is MatchNestedRoutes, we need to replace the return type with AnyNestedRoute:\n                if bounds.iter().any(|bound| {\n                    if let syn::TypeParamBound::Trait(trait_bound) = bound {\n                        if trait_bound.path.segments.iter().any(\n                            |path_segment| {\n                                path_segment.ident == \"MatchNestedRoutes\"\n                            },\n                        ) {\n                            return true;\n                        }\n                    }\n                    false\n                }) {\n                    *ty = parse_quote!(\n                        ::leptos_router::any_nested_route::AnyNestedRoute\n                    );\n                }\n            }\n        }\n    }\n    #[cfg(not(feature = \"__internal_erase_components\"))]\n    {\n        let _ = ret;\n    }\n}\n\n// implemented manually because Vec::drain_filter is nightly only\n// follows std recommended parallel\npub fn drain_filter<T>(\n    vec: &mut Vec<T>,\n    mut some_predicate: impl FnMut(&mut T) -> bool,\n) {\n    let mut i = 0;\n    while i < vec.len() {\n        if some_predicate(&mut vec[i]) {\n            _ = vec.remove(i);\n        } else {\n            i += 1;\n        }\n    }\n}\n\npub fn convert_from_snake_case(name: &Ident) -> Ident {\n    let name_str = name.to_string();\n    if !is_case(&name_str, Snake) {\n        name.clone()\n    } else {\n        Ident::new(&name_str.to_case(Pascal), name.span())\n    }\n}\n\nimpl ToTokens for Model {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        let Self {\n            is_transparent,\n            is_lazy,\n            island,\n            docs,\n            unknown_attrs,\n            vis,\n            name,\n            props,\n            body,\n            ret,\n        } = self;\n        let is_island = island.is_some();\n\n        let no_props = props.is_empty();\n\n        // check for components that end ;\n        if !is_transparent {\n            let ends_semi =\n                body.block.stmts.iter().last().and_then(|stmt| match stmt {\n                    Stmt::Item(Item::Macro(mac)) => mac.semi_token.as_ref(),\n                    _ => None,\n                });\n            if let Some(semi) = ends_semi {\n                proc_macro_error2::emit_error!(\n                    semi.span(),\n                    \"A component that ends with a `view!` macro followed by a \\\n                     semicolon will return (), an empty view. This is usually \\\n                     an accident, not intentional, so we prevent it. If you’d \\\n                     like to return (), you can do it it explicitly by \\\n                     returning () as the last item from the component.\"\n                );\n            }\n        }\n\n        //body.sig.ident = format_ident!(\"__{}\", body.sig.ident);\n        #[allow(clippy::redundant_clone)] // false positive\n        let body_name = body.sig.ident.clone();\n\n        let (impl_generics, generics, where_clause) =\n            body.sig.generics.split_for_impl();\n\n        let props_name = format_ident!(\"{name}Props\");\n        let props_builder_name = format_ident!(\"{name}PropsBuilder\");\n        let props_serialized_name = format_ident!(\"{name}PropsSerialized\");\n        #[cfg(feature = \"tracing\")]\n        let trace_name = format!(\"<{name} />\");\n\n        let is_island_with_children =\n            is_island && props.iter().any(|prop| prop.name.ident == \"children\");\n        let is_island_with_other_props = is_island\n            && ((is_island_with_children && props.len() > 1)\n                || (!is_island_with_children && !props.is_empty()));\n\n        let prop_builder_fields =\n            prop_builder_fields(vis, props, is_island_with_other_props);\n        let props_serializer = if is_island_with_other_props {\n            let fields = prop_serializer_fields(vis, props);\n            quote! {\n                #[derive(::leptos::serde::Deserialize)]\n                #vis struct #props_serialized_name {\n                    #fields\n                }\n            }\n        } else {\n            quote! {}\n        };\n\n        let prop_names = prop_names(props);\n\n        let builder_name_doc = LitStr::new(\n            &format!(\" Props for the [`{name}`] component.\"),\n            name.span(),\n        );\n\n        let component_fn_prop_docs = generate_component_fn_prop_docs(props);\n        let docs_and_prop_docs = if component_fn_prop_docs.is_empty() {\n            // Avoid generating an empty doc line in case the component has no doc and no props.\n            quote! {\n                #docs\n            }\n        } else {\n            quote! {\n                #docs\n                #[doc = \"\"]\n                #component_fn_prop_docs\n            }\n        };\n\n        let (\n            tracing_instrument_attr,\n            tracing_span_expr,\n            tracing_guard_expr,\n            tracing_props_expr,\n        ) = {\n            #[cfg(feature = \"tracing\")]\n            {\n                /* TODO for 0.8: fix this\n                 *\n                 * The problem is that cargo now warns about an expected \"tracing\" cfg if\n                 * you don't have a \"tracing\" feature in your actual crate\n                 *\n                 * However, until https://github.com/tokio-rs/tracing/pull/1819 is merged\n                 * (?), you can't provide an alternate path for `tracing` (for example,\n                 * ::leptos::tracing), which means that if you're going to use the macro\n                 * you *must* have `tracing` in your Cargo.toml.\n                 *\n                 * Including the feature-check here causes cargo warnings on\n                 * previously-working projects.\n                 *\n                 * Removing the feature-check here breaks any project that uses leptos with\n                 * the tracing feature turned on, but without a tracing dependency in its\n                 * Cargo.toml.\n                 * /\n                 */\n                let instrument = cfg!(feature = \"trace-components\").then(|| quote! {\n                    #[cfg_attr(\n                        feature = \"tracing\",\n                        ::leptos::tracing::instrument(level = \"info\", name = #trace_name, skip_all)\n                    )]\n                });\n\n                (\n                    quote! {\n                        #[allow(clippy::let_with_type_underscore)]\n                        #instrument\n                    },\n                    quote! {\n                        let __span = ::leptos::tracing::Span::current();\n                    },\n                    quote! {\n                        #[cfg(debug_assertions)]\n                        let _guard = __span.entered();\n                    },\n                    if no_props || !cfg!(feature = \"trace-component-props\") {\n                        quote!()\n                    } else {\n                        quote! {\n                            ::leptos::leptos_dom::tracing_props![#prop_names];\n                        }\n                    },\n                )\n            }\n\n            #[cfg(not(feature = \"tracing\"))]\n            {\n                (quote!(), quote!(), quote!(), quote!())\n            }\n        };\n\n        let component_id = name.to_string();\n        let hydrate_fn_name = is_island.then(|| {\n            use std::hash::{Hash, Hasher};\n\n            let mut hasher = DefaultHasher::new();\n            island.hash(&mut hasher);\n            let caller = hasher.finish() as usize;\n            Ident::new(&format!(\"{component_id}_{caller:?}\"), name.span())\n        });\n\n        let island_serialize_props = if is_island_with_other_props {\n            quote! {\n                let _leptos_ser_props = ::leptos::serde_json::to_string(&props).expect(\"couldn't serialize island props\");\n            }\n        } else {\n            quote! {}\n        };\n        let island_serialized_props = if is_island_with_other_props {\n            quote! {\n                .with_props( _leptos_ser_props)\n            }\n        } else {\n            quote! {}\n        };\n\n        let body_name = unmodified_fn_name_from_fn_name(&body_name);\n        let body_expr = if is_island {\n            quote! {\n                ::leptos::reactive::owner::Owner::new().with(|| {\n                    ::leptos::reactive::owner::Owner::with_hydration(move || {\n                        ::leptos::tachys::reactive_graph::OwnedView::new({\n                            #body_name(#prop_names)\n                        })\n                    })\n                })\n            }\n        } else {\n            quote! {\n                #body_name(#prop_names)\n            }\n        };\n\n        let component = if *is_transparent {\n            body_expr\n        } else if cfg!(feature = \"__internal_erase_components\") {\n            quote! {\n                ::leptos::prelude::IntoMaybeErased::into_maybe_erased(\n                    ::leptos::reactive::graph::untrack_with_diagnostics(\n                        move || {\n                            #tracing_guard_expr\n                            #tracing_props_expr\n                            #body_expr\n                        }\n                    )\n                )\n            }\n        } else {\n            quote! {\n                ::leptos::reactive::graph::untrack_with_diagnostics(\n                    move || {\n                        #tracing_guard_expr\n                        #tracing_props_expr\n                        #body_expr\n                    }\n                )\n            }\n        };\n\n        // add island wrapper if island\n        let component = if is_island {\n            let hydrate_fn_name = hydrate_fn_name.as_ref().unwrap();\n            quote! {\n                ::leptos::tachys::html::islands::Island::new(\n                    stringify!(#hydrate_fn_name),\n                    #component\n                )\n                #island_serialized_props\n            }\n        } else {\n            component\n        };\n\n        let props_arg = if no_props {\n            quote! {}\n        } else {\n            quote! {\n                props: #props_name #generics\n            }\n        };\n\n        let destructure_props = if no_props {\n            quote! {}\n        } else {\n            let wrapped_children = if is_island_with_children {\n                quote! {\n                    use leptos::tachys::view::any_view::IntoAny;\n                    let children = Box::new(|| {\n                        let sc = ::leptos::reactive::owner::Owner::current_shared_context().unwrap();\n                        let prev = sc.get_is_hydrating();\n                        let owner = ::leptos::reactive::owner::Owner::new();\n                        let value = owner.clone().with(|| {\n                            ::leptos::reactive::owner::Owner::with_no_hydration(move || {\n                                ::leptos::tachys::reactive_graph::OwnedView::new({\n                                    ::leptos::tachys::html::islands::IslandChildren::new_with_on_hydrate(\n                                        children(),\n                                        {\n                                            let owner = owner.clone();\n                                            move || {\n                                                owner.set()\n                                            }\n                                        }\n\n                                    )\n                                }).into_any()\n                            })\n                        });\n                        sc.set_is_hydrating(prev);\n                        value\n                    });\n                }\n            } else {\n                quote! {}\n            };\n            quote! {\n                #island_serialize_props\n                let #props_name {\n                    #prop_names\n                } = props;\n                #wrapped_children\n            }\n        };\n\n        let body = quote! {\n            #destructure_props\n            #tracing_span_expr\n            #component\n        };\n\n        let binding = if is_island {\n            let island_props = if is_island_with_children\n                || is_island_with_other_props\n            {\n                let (destructure, prop_builders, optional_props) =\n                    if is_island_with_other_props {\n                        let prop_names = props\n                            .iter()\n                            .filter_map(|prop| {\n                                if prop.name.ident == \"children\" {\n                                    None\n                                } else {\n                                    let name = &prop.name.ident;\n                                    Some(quote! { #name, })\n                                }\n                            })\n                            .collect::<TokenStream>();\n                        let destructure = quote! {\n                            let #props_serialized_name {\n                                #prop_names\n                            } = props;\n                        };\n                        let prop_builders = props\n                            .iter()\n                            .filter_map(|prop| {\n                                if prop.name.ident == \"children\"\n                                    || prop.prop_opts.optional\n                                {\n                                    None\n                                } else {\n                                    let name = &prop.name.ident;\n                                    Some(quote! {\n                                        .#name(#name)\n                                    })\n                                }\n                            })\n                            .collect::<TokenStream>();\n                        let optional_props = props\n                            .iter()\n                            .filter_map(|prop| {\n                                if prop.name.ident == \"children\"\n                                    || !prop.prop_opts.optional\n                                {\n                                    None\n                                } else {\n                                    let name = &prop.name.ident;\n                                    Some(quote! {\n                                        if let Some(#name) = #name {\n                                            props.#name = Some(#name)\n                                        }\n                                    })\n                                }\n                            })\n                            .collect::<TokenStream>();\n                        (destructure, prop_builders, optional_props)\n                    } else {\n                        (quote! {}, quote! {}, quote! {})\n                    };\n                let children = if is_island_with_children {\n                    quote! {\n                        .children({\n                            let owner = leptos::reactive::owner::Owner::current();\n                            Box::new(move || {\n                            use leptos::tachys::view::any_view::IntoAny;\n                            ::leptos::tachys::html::islands::IslandChildren::new_with_on_hydrate(\n                                (),\n                                {\n                                    let owner = owner.clone();\n                                    move || {\n                                        if let Some(owner) = &owner {\n                                            owner.set()\n                                        }\n                                    }\n                                }\n                            ).into_any()})})\n                    }\n                } else {\n                    quote! {}\n                };\n\n                quote! {{\n                    #destructure\n                    let mut props = #props_name::builder()\n                        #prop_builders\n                        #children\n                        .build();\n\n                    #optional_props\n\n                    props\n                }}\n            } else {\n                quote! {}\n            };\n            let deserialize_island_props = if is_island_with_other_props {\n                quote! {\n                    let props = el.dataset().get(::leptos::wasm_bindgen::intern(\"props\"))\n                        .and_then(|data| ::leptos::serde_json::from_str::<#props_serialized_name>(&data).ok())\n                        .expect(\"could not deserialize props\");\n                }\n            } else {\n                quote! {}\n            };\n\n            let hydrate_fn_name = hydrate_fn_name.as_ref().unwrap();\n\n            let hydrate_fn_inner = quote! {\n                #deserialize_island_props\n                let island = #name(#island_props);\n                let state = island.hydrate_from_position::<true>(&el, ::leptos::tachys::view::Position::Current);\n                // TODO better cleanup\n                std::mem::forget(state);\n            };\n            if *is_lazy {\n                let outer_name =\n                    Ident::new(&format!(\"{name}_loader\"), name.span());\n\n                quote! {\n                    #[::leptos::prelude::lazy]\n                    #[allow(non_snake_case)]\n                    fn #outer_name (el: ::leptos::web_sys::HtmlElement) {\n                        #hydrate_fn_inner\n                    }\n\n                    #[::leptos::wasm_bindgen::prelude::wasm_bindgen(\n                        wasm_bindgen = ::leptos::wasm_bindgen,\n                        wasm_bindgen_futures = ::leptos::__reexports::wasm_bindgen_futures\n                    )]\n                    #[allow(non_snake_case)]\n                    pub async fn #hydrate_fn_name(el: ::leptos::web_sys::HtmlElement) {\n                        #outer_name(el).await\n                    }\n                }\n            } else {\n                quote! {\n                    #[::leptos::wasm_bindgen::prelude::wasm_bindgen(wasm_bindgen = ::leptos::wasm_bindgen)]\n                    #[allow(non_snake_case)]\n                    pub fn #hydrate_fn_name(el: ::leptos::web_sys::HtmlElement) {\n                        #hydrate_fn_inner\n                    }\n                }\n            }\n        } else {\n            quote! {}\n        };\n\n        let props_derive_serialize = if is_island_with_other_props {\n            quote! { , ::leptos::serde::Serialize }\n        } else {\n            quote! {}\n        };\n\n        let output = quote! {\n            #[doc = #builder_name_doc]\n            #[doc = \"\"]\n            #docs_and_prop_docs\n            #[derive(::leptos::typed_builder_macro::TypedBuilder #props_derive_serialize)]\n            //#[builder(doc)]\n            #[builder(crate_module_path=::leptos::typed_builder)]\n            #[allow(non_snake_case)]\n            #vis struct #props_name #impl_generics #where_clause {\n                #prop_builder_fields\n            }\n\n            #props_serializer\n\n            #[allow(missing_docs)]\n            #binding\n\n            impl #impl_generics ::leptos::component::Props for #props_name #generics #where_clause {\n                type Builder = #props_builder_name #generics;\n\n                fn builder() -> Self::Builder {\n                    #props_name::builder()\n                }\n            }\n\n            // TODO restore dyn attrs\n            /*impl #impl_generics ::leptos::DynAttrs for #props_name #generics #where_clause {\n                fn dyn_attrs(mut self, v: Vec<(&'static str, ::leptos::Attribute)>) -> Self {\n                    #dyn_attrs_props\n                    self\n                }\n            } */\n\n            #unknown_attrs\n            #docs_and_prop_docs\n            #[allow(non_snake_case, clippy::too_many_arguments)]\n            #[allow(clippy::needless_lifetimes)]\n            #tracing_instrument_attr\n            #vis fn #name #impl_generics (\n                #props_arg\n            ) #ret\n            #where_clause\n            {\n                #body\n            }\n        };\n\n        tokens.append_all(output)\n    }\n}\n\nimpl Model {\n    #[allow(clippy::wrong_self_convention)]\n    pub fn is_transparent(mut self, is_transparent: bool) -> Self {\n        self.is_transparent = is_transparent;\n\n        self\n    }\n\n    #[allow(clippy::wrong_self_convention)]\n    pub fn is_lazy(mut self, is_lazy: bool) -> Self {\n        self.is_lazy = is_lazy;\n\n        self\n    }\n\n    #[allow(clippy::wrong_self_convention)]\n    pub fn with_island(mut self, island: Option<String>) -> Self {\n        self.island = island;\n\n        self\n    }\n}\n\n/// A model that is more lenient in case of a syntax error in the function body,\n/// but does not actually implement the behavior of the real model. This is\n/// used to improve IDEs and rust-analyzer's auto-completion behavior in case\n/// of a syntax error.\npub struct DummyModel {\n    pub attrs: Vec<Attribute>,\n    pub vis: Visibility,\n    pub sig: Signature,\n    pub body: TokenStream,\n}\n\nimpl Parse for DummyModel {\n    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {\n        let mut attrs = input.call(Attribute::parse_outer)?;\n        // Drop unknown attributes like #[deprecated]\n        drain_filter(&mut attrs, |attr| {\n            !is_lint_attr(attr) && !attr.path().is_ident(\"doc\")\n        });\n\n        let vis: Visibility = input.parse()?;\n        let mut sig: Signature = input.parse()?;\n        maybe_modify_return_type(&mut sig.output);\n\n        // The body is left untouched, so it will not cause an error\n        // even if the syntax is invalid.\n        let body: TokenStream = input.parse()?;\n\n        Ok(Self {\n            attrs,\n            vis,\n            sig,\n            body,\n        })\n    }\n}\n\nimpl ToTokens for DummyModel {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        let Self {\n            attrs,\n            vis,\n            sig,\n            body,\n        } = self;\n\n        // Strip attributes like documentation comments and #[prop]\n        // from the signature, so as to not confuse the user with incorrect\n        // error messages.\n        let sig = {\n            let mut sig = sig.clone();\n            sig.inputs.iter_mut().for_each(|arg| {\n                if let FnArg::Typed(ty) = arg {\n                    ty.attrs.retain(|attr| match &attr.meta {\n                        Meta::List(list) => list\n                            .path\n                            .segments\n                            .first()\n                            .map(|n| n.ident != \"prop\")\n                            .unwrap_or(true),\n                        Meta::NameValue(name_value) => name_value\n                            .path\n                            .segments\n                            .first()\n                            .map(|n| n.ident != \"doc\")\n                            .unwrap_or(true),\n                        _ => true,\n                    });\n                }\n            });\n            sig\n        };\n\n        let output = quote! {\n            #(#attrs)*\n            #vis #sig #body\n        };\n\n        tokens.append_all(output)\n    }\n}\n\nstruct Prop {\n    docs: Docs,\n    prop_opts: PropOpt,\n    name: PatIdent,\n    ty: Type,\n}\n\nimpl Prop {\n    fn new(arg: FnArg) -> Self {\n        let typed = if let FnArg::Typed(ty) = arg {\n            ty\n        } else {\n            abort!(arg, \"receiver not allowed in `fn`\");\n        };\n\n        let prop_opts =\n            PropOpt::from_attributes(&typed.attrs).unwrap_or_else(|e| {\n                // TODO: replace with `.unwrap_or_abort()` once https://gitlab.com/CreepySkeleton/proc-macro-error/-/issues/17 is fixed\n                abort!(e.span(), e.to_string());\n            });\n\n        let name = match *typed.pat {\n            Pat::Ident(i) => {\n                if let Some(name) = &prop_opts.name {\n                    PatIdent {\n                        attrs: vec![],\n                        by_ref: None,\n                        mutability: None,\n                        ident: Ident::new(name, i.span()),\n                        subpat: None,\n                    }\n                } else {\n                    i\n                }\n            }\n            Pat::Struct(_) | Pat::Tuple(_) | Pat::TupleStruct(_) => {\n                if let Some(name) = &prop_opts.name {\n                    PatIdent {\n                        attrs: vec![],\n                        by_ref: None,\n                        mutability: None,\n                        ident: Ident::new(name, typed.pat.span()),\n                        subpat: None,\n                    }\n                } else {\n                    abort!(\n                        typed.pat,\n                        \"destructured props must be given a name e.g. \\\n                         #[prop(name = \\\"data\\\")]\"\n                    );\n                }\n            }\n            _ => {\n                abort!(\n                    typed.pat,\n                    \"only `prop: bool` style types are allowed within the \\\n                     `#[component]` macro\"\n                );\n            }\n        };\n\n        Self {\n            docs: Docs::new(&typed.attrs),\n            prop_opts,\n            name,\n            ty: *typed.ty,\n        }\n    }\n}\n\n#[derive(Clone)]\npub struct Docs(Vec<(String, Span)>);\n\nimpl ToTokens for Docs {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        let s = self\n            .0\n            .iter()\n            .map(|(doc, span)| quote_spanned!(*span=> #[doc = #doc]))\n            .collect::<TokenStream>();\n\n        tokens.append_all(s);\n    }\n}\n\nimpl Docs {\n    pub fn new(attrs: &[Attribute]) -> Self {\n        #[derive(Debug, Copy, Clone, PartialEq, Eq)]\n        enum ViewCodeFenceState {\n            Outside,\n            Rust,\n            Rsx,\n        }\n        let mut quotes = \"```\".to_string();\n        let mut quote_ws = \"\".to_string();\n        let mut view_code_fence_state = ViewCodeFenceState::Outside;\n        // todo fix docs stuff\n        const RSX_START: &str = \"# ::leptos::view! {\";\n        const RSX_END: &str = \"# };\";\n\n        // Separated out of chain to allow rustfmt to work\n        let map = |(doc, span): (String, Span)| {\n            doc.split('\\n')\n                .map(str::trim_end)\n                .flat_map(|doc| {\n                    let trimmed_doc = doc.trim_start();\n                    let leading_ws = &doc[..doc.len() - trimmed_doc.len()];\n                    let trimmed_doc = trimmed_doc.trim_end();\n                    match view_code_fence_state {\n                        ViewCodeFenceState::Outside\n                            if trimmed_doc.starts_with(\"```\")\n                                && trimmed_doc\n                                    .trim_start_matches('`')\n                                    .starts_with(\"view\") =>\n                        {\n                            view_code_fence_state = ViewCodeFenceState::Rust;\n                            let view = trimmed_doc.find('v').unwrap();\n                            trimmed_doc[..view].clone_into(&mut quotes);\n                            leading_ws.clone_into(&mut quote_ws);\n                            let rust_options = &trimmed_doc\n                                [view + \"view\".len()..]\n                                .trim_start();\n                            vec![\n                                format!(\"{leading_ws}{quotes}{rust_options}\"),\n                                format!(\"{leading_ws}\"),\n                            ]\n                        }\n                        ViewCodeFenceState::Rust if trimmed_doc == quotes => {\n                            view_code_fence_state = ViewCodeFenceState::Outside;\n                            vec![format!(\"{leading_ws}\"), doc.to_owned()]\n                        }\n                        ViewCodeFenceState::Rust\n                            if trimmed_doc.starts_with('<') =>\n                        {\n                            view_code_fence_state = ViewCodeFenceState::Rsx;\n                            vec![\n                                format!(\"{leading_ws}{RSX_START}\"),\n                                doc.to_owned(),\n                            ]\n                        }\n                        ViewCodeFenceState::Rsx if trimmed_doc == quotes => {\n                            view_code_fence_state = ViewCodeFenceState::Outside;\n                            vec![\n                                format!(\"{leading_ws}{RSX_END}\"),\n                                doc.to_owned(),\n                            ]\n                        }\n                        _ => vec![doc.to_string()],\n                    }\n                })\n                .map(|l| (l, span))\n                .collect_vec()\n        };\n\n        let mut attrs = attrs\n            .iter()\n            .filter_map(|attr| {\n                let Meta::NameValue(attr) = &attr.meta else {\n                    return None;\n                };\n                if !attr.path.is_ident(\"doc\") {\n                    return None;\n                }\n\n                let Some(val) = value_to_string(&attr.value) else {\n                    abort!(\n                        attr,\n                        \"expected string literal in value of doc comment\"\n                    );\n                };\n\n                Some((val, attr.path.span()))\n            })\n            .flat_map(map)\n            .collect_vec();\n\n        if view_code_fence_state != ViewCodeFenceState::Outside {\n            if view_code_fence_state == ViewCodeFenceState::Rust {\n                attrs.push((quote_ws.clone(), Span::call_site()))\n            } else {\n                attrs.push((format!(\"{quote_ws}{RSX_END}\"), Span::call_site()))\n            }\n            attrs.push((format!(\"{quote_ws}{quotes}\"), Span::call_site()))\n        }\n\n        Self(attrs)\n    }\n\n    pub fn padded(&self) -> TokenStream {\n        self.0\n            .iter()\n            .enumerate()\n            .map(|(idx, (doc, span))| {\n                let doc = if idx == 0 {\n                    format!(\"    - {doc}\")\n                } else {\n                    format!(\"      {doc}\")\n                };\n\n                let doc = LitStr::new(&doc, *span);\n\n                quote! { #[doc = #doc] }\n            })\n            .collect()\n    }\n\n    pub fn typed_builder(&self) -> String {\n        let doc_str = self.0.iter().map(|s| s.0.as_str()).join(\"\\n\");\n\n        if doc_str.chars().filter(|c| *c != '\\n').count() != 0 {\n            format!(\"\\n\\n{doc_str}\")\n        } else {\n            String::new()\n        }\n    }\n}\n\nfn is_lint_attr(attr: &Attribute) -> bool {\n    let path = &attr.path();\n    path.is_ident(\"allow\")\n        || path.is_ident(\"warn\")\n        || path.is_ident(\"expect\")\n        || path.is_ident(\"deny\")\n        || path.is_ident(\"forbid\")\n}\n\npub struct UnknownAttrs(Vec<(TokenStream, Span)>);\n\nimpl UnknownAttrs {\n    pub fn new(attrs: &[Attribute]) -> Self {\n        let attrs = attrs\n            .iter()\n            .filter_map(|attr| {\n                if attr.path().is_ident(\"doc\") {\n                    if let Meta::NameValue(_) = &attr.meta {\n                        return None;\n                    }\n                }\n\n                if is_lint_attr(attr) {\n                    return None;\n                }\n\n                Some((attr.into_token_stream(), attr.span()))\n            })\n            .collect_vec();\n        Self(attrs)\n    }\n}\n\nimpl ToTokens for UnknownAttrs {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        let s = self\n            .0\n            .iter()\n            .map(|(attr, span)| quote_spanned!(*span=> #attr))\n            .collect::<TokenStream>();\n        tokens.append_all(s);\n    }\n}\n\n#[derive(Clone, Debug, FromAttr)]\n#[attribute(ident = prop)]\nstruct PropOpt {\n    #[attribute(conflicts = [optional_no_strip, strip_option])]\n    optional: bool,\n    #[attribute(conflicts = [optional, strip_option])]\n    optional_no_strip: bool,\n    #[attribute(conflicts = [optional, optional_no_strip])]\n    strip_option: bool,\n    #[attribute(example = \"5 * 10\")]\n    default: Option<syn::Expr>,\n    into: bool,\n    attrs: bool,\n    name: Option<String>,\n}\n\nstruct TypedBuilderOpts<'a> {\n    default: bool,\n    default_with_value: Option<syn::Expr>,\n    strip_option: bool,\n    into: bool,\n    ty: &'a Type,\n}\n\nimpl<'a> TypedBuilderOpts<'a> {\n    fn from_opts(opts: &PropOpt, ty: &'a Type) -> Self {\n        Self {\n            default: opts.optional || opts.optional_no_strip || opts.attrs,\n            default_with_value: opts.default.clone(),\n            strip_option: opts.strip_option || opts.optional && is_option(ty),\n            into: opts.into,\n            ty,\n        }\n    }\n}\n\nimpl TypedBuilderOpts<'_> {\n    fn to_serde_tokens(&self) -> TokenStream {\n        let default = if let Some(v) = &self.default_with_value {\n            let v = v.to_token_stream().to_string();\n            quote! { default=#v, }\n        } else if self.default {\n            quote! { default, }\n        } else {\n            quote! {}\n        };\n\n        if !default.is_empty() {\n            quote! { #[serde(#default)] }\n        } else {\n            quote! {}\n        }\n    }\n}\n\nimpl ToTokens for TypedBuilderOpts<'_> {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        let default = if let Some(v) = &self.default_with_value {\n            let v = v.to_token_stream().to_string();\n            quote! { default_code=#v, }\n        } else if self.default {\n            quote! { default, }\n        } else {\n            quote! {}\n        };\n\n        // If self.strip_option && self.into, then the strip_option will be represented as part of the transform closure.\n        let strip_option = if self.strip_option && !self.into {\n            quote! { strip_option, }\n        } else {\n            quote! {}\n        };\n\n        let into = if self.into {\n            if !self.strip_option {\n                let ty = &self.ty;\n                quote! {\n                    fn transform<__IntoReactiveValueMarker>(value: impl ::leptos::prelude::IntoReactiveValue<#ty, __IntoReactiveValueMarker>) -> #ty {\n                        value.into_reactive_value()\n                    },\n                }\n            } else {\n                let ty = unwrap_option(self.ty);\n                quote! {\n                    fn transform<__IntoReactiveValueMarker>(value: impl ::leptos::prelude::IntoReactiveValue<#ty, __IntoReactiveValueMarker>) -> Option<#ty> {\n                        Some(value.into_reactive_value())\n                    },\n                }\n            }\n        } else {\n            quote! {}\n        };\n\n        let setter = if !strip_option.is_empty() || !into.is_empty() {\n            quote! { setter(#strip_option #into) }\n        } else {\n            quote! {}\n        };\n\n        let output = if !default.is_empty() || !setter.is_empty() {\n            quote! { #[builder(#default #setter)] }\n        } else {\n            quote! {}\n        };\n\n        tokens.append_all(output);\n    }\n}\n\nfn prop_builder_fields(\n    vis: &Visibility,\n    props: &[Prop],\n    is_island_with_other_props: bool,\n) -> TokenStream {\n    props\n        .iter()\n        .map(|prop| {\n            let Prop {\n                docs,\n                name,\n                prop_opts,\n                ty,\n            } = prop;\n\n            let builder_attrs = TypedBuilderOpts::from_opts(prop_opts, ty);\n\n            let builder_docs = prop_to_doc(prop, PropDocStyle::Inline);\n\n            // Children won't need documentation in many cases\n            let allow_missing_docs = if name.ident == \"children\" {\n                quote!(#[allow(missing_docs)])\n            } else {\n                quote!()\n            };\n            let skip_children_serde =\n                if is_island_with_other_props && name.ident == \"children\" {\n                    quote!(#[serde(skip)])\n                } else {\n                    quote!()\n                };\n\n            let PatIdent { ident, by_ref, .. } = &name;\n\n            quote! {\n                #docs\n                #builder_docs\n                #builder_attrs\n                #allow_missing_docs\n                #skip_children_serde\n                #vis #by_ref #ident: #ty,\n            }\n        })\n        .collect()\n}\n\nfn prop_serializer_fields(vis: &Visibility, props: &[Prop]) -> TokenStream {\n    props\n        .iter()\n        .filter_map(|prop| {\n            if prop.name.ident == \"children\" {\n                None\n            } else {\n                let Prop {\n                    docs,\n                    name,\n                    prop_opts,\n                    ty,\n                } = prop;\n\n                let builder_attrs = TypedBuilderOpts::from_opts(prop_opts, ty);\n                let serde_attrs = builder_attrs.to_serde_tokens();\n\n                let PatIdent { ident, by_ref, .. } = &name;\n\n                Some(quote! {\n                    #docs\n                    #serde_attrs\n                    #vis #by_ref #ident: #ty,\n                })\n            }\n        })\n        .collect()\n}\n\nfn prop_names(props: &[Prop]) -> TokenStream {\n    props\n        .iter()\n        .map(|Prop { name, .. }| {\n            // fields like mutability are removed because unneeded\n            // in the contexts in which this is used\n            let ident = &name.ident;\n            quote! { #ident, }\n        })\n        .collect()\n}\n\nfn generate_component_fn_prop_docs(props: &[Prop]) -> TokenStream {\n    let required_prop_docs = props\n        .iter()\n        .filter(|Prop { prop_opts, .. }| {\n            !(prop_opts.optional\n                || prop_opts.optional_no_strip\n                || prop_opts.default.is_some())\n        })\n        .map(|p| prop_to_doc(p, PropDocStyle::List))\n        .collect::<TokenStream>();\n\n    let optional_prop_docs = props\n        .iter()\n        .filter(|Prop { prop_opts, .. }| {\n            prop_opts.optional\n                || prop_opts.optional_no_strip\n                || prop_opts.default.is_some()\n        })\n        .map(|p| prop_to_doc(p, PropDocStyle::List))\n        .collect::<TokenStream>();\n\n    let required_prop_docs = if !required_prop_docs.is_empty() {\n        quote! {\n            #[doc = \" # Required Props\"]\n            #required_prop_docs\n        }\n    } else {\n        quote! {}\n    };\n\n    let optional_prop_docs = if !optional_prop_docs.is_empty() {\n        quote! {\n            #[doc = \" # Optional Props\"]\n            #optional_prop_docs\n        }\n    } else {\n        quote! {}\n    };\n\n    quote! {\n        #required_prop_docs\n        #optional_prop_docs\n    }\n}\n\npub fn is_option(ty: &Type) -> bool {\n    if let Type::Path(TypePath {\n        path: Path { segments, .. },\n        ..\n    }) = ty\n    {\n        if let [first] = &segments.iter().collect::<Vec<_>>()[..] {\n            first.ident == \"Option\"\n        } else {\n            false\n        }\n    } else {\n        false\n    }\n}\n\npub fn unwrap_option(ty: &Type) -> Type {\n    const STD_OPTION_MSG: &str =\n        \"make sure you're not shadowing the `std::option::Option` type that \\\n         is automatically imported from the standard prelude\";\n\n    if let Type::Path(TypePath {\n        path: Path { segments, .. },\n        ..\n    }) = ty\n    {\n        if let [first] = &segments.iter().collect::<Vec<_>>()[..] {\n            if first.ident == \"Option\" {\n                if let PathArguments::AngleBracketed(\n                    AngleBracketedGenericArguments { args, .. },\n                ) = &first.arguments\n                {\n                    if let [GenericArgument::Type(ty)] =\n                        &args.iter().collect::<Vec<_>>()[..]\n                    {\n                        return ty.clone();\n                    }\n                }\n            }\n        }\n    }\n\n    abort!(\n        ty,\n        \"`Option` must be `std::option::Option`\";\n        help = STD_OPTION_MSG\n    );\n}\n\n#[derive(Clone, Copy)]\nenum PropDocStyle {\n    List,\n    Inline,\n}\n\nfn prop_to_doc(\n    Prop {\n        docs,\n        name,\n        ty,\n        prop_opts,\n    }: &Prop,\n    style: PropDocStyle,\n) -> TokenStream {\n    let ty = if (prop_opts.optional || prop_opts.strip_option) && is_option(ty)\n    {\n        unwrap_option(ty)\n    } else {\n        ty.to_owned()\n    };\n\n    let type_item: syn::Item = parse_quote! {\n        type SomeType = #ty;\n    };\n\n    let file = syn::File {\n        shebang: None,\n        attrs: vec![],\n        items: vec![type_item],\n    };\n\n    let pretty_ty = prettyplease::unparse(&file);\n\n    let pretty_ty = &pretty_ty[16..&pretty_ty.len() - 2];\n\n    match style {\n        PropDocStyle::List => {\n            let arg_ty_doc = LitStr::new(\n                &if !prop_opts.into {\n                    format!(\" - **{}**: [`{pretty_ty}`]\", quote!(#name))\n                } else {\n                    format!(\n                        \" - **{}**: [`impl Into<{pretty_ty}>`]({pretty_ty})\",\n                        quote!(#name),\n                    )\n                },\n                name.ident.span(),\n            );\n\n            let arg_user_docs = docs.padded();\n\n            quote! {\n                #[doc = #arg_ty_doc]\n                #arg_user_docs\n            }\n        }\n        PropDocStyle::Inline => {\n            let arg_ty_doc = LitStr::new(\n                &if !prop_opts.into {\n                    format!(\n                        \"**{}**: [`{}`]{}\",\n                        quote!(#name),\n                        pretty_ty,\n                        docs.typed_builder()\n                    )\n                } else {\n                    format!(\n                        \"**{}**: `impl`[`Into<{}>`]{}\",\n                        quote!(#name),\n                        pretty_ty,\n                        docs.typed_builder()\n                    )\n                },\n                name.ident.span(),\n            );\n\n            quote! {\n                #[builder(setter(doc = #arg_ty_doc))]\n            }\n        }\n    }\n}\n\npub fn unmodified_fn_name_from_fn_name(ident: &Ident) -> Ident {\n    Ident::new(\n        &format!(\"__component_{}\", ident.to_string().to_case(Snake)),\n        ident.span(),\n    )\n}\n\n/// Converts all `impl Trait`s in a function signature to use generic params instead.\nfn convert_impl_trait_to_generic(sig: &mut Signature) {\n    fn new_generic_ident(i: usize, span: Span) -> Ident {\n        Ident::new(&format!(\"__ImplTrait{i}\"), span)\n    }\n\n    // First: visit all `impl Trait`s and replace them with new generic params.\n    #[derive(Default)]\n    struct RemoveImplTrait(Vec<TypeImplTrait>);\n    impl VisitMut for RemoveImplTrait {\n        fn visit_type_mut(&mut self, ty: &mut Type) {\n            syn::visit_mut::visit_type_mut(self, ty);\n            if matches!(ty, Type::ImplTrait(_)) {\n                let ident = new_generic_ident(self.0.len(), ty.span());\n                let generic_type = Type::Path(TypePath {\n                    qself: None,\n                    path: Path::from(ident),\n                });\n                let Type::ImplTrait(impl_trait) =\n                    std::mem::replace(ty, generic_type)\n                else {\n                    unreachable!();\n                };\n                self.0.push(impl_trait);\n            }\n        }\n\n        // Early exits.\n        fn visit_attribute_mut(&mut self, _: &mut Attribute) {}\n        fn visit_pat_mut(&mut self, _: &mut Pat) {}\n    }\n    let mut visitor = RemoveImplTrait::default();\n    for fn_arg in sig.inputs.iter_mut() {\n        visitor.visit_fn_arg_mut(fn_arg);\n    }\n    let RemoveImplTrait(impl_traits) = visitor;\n\n    // Second: Add the new generic params into the signature.\n    for (i, impl_trait) in impl_traits.into_iter().enumerate() {\n        let span = impl_trait.span();\n        let ident = new_generic_ident(i, span);\n        // We can simply append to the end (only lifetime params must be first).\n        // Note currently default generics are not allowed in `fn`, so not a concern.\n        sig.generics.params.push(GenericParam::Type(TypeParam {\n            attrs: vec![],\n            ident,\n            colon_token: Some(Colon { spans: [span] }),\n            bounds: impl_trait.bounds,\n            eq_token: None,\n            default: None,\n        }));\n    }\n}\n"
  },
  {
    "path": "leptos_macro/src/lazy.rs",
    "content": "use convert_case::{Case, Casing};\nuse proc_macro::TokenStream;\nuse proc_macro2::Ident;\nuse proc_macro_error2::abort;\nuse quote::{format_ident, quote};\nuse std::{\n    hash::{DefaultHasher, Hash, Hasher},\n    mem,\n};\nuse syn::{parse_macro_input, parse_quote, ItemFn, ReturnType, Stmt};\n\npub fn lazy_impl(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n    let name = if !args.is_empty() {\n        Some(parse_macro_input!(args as syn::Ident))\n    } else {\n        None\n    };\n\n    let fun = syn::parse::<ItemFn>(s).unwrap_or_else(|e| {\n        abort!(e.span(), \"`lazy` can only be used on a function\")\n    });\n\n    let was_async = fun.sig.asyncness.is_some();\n\n    let converted_name = name.unwrap_or_else(|| {\n        Ident::new(\n            &fun.sig.ident.to_string().to_case(Case::Snake),\n            fun.sig.ident.span(),\n        )\n    });\n\n    let (unique_name, unique_name_str) = {\n        let span = proc_macro::Span::call_site();\n        let location = (span.line(), span.start().column(), span.file());\n\n        let mut hasher = DefaultHasher::new();\n        location.hash(&mut hasher);\n        let hash = hasher.finish();\n\n        let unique_name_str = format!(\"{converted_name}_{hash}\");\n\n        (\n            Ident::new(&unique_name_str, converted_name.span()),\n            unique_name_str,\n        )\n    };\n\n    let is_wasm = cfg!(feature = \"csr\") || cfg!(feature = \"hydrate\");\n    if is_wasm {\n        let mut fun = fun;\n        let mut return_wrapper = None;\n        if was_async {\n            fun.sig.asyncness = None;\n            let ty = match &fun.sig.output {\n                ReturnType::Default => quote! { () },\n                ReturnType::Type(_, ty) => quote! { #ty },\n            };\n            let sync_output: ReturnType = parse_quote! {\n                -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = #ty> + ::std::marker::Send + ::std::marker::Sync>>\n            };\n            let async_output = mem::replace(&mut fun.sig.output, sync_output);\n            let stmts = mem::take(&mut fun.block.stmts);\n            fun.block.stmts.push(Stmt::Expr(parse_quote! {\n                ::std::boxed::Box::pin(::leptos::__reexports::send_wrapper::SendWrapper::new(async move {\n                    #( #stmts )*\n                }))\n            }, None));\n            return_wrapper = Some(quote! {\n                return_wrapper(let future = _; { future.await } #async_output),\n            });\n        }\n        let preload_name = format_ident!(\"__preload_{}\", fun.sig.ident);\n\n        quote! {\n            #[::leptos::wasm_split::wasm_split(\n                #unique_name,\n                wasm_split_path = ::leptos::wasm_split,\n                preload(#[doc(hidden)] #[allow(non_snake_case)] #preload_name),\n                #return_wrapper\n            )]\n            #fun\n        }\n    } else {\n        let mut fun = fun;\n        if !was_async {\n            fun.sig.asyncness = Some(Default::default());\n        }\n\n        let statements = &mut fun.block.stmts;\n        let old_statements = mem::take(statements);\n        statements.push(parse_quote! {\n            ::leptos::prefetch_lazy_fn_on_server(#unique_name_str);\n        });\n        statements.extend(old_statements);\n        quote! { #fun }\n    }\n    .into()\n}\n"
  },
  {
    "path": "leptos_macro/src/lib.rs",
    "content": "//! Macros for use with the Leptos framework.\n\n#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(proc_macro_span))]\n#![forbid(unsafe_code)]\n// to prevent warnings from popping up when a nightly feature is stabilized\n#![allow(stable_features)]\n// FIXME? every use of quote! {} is warning here -- false positive?\n#![allow(unknown_lints)]\n#![allow(private_macro_use)]\n#![deny(missing_docs)]\n\n#[macro_use]\nextern crate proc_macro_error2;\n\nuse component::DummyModel;\nuse proc_macro::TokenStream;\nuse proc_macro2::{Span, TokenTree};\nuse quote::{quote, ToTokens};\nuse std::str::FromStr;\nuse syn::{parse_macro_input, spanned::Spanned, token::Pub, Visibility};\n\nmod params;\nmod view;\nuse crate::component::unmodified_fn_name_from_fn_name;\nmod component;\nmod lazy;\nmod memo;\nmod slice;\nmod slot;\n\n/// The `view` macro uses RSX (like JSX, but Rust!) It follows most of the\n/// same rules as HTML, with the following differences:\n///\n/// 1. Text content should be provided as a Rust string, i.e., double-quoted:\n/// ```rust\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// view! { <p>\"Here’s some text\"</p> }\n/// # }\n/// ```\n///\n/// 2. Self-closing tags need an explicit `/` as in XML/XHTML\n/// ```rust,compile_fail\n/// # use leptos::prelude::*;\n///\n/// # fn test() -> impl IntoView {\n/// // ❌ not like this\n/// view! { <input type=\"text\" name=\"name\"> }\n/// # ;\n/// # }\n/// ```\n/// ```rust\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// // ✅ add that slash\n/// view! { <input type=\"text\" name=\"name\" /> }\n/// # }\n/// ```\n///\n/// 3. Components (functions annotated with `#[component]`) can be inserted as camel-cased tags. (Generics\n///    on components are specified as `<Component<T>/>`, not the turbofish `<Component::<T>/>`.)\n/// ```rust\n/// # use leptos::prelude::*;\n///\n/// # #[component]\n/// # fn Counter(initial_value: i32) -> impl IntoView { view! { <p></p>} }\n/// # fn test() -> impl IntoView {\n/// view! { <div><Counter initial_value=3 /></div> }\n/// # ;\n/// # }\n/// ```\n///\n/// 4. Dynamic content can be wrapped in curly braces (`{ }`) to insert text nodes, elements, or set attributes.\n///    If you insert a signal here, Leptos will create an effect to update the DOM whenever the value changes.\n///    *(“Signal” here means `Fn() -> T` where `T` is the appropriate type for that node: a `String` in case\n///    of text nodes, a `bool` for `class:` attributes, etc.)*\n///\n///    Attributes can take a wide variety of primitive types that can be converted to strings. They can also\n///    take an `Option`, in which case `Some` sets the attribute and `None` removes the attribute.\n///\n///    Note that in some cases, rust-analyzer support may be better if attribute values are surrounded with braces (`{}`).\n///    Unlike in JSX, attribute values are not required to be in braces, but braces can be used and may improve this LSP support.\n///\n/// ```rust,ignore\n/// # use leptos::prelude::*;\n///\n/// # fn test() -> impl IntoView {\n/// let (count, set_count) = create_signal(0);\n///\n/// view! {\n///   // ❌ not like this: `count.get()` returns an `i32`, not a function\n///   <p>{count.get()}</p>\n///   // ✅ this is good: Leptos sees the function and knows it's a dynamic value\n///   <p>{move || count.get()}</p>\n///   // 🔥 with the `nightly` feature, `count` is a function, so `count` itself can be passed directly into the view\n///   <p>{count}</p>\n/// }\n/// # ;\n/// # };\n/// ```\n///\n/// 5. Event handlers can be added with `on:` attributes. In most cases, the events are given the correct type\n///    based on the event name.\n/// ```rust\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// view! {\n///   <button on:click=|ev| {\n///     log::debug!(\"click event: {ev:#?}\");\n///   }>\n///     \"Click me\"\n///   </button>\n/// }\n/// # }\n/// ```\n///\n/// 6. DOM properties can be set with `prop:` attributes, which take any primitive type or `JsValue` (or a signal\n///    that returns a primitive or JsValue). They can also take an `Option`, in which case `Some` sets the property\n///    and `None` deletes the property.\n/// ```rust\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// let (name, set_name) = create_signal(\"Alice\".to_string());\n///\n/// view! {\n///   <input\n///     type=\"text\"\n///     name=\"user_name\"\n///     value={move || name.get()} // this only sets the default value!\n///     prop:value={move || name.get()} // here's how you update values. Sorry, I didn’t invent the DOM.\n///     on:click=move |ev| set_name.set(event_target_value(&ev)) // `event_target_value` is a useful little Leptos helper\n///   />\n/// }\n/// # }\n/// ```\n///\n/// 7. Classes can be toggled with `class:` attributes, which take a `bool` (or a signal that returns a `bool`).\n/// ```rust\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// let (count, set_count) = create_signal(2);\n/// view! { <div class:hidden-div={move || count.get() < 3}>\"Now you see me, now you don’t.\"</div> }\n/// # }\n/// ```\n///\n/// Class names can include dashes, and since v0.5.0 can include a dash-separated segment of only numbers.\n/// ```rust\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// let (count, set_count) = create_signal(2);\n/// view! { <div class:hidden-div-25={move || count.get() < 3}>\"Now you see me, now you don’t.\"</div> }\n/// # }\n/// ```\n///\n/// Class names cannot include special symbols.\n/// ```rust,compile_fail\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// let (count, set_count) = create_signal(2);\n/// // class:hidden-[div]-25 is invalid attribute name\n/// view! { <div class:hidden-[div]-25={move || count.get() < 3}>\"Now you see me, now you don’t.\"</div> }\n/// # }\n/// ```\n///\n/// However, you can pass arbitrary class names using the syntax `class=(\"name\", value)`.\n/// ```rust\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// let (count, set_count) = create_signal(2);\n/// // this allows you to use CSS frameworks that include complex class names\n/// view! {\n///   <div\n///     class=(\"is-[this_-_really]-necessary-42\", move || count.get() < 3)\n///   >\n///     \"Now you see me, now you don’t.\"\n///   </div>\n/// }\n/// # }\n/// ```\n///\n/// 8. Individual styles can also be set with `style:` or `style=(\"property-name\", value)` syntax.\n/// ```rust\n/// # use leptos::prelude::*;\n///\n/// # fn test() -> impl IntoView {\n/// let (x, set_x) = create_signal(0);\n/// let (y, set_y) = create_signal(0);\n/// view! {\n///   <div\n///     style=\"position: absolute\"\n///     style:left=move || format!(\"{}px\", x.get())\n///     style:top=move || format!(\"{}px\", y.get())\n///     style=(\"background-color\", move || format!(\"rgb({}, {}, 100)\", x.get(), y.get()))\n///   >\n///     \"Moves when coordinates change\"\n///   </div>\n/// }\n/// # }\n/// ```\n///\n/// 9. You can use the `node_ref` or `_ref` attribute to store a reference to its DOM element in a\n///    [NodeRef](https://docs.rs/leptos/latest/leptos/prelude/struct.NodeRef.html) to use later.\n/// ```rust\n/// # use leptos::prelude::*;\n///\n/// # fn test() -> impl IntoView {\n/// use leptos::html::Input;\n///\n/// let (value, set_value) = signal(0);\n/// let my_input = NodeRef::<Input>::new();\n/// view! { <input type=\"text\" node_ref=my_input/> }\n/// // `my_input` now contains an `Element` that we can use anywhere\n/// # ;\n/// # };\n/// ```\n///\n/// 10. You can add the same class to every element in the view by passing in a special\n///    `class = {/* ... */},` argument after ``. This is useful for injecting a class\n///    provided by a scoped styling library.\n/// ```rust\n/// # use leptos::prelude::*;\n///\n/// # fn test() -> impl IntoView {\n/// let class = \"mycustomclass\";\n/// view! { class = class,\n///   <div> // will have class=\"mycustomclass\"\n///     <p>\"Some text\"</p> // will also have class \"mycustomclass\"\n///   </div>\n/// }\n/// # }\n/// ```\n///\n/// 11. You can set any HTML element’s `innerHTML` with the `inner_html` attribute on an\n///     element. Be careful: this HTML will not be escaped, so you should ensure that it\n///     only contains trusted input.\n/// ```rust\n/// # use leptos::prelude::*;\n/// # fn test() -> impl IntoView {\n/// let html = \"<p>This HTML will be injected.</p>\";\n/// view! {\n///   <div inner_html=html/>\n/// }\n/// # }\n/// ```\n///\n/// Here’s a simple example that shows off several of these features, put together\n/// ```rust\n/// # use leptos::prelude::*;\n/// pub fn SimpleCounter() -> impl IntoView {\n///     // create a reactive signal with the initial value\n///     let (value, set_value) = create_signal(0);\n///\n///     // create event handlers for our buttons\n///     // note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures\n///     let clear = move |_ev| set_value.set(0);\n///     let decrement = move |_ev| set_value.update(|value| *value -= 1);\n///     let increment = move |_ev| set_value.update(|value| *value += 1);\n///\n///     view! {\n///         <div>\n///             <button on:click=clear>\"Clear\"</button>\n///             <button on:click=decrement>\"-1\"</button>\n///             <span>\"Value: \" {move || value.get().to_string()} \"!\"</span>\n///             <button on:click=increment>\"+1\"</button>\n///         </div>\n///     }\n/// }\n/// ```\n#[proc_macro_error2::proc_macro_error]\n#[proc_macro]\n#[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\", skip_all))]\npub fn view(tokens: TokenStream) -> TokenStream {\n    view_macro_impl(tokens, false)\n}\n\n/// The `template` macro behaves like [`view`](view!), except that it wraps the entire tree in a\n/// [`ViewTemplate`](https://docs.rs/leptos/0.7.0-gamma3/leptos/prelude/struct.ViewTemplate.html). This optimizes creation speed by rendering\n/// most of the view into a `<template>` tag with HTML rendered at compile time, then hydrating it.\n/// In exchange, there is a small binary size overhead.\n#[proc_macro_error2::proc_macro_error]\n#[proc_macro]\n#[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\", skip_all))]\npub fn template(tokens: TokenStream) -> TokenStream {\n    if cfg!(feature = \"__internal_erase_components\") {\n        view(tokens)\n    } else {\n        view_macro_impl(tokens, true)\n    }\n}\n\nfn view_macro_impl(tokens: TokenStream, template: bool) -> TokenStream {\n    let tokens: proc_macro2::TokenStream = tokens.into();\n    let mut tokens = tokens.into_iter();\n\n    let first = tokens.next();\n    let second = tokens.next();\n    let third = tokens.next();\n    let fourth = tokens.next();\n    let global_class = match (&first, &second) {\n        (Some(TokenTree::Ident(first)), Some(TokenTree::Punct(eq)))\n            if *first == \"class\" && eq.as_char() == '=' =>\n        {\n            match &fourth {\n                Some(TokenTree::Punct(comma)) if comma.as_char() == ',' => {\n                    third.clone()\n                }\n                _ => {\n                    abort!(\n                        second, \"To create a scope class with the view! macro you must put a comma `,` after the value\";\n                        help = r#\"e.g., view!{ class=\"my-class\", <div>...</div>}\"#\n                    )\n                }\n            }\n        }\n        _ => None,\n    };\n    let tokens = if global_class.is_some() {\n        tokens.collect::<proc_macro2::TokenStream>()\n    } else {\n        [first, second, third, fourth]\n            .into_iter()\n            .flatten()\n            .chain(tokens)\n            .collect()\n    };\n    let config = rstml::ParserConfig::default().recover_block(true);\n    let parser = rstml::Parser::new(config);\n    let (mut nodes, errors) = parser.parse_recoverable(tokens).split_vec();\n    let errors = errors.into_iter().map(|e| e.emit_as_expr_tokens());\n    let nodes_output = view::render_view(\n        &mut nodes,\n        global_class.as_ref(),\n        normalized_call_site(proc_macro::Span::call_site()),\n        template,\n    );\n\n    // The allow lint needs to be put here instead of at the expansion of\n    // view::attribute_value(). Adding this next to the expanded expression\n    // seems to break rust-analyzer, but it works when the allow is put here.\n    let output = quote! {\n        {\n            #[allow(unused_braces)]\n            {\n                #(#errors;)*\n                #nodes_output\n            }\n        }\n    };\n\n    if template {\n        quote! {\n            ::leptos::prelude::ViewTemplate::new(#output)\n        }\n    } else {\n        output\n    }\n    .into()\n}\n\nfn normalized_call_site(site: proc_macro::Span) -> Option<String> {\n    if cfg!(debug_assertions) {\n        Some(leptos_hot_reload::span_to_stable_id(\n            site.file(),\n            site.start().line(),\n        ))\n    } else {\n        _ = site;\n        None\n    }\n}\n\n/// This behaves like the [`view`](view!) macro, but loads the view from an external file instead of\n/// parsing it inline.\n///\n/// This is designed to allow editing views in a separate file, if this improves a user's workflow.\n///\n/// The file is loaded and parsed during proc-macro execution, and its path is resolved relative to\n/// the crate root rather than relative to the file from which it is called.\n#[proc_macro_error2::proc_macro_error]\n#[proc_macro]\npub fn include_view(tokens: TokenStream) -> TokenStream {\n    let file_name = syn::parse::<syn::LitStr>(tokens).unwrap_or_else(|_| {\n        abort!(\n            Span::call_site(),\n            \"the only supported argument is a string literal\"\n        );\n    });\n    let file =\n        std::fs::read_to_string(file_name.value()).unwrap_or_else(|_| {\n            abort!(Span::call_site(), \"could not open file\");\n        });\n    let tokens = proc_macro2::TokenStream::from_str(&file)\n        .unwrap_or_else(|e| abort!(Span::call_site(), e));\n    view(tokens.into())\n}\n\n/// Annotates a function so that it can be used with your template as a Leptos `<Component/>`.\n///\n/// The `#[component]` macro allows you to annotate plain Rust functions as components\n/// and use them within your Leptos [view](crate::view!) as if they were custom HTML elements. The\n/// component function takes any number of other arguments. When you use the component somewhere else,\n/// the names of its arguments are the names of the properties you use in the [view](crate::view!) macro.\n///\n/// Every component function should have the return type `-> impl IntoView`.\n///\n/// You can add Rust doc comments to component function arguments and the macro will use them to\n/// generate documentation for the component.\n///\n/// Here’s how you would define and use a simple Leptos component which can accept custom properties for a name and age:\n///\n/// ```rust\n/// # use leptos::prelude::*;\n/// use std::time::Duration;\n///\n/// #[component]\n/// fn HelloComponent(\n///     /// The user's name.\n///     name: String,\n///     /// The user's age.\n///     age: u8,\n/// ) -> impl IntoView {\n///     // create the signals (reactive values) that will update the UI\n///     let (age, set_age) = create_signal(age);\n///     // increase `age` by 1 every second\n///     set_interval(\n///         move || set_age.update(|age| *age += 1),\n///         Duration::from_secs(1),\n///     );\n///\n///     // return the user interface, which will be automatically updated\n///     // when signal values change\n///     view! {\n///       <p>\"Your name is \" {name} \" and you are \" {move || age.get()} \" years old.\"</p>\n///     }\n/// }\n///\n/// #[component]\n/// fn App() -> impl IntoView {\n///     view! {\n///       <main>\n///         <HelloComponent name=\"Greg\".to_string() age=32/>\n///       </main>\n///     }\n/// }\n/// ```\n///\n/// Here are some important details about how Leptos components work within the framework:\n///\n/// * **The component function only runs once.** Your component function is not a “render” function\n///    that re-runs whenever changes happen in the state. It’s a “setup” function that runs once to\n///    create the user interface, and sets up a reactive system to update it. This means it’s okay\n///    to do relatively expensive work within the component function, as it will only happen once,\n///    not on every state change.\n///\n/// * Component names are usually in `PascalCase`. If you use a `snake_case` name, then the generated\n///    component's name will still be in `PascalCase`. This is how the framework recognizes that\n///    a particular tag is a component, not an HTML element.\n///\n/// ```\n/// # use leptos::prelude::*;\n/// // PascalCase: Generated component will be called MyComponent\n/// #[component]\n/// fn MyComponent() -> impl IntoView {}\n///\n/// // snake_case: Generated component will be called MySnakeCaseComponent\n/// #[component]\n/// fn my_snake_case_component() -> impl IntoView {}\n/// ```\n///\n/// 5. You can access the children passed into the component with the `children` property, which takes\n///    an argument of the type `Children`. This is an alias for `Box<dyn FnOnce() -> AnyView<_>>`.\n///    If you need `children` to be a `Fn` or `FnMut`, you can use the `ChildrenFn` or `ChildrenFnMut`\n///    type aliases. If you want to iterate over the children, you can take `ChildrenFragment`.\n///\n/// ```\n/// # use leptos::prelude::*;\n/// #[component]\n/// fn ComponentWithChildren(children: ChildrenFragment) -> impl IntoView {\n///     view! {\n///       <ul>\n///         {children()\n///           .nodes\n///           .into_iter()\n///           .map(|child| view! { <li>{child}</li> })\n///           .collect::<Vec<_>>()}\n///       </ul>\n///     }\n/// }\n///\n/// #[component]\n/// fn WrapSomeChildren() -> impl IntoView {\n///     view! {\n///       <ComponentWithChildren>\n///         \"Ooh, look at us!\"\n///         <span>\"We're being projected!\"</span>\n///       </ComponentWithChildren>\n///     }\n/// }\n/// ```\n///\n/// ## Customizing Properties\n///\n/// You can use the `#[prop]` attribute on individual component properties (function arguments) to\n/// customize the types that component property can receive. You can use the following attributes:\n///\n/// * `#[prop(into)]`: This will call `.into()` on any value passed into the component prop. (For example,\n///   you could apply `#[prop(into)]` to a prop that takes\n///   [Signal](https://docs.rs/leptos/latest/leptos/prelude/struct.Signal.html), which would\n///   allow users to pass a [ReadSignal](https://docs.rs/leptos/latest/leptos/prelude/struct.ReadSignal.html) or\n///   [RwSignal](https://docs.rs/leptos/latest/leptos/prelude/struct.RwSignal.html)\n///   and automatically convert it.)\n/// * `#[prop(optional)]`: If the user does not specify this property when they use the component,\n///   it will be set to its default value. If the property type is `Option<T>`, values should be passed\n///   as `name=T` and will be received as `Some(T)`.\n/// * `#[prop(optional_no_strip)]`: The same as `optional`, but requires values to be passed as `None` or\n///   `Some(T)` explicitly. This means that the optional property can be omitted (and be `None`), or explicitly\n///   specified as either `None` or `Some(T)`.\n/// * `#[prop(default = <expr>)]`: Optional property that specifies a default value, which is used when the\n///   property is not specified.\n/// * `#[prop(name = \"new_name\")]`: Specifiy a different name for the property. Can be used to destructure\n///   fields in component function parameters (see example below).\n///\n/// ```rust\n/// # use leptos::prelude::*;\n///\n/// #[component]\n/// pub fn MyComponent(\n///     #[prop(into)] name: String,\n///     #[prop(optional)] optional_value: Option<i32>,\n///     #[prop(optional_no_strip)] optional_no_strip: Option<i32>,\n///     #[prop(default = 7)] optional_default: i32,\n///     #[prop(name = \"data\")] UserInfo { email, user_id }: UserInfo,\n/// ) -> impl IntoView {\n///     // whatever UI you need\n/// }\n///\n/// #[component]\n/// pub fn App() -> impl IntoView {\n///     view! {\n///       <MyComponent\n///         name=\"Greg\"  // automatically converted to String with `.into()`\n///         optional_value=42  // received as `Some(42)`\n///         optional_no_strip=Some(42)  // received as `Some(42)`\n///         optional_default=42  // received as `42`\n///         data=UserInfo {email: \"foo\", user_id: \"bar\" }\n///       />\n///       <MyComponent\n///         name=\"Bob\" // automatically converted to String with `.into()`\n///         data=UserInfo {email: \"foo\", user_id: \"bar\" }\n///         // optional values can be omitted\n///       />\n///     }\n/// }\n///\n/// pub struct UserInfo {\n///     pub email: &'static str,\n///     pub user_id: &'static str,\n/// }\n/// ```\n#[proc_macro_error2::proc_macro_error]\n#[proc_macro_attribute]\npub fn component(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n    let is_transparent = if !args.is_empty() {\n        let transparent = parse_macro_input!(args as syn::Ident);\n\n        if transparent != \"transparent\" {\n            abort!(\n                transparent,\n                \"only `transparent` is supported\";\n                help = \"try `#[component(transparent)]` or `#[component]`\"\n            );\n        }\n\n        true\n    } else {\n        false\n    };\n\n    component_macro(s, is_transparent, false, None)\n}\n\n/// Defines a component as an interactive island when you are using the\n/// `islands` feature of Leptos. Apart from the macro name,\n/// the API is the same as the [`component`](macro@component) macro.\n///\n/// When you activate the `islands` feature, every `#[component]`\n/// is server-only by default. This \"default to server\" behavior is important:\n/// you opt into shipping code to the client, rather than opting out. You can\n/// opt into client-side interactivity for any given component by changing from\n///  `#[component]` to `#[island]`—the two macros are otherwise identical.\n///\n/// Everything that is included inside an island will be compiled to WASM and\n/// shipped to the browser. So the key to really benefiting from this architecture\n/// is to make islands as small as possible, and include only the minimal\n/// required amount of functionality in islands themselves.\n///\n/// Only code included in an island itself is compiled to WASM. This means:\n/// 1. `children` can be provided from a server `#[component]` to an `#[island]`\n/// without the island needing to be able to hydrate them.\n/// 2. Props can be passed from the server to an island.\n///\n/// ## Present Limitations\n/// A few noteworthy limitations, at the moment:\n/// 1. `children` are completely opaque in islands. You can't iterate over `children`;\n/// in fact they're all bundled into a single `<leptos-children>` HTML element.\n/// 2. Similarly, `children` need to be used in the HTML rendered on the server.\n/// If they need to be displayed conditionally, they should be included in the HTML\n/// and rendered or not using `display: none` rather than `<Show>` or ordinary control flow.\n/// This is because the children aren't serialized at all, other than as HTML: if that\n/// HTML isn't present in the DOM, even if hidden, it is never sent and not available\n/// to the client at all.\n///\n/// ## Example\n/// ```rust,ignore\n/// use leptos::prelude::*;\n///\n/// #[component]\n/// pub fn App() -> impl IntoView {\n///     // this would panic if it ran in the browser\n///     // but because this isn't an island, it only runs on the server\n///     let file =\n///         std::fs::read_to_string(\"./src/is_this_a_server_component.txt\")\n///             .unwrap();\n///     let len = file.len();\n///\n///     view! {\n///         <p>\"The starting value for the button is the file's length.\"</p>\n///         // `value` is serialized and given to the island as a prop\n///         <Island value=len>\n///             // `file` is only available on the server\n///             // island props are projected in, so we can nest\n///             // server-only content inside islands inside server content etc.\n///             <p>{file}</p>\n///         </Island>\n///     }\n/// }\n///\n/// #[island]\n/// pub fn Island(\n///     #[prop(into)] value: RwSignal<usize>,\n///     children: Children,\n/// ) -> impl IntoView {\n///     // because `RwSignal<T>` implements `From<T>`, we can pass in a plain\n///     // value and use it as the starting value of a signal here\n///     view! {\n///         <button on:click=move |_| value.update(|n| *n += 1)>\n///             {value}\n///         </button>\n///         {children()}\n///     }\n/// }\n/// ```\n#[proc_macro_error2::proc_macro_error]\n#[proc_macro_attribute]\npub fn island(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n    let (is_transparent, is_lazy) = if !args.is_empty() {\n        let arg = parse_macro_input!(args as syn::Ident);\n\n        if arg != \"transparent\" && arg != \"lazy\" {\n            abort!(\n                arg,\n                \"only `transparent` or `lazy` are supported\";\n                help = \"try `#[island(transparent)]`, `#[island(lazy)]`, or `#[island]`\"\n            );\n        }\n\n        (arg == \"transparent\", arg == \"lazy\")\n    } else {\n        (false, false)\n    };\n\n    let island_src = s.to_string();\n    component_macro(s, is_transparent, is_lazy, Some(island_src))\n}\n\nfn component_macro(\n    s: TokenStream,\n    is_transparent: bool,\n    is_lazy: bool,\n    island: Option<String>,\n) -> TokenStream {\n    let mut dummy = syn::parse::<DummyModel>(s.clone());\n    let parse_result = syn::parse::<component::Model>(s);\n\n    if let (Ok(ref mut unexpanded), Ok(model)) = (&mut dummy, parse_result) {\n        let expanded = model\n            .is_transparent(is_transparent)\n            .is_lazy(is_lazy)\n            .with_island(island)\n            .into_token_stream();\n        if !matches!(unexpanded.vis, Visibility::Public(_)) {\n            unexpanded.vis = Visibility::Public(Pub {\n                span: unexpanded.vis.span(),\n            })\n        }\n        unexpanded.sig.ident =\n            unmodified_fn_name_from_fn_name(&unexpanded.sig.ident);\n\n        quote! {\n            #expanded\n\n            #[doc(hidden)]\n            #[allow(clippy::too_many_arguments, clippy::needless_lifetimes)]\n            #unexpanded\n        }\n    } else {\n        match dummy {\n            Ok(mut dummy) => {\n                dummy.sig.ident = unmodified_fn_name_from_fn_name(&dummy.sig.ident);\n                quote! {\n                    #[doc(hidden)]\n                    #[allow(clippy::too_many_arguments, clippy::needless_lifetimes)]\n                    #dummy\n                }\n            }\n            Err(e) => {\n                proc_macro_error2::abort!(e.span(), e);\n            }\n        }\n    }.into()\n}\n\n/// Annotates a struct so that it can be used with your Component as a `slot`.\n///\n/// The `#[slot]` macro allows you to annotate plain Rust struct as component slots and use them\n/// within your Leptos [`component`](macro@crate::component) properties. The struct can contain any number\n/// of fields. When you use the component somewhere else, the names of the slot fields are the\n/// names of the properties you use in the [view](crate::view!) macro.\n///\n/// Here’s how you would define and use a simple Leptos component which can accept a custom slot:\n/// ```rust\n/// # use leptos::prelude::*;\n/// use std::time::Duration;\n///\n/// #[slot]\n/// struct HelloSlot {\n///     // Same prop syntax as components.\n///     #[prop(optional)]\n///     children: Option<Children>,\n/// }\n///\n/// #[component]\n/// fn HelloComponent(\n///     /// Component slot, should be passed through the <HelloSlot slot> syntax.\n///     hello_slot: HelloSlot,\n/// ) -> impl IntoView {\n///     hello_slot.children.map(|children| children())\n/// }\n///\n/// #[component]\n/// fn App() -> impl IntoView {\n///     view! {\n///         <HelloComponent>\n///             <HelloSlot slot>\n///                 \"Hello, World!\"\n///             </HelloSlot>\n///         </HelloComponent>\n///     }\n/// }\n/// ```\n///\n/// /// Here are some important details about how slots work within the framework:\n/// 1. Most of the same rules from [`macro@component`] macro should also be followed on slots.\n///\n/// 2. Specifying only `slot` without a name (such as in `<HelloSlot slot>`) will default the chosen slot to\n/// the a snake case version of the slot struct name (`hello_slot` for `<HelloSlot>`).\n///\n/// 3. Event handlers cannot be specified directly on the slot.\n///\n/// ```compile_error\n/// // ❌ This won't work\n/// # use leptos::prelude::*;\n///\n/// #[slot]\n/// struct SlotWithChildren {\n///     children: Children,\n/// }\n///\n/// #[component]\n/// fn ComponentWithSlot(slot: SlotWithChildren) -> impl IntoView {\n///     (slot.children)()\n/// }\n///\n/// #[component]\n/// fn App() -> impl IntoView {\n///     view! {\n///         <ComponentWithSlot>\n///           <SlotWithChildren slot:slot on:click=move |_| {}>\n///             <h1>\"Hello, World!\"</h1>\n///           </SlotWithChildren>\n///         </ComponentWithSlot>\n///     }\n/// }\n/// ```\n///\n/// ```\n/// // ✅ Do this instead\n/// # use leptos::prelude::*;\n///\n/// #[slot]\n/// struct SlotWithChildren {\n///     children: Children,\n/// }\n///\n/// #[component]\n/// fn ComponentWithSlot(slot: SlotWithChildren) -> impl IntoView {\n///     (slot.children)()\n/// }\n///\n/// #[component]\n/// fn App() -> impl IntoView {\n///     view! {\n///         <ComponentWithSlot>\n///           <SlotWithChildren slot:slot>\n///             <div on:click=move |_| {}>\n///               <h1>\"Hello, World!\"</h1>\n///             </div>\n///           </SlotWithChildren>\n///         </ComponentWithSlot>\n///     }\n/// }\n/// ```\n#[proc_macro_error2::proc_macro_error]\n#[proc_macro_attribute]\npub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n    if !args.is_empty() {\n        abort!(\n            Span::call_site(),\n            \"no arguments are supported\";\n            help = \"try just `#[slot]`\"\n        );\n    }\n\n    parse_macro_input!(s as slot::Model)\n        .into_token_stream()\n        .into()\n}\n\n/// Declares that a function is a [server function](https://docs.rs/server_fn/latest/server_fn/index.html).\n/// This means that its body will only run on the server, i.e., when the `ssr` feature on this crate is enabled.\n///\n/// If you call a server function from the client (i.e., when the `csr` or `hydrate` features\n/// are enabled), it will instead make a network request to the server.\n///\n/// ## Named Arguments\n///\n/// You can provide any combination of the following named arguments:\n/// - `name`: sets the identifier for the server function’s type, which is a struct created\n///    to hold the arguments (defaults to the function identifier in PascalCase)\n/// - `prefix`: a prefix at which the server function handler will be mounted (defaults to `/api`)\n///    your prefix must begin with `/`. Otherwise your function won't be found.\n/// - `endpoint`: specifies the exact path at which the server function handler will be mounted,\n///   relative to the prefix (defaults to the function name followed by unique hash)\n/// - `input`: the encoding for the arguments (defaults to `PostUrl`)\n/// - `output`: the encoding for the response (defaults to `Json`)\n/// - `client`: a custom `Client` implementation that will be used for this server fn\n/// - `encoding`: (legacy, may be deprecated in future) specifies the encoding, which may be one\n///   of the following (not case sensitive)\n///     - `\"Url\"`: `POST` request with URL-encoded arguments and JSON response\n///     - `\"GetUrl\"`: `GET` request with URL-encoded arguments and JSON response\n///     - `\"Cbor\"`: `POST` request with CBOR-encoded arguments and response\n///     - `\"GetCbor\"`: `GET` request with URL-encoded arguments and CBOR response\n/// - `req` and `res` specify the HTTP request and response types to be used on the server (these\n///   should usually only be necessary if you are integrating with a server other than Actix/Axum)\n/// - `impl_from`: specifies whether to implement trait `From` for server function's type or not.\n///   By default, if a server function only has one argument, the macro automatically implements the `From` trait\n///   to convert from the argument type to the server function type, and vice versa, allowing you to convert\n///   between them easily. Setting `impl_from` to `false` disables this, which can be necessary for argument types\n///   for which this would create a conflicting implementation. (defaults to `true`)\n///\n/// ```rust,ignore\n/// #[server(\n///   name = SomeStructName,\n///   prefix = \"/my_api\",\n///   endpoint = \"my_fn\",\n///   input = Cbor,\n///   output = Json\n///   impl_from = true\n/// )]\n/// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<usize, ServerFnError> {\n///   todo!()\n/// }\n/// ```\n///\n/// ## Server Function Encodings\n///\n/// Server functions are designed to allow a flexible combination of `input` and `output` encodings, the set\n/// of which can be found in the [`server_fn::codec`](../server_fn/codec/index.html) module.\n///\n/// The serialization/deserialization process for server functions consists of a series of steps,\n/// each of which is represented by a different trait:\n/// 1. [`IntoReq`](../server_fn/codec/trait.IntoReq.html): The client serializes the [`ServerFn`](../server_fn/trait.ServerFn.html) argument type into an HTTP request.\n/// 2. The [`Client`](../server_fn/client/trait.Client.html) sends the request to the server.\n/// 3. [`FromReq`](../server_fn/codec/trait.FromReq.html): The server deserializes the HTTP request back into the [`ServerFn`](../server_fn/client/trait.Client.html) type.\n/// 4. The server calls calls [`ServerFn::run_body`](../server_fn/trait.ServerFn.html#tymethod.run_body) on the data.\n/// 5. [`IntoRes`](../server_fn/codec/trait.IntoRes.html): The server serializes the [`ServerFn::Output`](../server_fn/trait.ServerFn.html#associatedtype.Output) type into an HTTP response.\n/// 6. The server integration applies any middleware from [`ServerFn::middleware`](../server_fn/middleware/index.html) and responds to the request.\n/// 7. [`FromRes`](../server_fn/codec/trait.FromRes.html): The client deserializes the response back into the [`ServerFn::Output`](../server_fn/trait.ServerFn.html#associatedtype.Output) type.\n///\n/// Whatever encoding is provided to `input` should implement `IntoReq` and `FromReq`. Whatever encoding is provided\n/// to `output` should implement `IntoRes` and `FromRes`.\n///\n/// ## Default Values for Parameters\n///\n/// Individual function parameters can be annotated with `#[server(default)]`, which will pass\n/// through `#[serde(default)]`. This is useful for the empty values of arguments with some\n/// encodings. The URL encoding, for example, omits a field entirely if it is an empty `Vec<_>`,\n/// but this causes a deserialization error: the correct solution is to add `#[server(default)]`.\n/// ```rust,ignore\n/// pub async fn with_default_value(#[server(default)] values: Vec<u32>) /* etc. */\n/// ```\n///\n/// ## Important Notes\n/// - **Server functions must be `async`.** Even if the work being done inside the function body\n///   can run synchronously on the server, from the client’s perspective it involves an asynchronous\n///   function call.\n/// - **Server functions must return `Result<T, ServerFnError>`.** Even if the work being done\n///   inside the function body can’t fail, the processes of serialization/deserialization and the\n///   network call are fallible.\n///     - [`ServerFnError`](../server_fn/error/enum.ServerFnError.html) can be generic over some custom error type. If so, that type should implement\n///       [`FromStr`](std::str::FromStr) and [`Display`](std::fmt::Display), but does not need to implement [`Error`](std::error::Error). This is so the value\n///       can be easily serialized and deserialized along with the result.\n/// - **Server functions are part of the public API of your application.** A server function is an\n///   ad hoc HTTP API endpoint, not a magic formula. Any server function can be accessed by any HTTP\n///   client. You should take care to sanitize any data being returned from the function to ensure it\n///   does not leak data that should exist only on the server.\n/// - **Server functions can’t be generic.** Because each server function creates a separate API endpoint,\n///   it is difficult to monomorphize. As a result, server functions cannot be generic (for now?) If you need to use\n///   a generic function, you can define a generic inner function called by multiple concrete server functions.\n/// - **Arguments and return types must be serializable.** We support a variety of different encodings,\n///   but one way or another arguments need to be serialized to be sent to the server and deserialized\n///   on the server, and the return type must be serialized on the server and deserialized on the client.\n///   This means that the set of valid server function argument and return types is a subset of all\n///   possible Rust argument and return types. (i.e., server functions are strictly more limited than\n///   ordinary functions.)\n/// - **Context comes from the server.** Server functions are provided access to the HTTP request and other relevant\n///   server data via the server integrations, but they do *not* have access to reactive state that exists in the client.\n/// - Your server must be ready to handle the server functions at the API prefix you list. The easiest way to do this\n///   is to use the `handle_server_fns` function from [`leptos_actix`](https://docs.rs/leptos_actix/latest/leptos_actix/fn.handle_server_fns.html)\n///   or [`leptos_axum`](https://docs.rs/leptos_axum/latest/leptos_axum/fn.handle_server_fns.html).\n/// - **Server functions must have unique paths**. Unique paths are automatically generated for each\n///   server function. If you choose to specify a path in the fourth argument, you must ensure that these\n///   are unique. You cannot define two server functions with the same URL prefix and endpoint path,\n///   even if they have different URL encodings, e.g. a POST method at `/api/foo` and a GET method at `/api/foo`.\n#[proc_macro_attribute]\n#[proc_macro_error]\npub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n    match server_fn_macro::server_macro_impl(\n        args.into(),\n        s.into(),\n        Some(syn::parse_quote!(::leptos::server_fn)),\n        option_env!(\"SERVER_FN_PREFIX\").unwrap_or(\"/api\"),\n        None,\n        None,\n    ) {\n        Err(e) => e.to_compile_error().into(),\n        Ok(s) => s.to_token_stream().into(),\n    }\n}\n\n/// Derives a trait that parses a map of string keys and values into a typed\n/// data structure, e.g., for route params.\n#[proc_macro_derive(Params)]\npub fn params_derive(\n    input: proc_macro::TokenStream,\n) -> proc_macro::TokenStream {\n    match syn::parse(input) {\n        Ok(ast) => params::params_impl(&ast),\n        Err(err) => err.to_compile_error().into(),\n    }\n}\n\n/// Generates a `slice` into a struct with a default getter and setter.\n///\n/// Can be used to access deeply nested fields within a global state object.\n///\n/// ```rust\n/// # use leptos::prelude::*;\n/// # use leptos_macro::slice;\n///\n/// #[derive(Default)]\n/// pub struct Outer {\n///     count: i32,\n///     inner: Inner,\n/// }\n///\n/// #[derive(Default)]\n/// pub struct Inner {\n///     inner_count: i32,\n///     inner_name: String,\n/// }\n///\n/// let outer_signal = RwSignal::new(Outer::default());\n///\n/// let (count, set_count) = slice!(outer_signal.count);\n///\n/// let (inner_count, set_inner_count) = slice!(outer_signal.inner.inner_count);\n/// let (inner_name, set_inner_name) = slice!(outer_signal.inner.inner_name);\n/// ```\n#[proc_macro]\npub fn slice(input: TokenStream) -> TokenStream {\n    slice::slice_impl(input)\n}\n\n/// Generates a `memo` into a struct with a default getter.\n///\n/// Can be used to access deeply nested fields within a global state object.\n///\n/// ```rust\n/// # use leptos::prelude::*;\n/// # use leptos_macro::memo;\n///\n/// #[derive(Default)]\n/// pub struct Outer {\n///     count: i32,\n///     inner: Inner,\n/// }\n///\n/// #[derive(Default)]\n/// pub struct Inner {\n///     inner_count: i32,\n///     inner_name: String,\n/// }\n///\n/// let outer_signal = RwSignal::new(Outer::default());\n///\n/// let count = memo!(outer_signal.count);\n///\n/// let inner_count = memo!(outer_signal.inner.inner_count);\n/// let inner_name = memo!(outer_signal.inner.inner_name);\n/// ```\n#[proc_macro]\npub fn memo(input: TokenStream) -> TokenStream {\n    memo::memo_impl(input)\n}\n\n/// The `#[lazy]` macro indicates that a function can be lazy-loaded from a separate WebAssembly (WASM) binary.\n///\n/// The first time the function is called, calling the function will first load that other binary,\n/// then call the function. On subsequent calls it will be called immediately, but still return\n/// asynchronously to maintain the same API.\n///\n/// `#[lazy]` can be used to annotate synchronous or `async` functions. In both cases, the final function will be\n/// `async` and must be called as such.\n///\n/// All parameters and output types should be concrete types, with no generics or `impl Trait` types.\n///\n/// This should be used in tandem with a suitable build process, such as `cargo leptos --split`.\n///\n/// ```rust\n/// # use leptos_macro::lazy;\n///\n/// #[lazy]\n/// fn lazy_synchronous_function() -> String {\n///     \"Hello, lazy world!\".to_string()\n/// }\n///\n/// #[lazy]\n/// async fn lazy_async_function() -> String {\n///     /* do something that requires async work */\n///     \"Hello, lazy async world!\".to_string()\n/// }\n///\n/// async fn use_lazy_functions() {\n///     // synchronous function has been converted to async\n///     let value1 = lazy_synchronous_function().await;\n///\n///     // async function is still async\n///     let value1 = lazy_async_function().await;\n/// }\n/// ```\n#[proc_macro_attribute]\n#[proc_macro_error]\npub fn lazy(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n    lazy::lazy_impl(args, s)\n}\n"
  },
  {
    "path": "leptos_macro/src/memo.rs",
    "content": "extern crate proc_macro;\n\nuse proc_macro::TokenStream;\nuse quote::{quote, ToTokens};\nuse syn::{\n    parse::{Parse, ParseStream},\n    parse_macro_input,\n    punctuated::Punctuated,\n    Token,\n};\n\nstruct MemoMacroInput {\n    root: syn::Ident,\n    path: Punctuated<syn::Member, Token![.]>,\n}\n\nimpl Parse for MemoMacroInput {\n    fn parse(input: ParseStream) -> syn::Result<Self> {\n        let root: syn::Ident = input.parse()?;\n        input.parse::<Token![.]>()?;\n        // do not accept trailing punctuation\n        let path: Punctuated<syn::Member, Token![.]> =\n            Punctuated::parse_separated_nonempty(input)?;\n\n        if path.is_empty() {\n            return Err(input.error(\"expected identifier\"));\n        }\n\n        if !input.is_empty() {\n            return Err(input.error(\"unexpected token\"));\n        }\n\n        Ok(Self { root, path })\n    }\n}\n\nimpl ToTokens for MemoMacroInput {\n    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {\n        let root = &self.root;\n        let path = &self.path;\n\n        tokens.extend(quote! {\n            ::leptos::reactive::computed::Memo::new(\n                move |_| {\n                    use ::leptos::reactive::traits::With;\n                    #root.with(|st: _| st.#path.clone())\n                }\n            )\n        })\n    }\n}\n\npub fn memo_impl(tokens: TokenStream) -> TokenStream {\n    let input = parse_macro_input!(tokens as MemoMacroInput);\n    input.into_token_stream().into()\n}\n"
  },
  {
    "path": "leptos_macro/src/params.rs",
    "content": "use quote::{quote, quote_spanned};\nuse syn::spanned::Spanned;\n\npub fn params_impl(ast: &syn::DeriveInput) -> proc_macro::TokenStream {\n    let name = &ast.ident;\n\n    let fields = if let syn::Data::Struct(syn::DataStruct {\n        fields: syn::Fields::Named(ref fields),\n        ..\n    }) = ast.data\n    {\n        fields\n            .named\n            .iter()\n            .map(|field| {\n\t\t\t\tlet field_name_string = &field\n                    .ident\n                    .as_ref()\n                    .expect(\"expected named struct fields\")\n                    .to_string()\n                    .trim_start_matches(\"r#\")\n                    .to_owned();\n\t\t\t\tlet ident = &field.ident;\n\t\t\t\tlet ty = &field.ty;\n\t\t\t\tlet span = field.span();\n\n\t\t\t\tquote_spanned! {\n\t\t\t\t\tspan=> #ident: ::leptos_router::params::macro_helpers::Wrapper::<#ty>::__into_param(\n                        map.get_str(#field_name_string),\n                        #field_name_string\n                    )?\n\t\t\t\t}\n\t\t\t})\n            .collect()\n    } else {\n        vec![]\n    };\n\n    let gen = quote! {\n        impl Params for #name {\n            fn from_map(map: &::leptos_router::params::ParamsMap) -> ::core::result::Result<Self, ::leptos_router::params::ParamsError> {\n                use ::leptos_router::params::macro_helpers::Fallback as _;\n\n                Ok(Self {\n                    #(#fields,)*\n                })\n            }\n        }\n    };\n    gen.into()\n}\n"
  },
  {
    "path": "leptos_macro/src/slice.rs",
    "content": "extern crate proc_macro;\n\nuse proc_macro::TokenStream;\nuse quote::{quote, ToTokens};\nuse syn::{\n    parse::{Parse, ParseStream},\n    parse_macro_input,\n    punctuated::Punctuated,\n    Token,\n};\n\nstruct SliceMacroInput {\n    root: syn::Ident,\n    path: Punctuated<syn::Member, Token![.]>,\n}\n\nimpl Parse for SliceMacroInput {\n    fn parse(input: ParseStream) -> syn::Result<Self> {\n        let root: syn::Ident = input.parse()?;\n        input.parse::<Token![.]>()?;\n        // do not accept trailing punctuation\n        let path: Punctuated<syn::Member, Token![.]> =\n            Punctuated::parse_separated_nonempty(input)?;\n\n        if path.is_empty() {\n            return Err(input.error(\"expected identifier\"));\n        }\n\n        if !input.is_empty() {\n            return Err(input.error(\"unexpected token\"));\n        }\n\n        Ok(Self { root, path })\n    }\n}\n\nimpl ToTokens for SliceMacroInput {\n    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {\n        let root = &self.root;\n        let path = &self.path;\n\n        tokens.extend(quote! {\n            ::leptos::reactive::computed::create_slice(\n                #root,\n                |st: &_| st.#path.clone(),\n                |st: &mut _, n| st.#path = n\n            )\n        })\n    }\n}\n\npub fn slice_impl(tokens: TokenStream) -> TokenStream {\n    let input = parse_macro_input!(tokens as SliceMacroInput);\n    input.into_token_stream().into()\n}\n"
  },
  {
    "path": "leptos_macro/src/slot.rs",
    "content": "use crate::component::{\n    convert_from_snake_case, drain_filter, is_option, unwrap_option, Docs,\n};\nuse attribute_derive::FromAttr;\nuse proc_macro2::{Ident, TokenStream};\nuse quote::{quote, ToTokens, TokenStreamExt};\nuse syn::{\n    parse::Parse, parse_quote, Field, ItemStruct, LitStr, Meta, Type,\n    Visibility,\n};\n\npub struct Model {\n    docs: Docs,\n    vis: Visibility,\n    name: Ident,\n    props: Vec<Prop>,\n    body: ItemStruct,\n}\n\nimpl Parse for Model {\n    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {\n        let mut item = ItemStruct::parse(input)?;\n\n        let docs = Docs::new(&item.attrs);\n\n        let props = item\n            .fields\n            .clone()\n            .into_iter()\n            .map(Prop::new)\n            .collect::<Vec<_>>();\n\n        // We need to remove the `#[doc = \"\"]` and `#[builder(_)]`\n        // attrs from the function signature\n        drain_filter(&mut item.attrs, |attr| match &attr.meta {\n            Meta::NameValue(attr) => attr.path == parse_quote!(doc),\n            Meta::List(attr) => attr.path == parse_quote!(prop),\n            _ => false,\n        });\n        item.fields.iter_mut().for_each(|arg| {\n            drain_filter(&mut arg.attrs, |attr| match &attr.meta {\n                Meta::NameValue(attr) => attr.path == parse_quote!(doc),\n                Meta::List(attr) => attr.path == parse_quote!(prop),\n                _ => false,\n            });\n        });\n\n        Ok(Self {\n            docs,\n            vis: item.vis.clone(),\n            name: convert_from_snake_case(&item.ident),\n            props,\n            body: item,\n        })\n    }\n}\n\nimpl ToTokens for Model {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        let Self {\n            docs,\n            vis,\n            name,\n            props,\n            body,\n        } = self;\n\n        let (_, generics, where_clause) = body.generics.split_for_impl();\n\n        let prop_builder_fields = prop_builder_fields(vis, props);\n        let prop_docs = generate_prop_docs(props);\n        let builder_name_doc = LitStr::new(\n            &format!(\"Props for the [`{name}`] slot.\"),\n            name.span(),\n        );\n\n        let output = quote! {\n            #[doc = #builder_name_doc]\n            #[doc = \"\"]\n            #docs\n            #prop_docs\n            #[derive(::leptos::typed_builder_macro::TypedBuilder)]\n            #[builder(doc, crate_module_path=::leptos::typed_builder)]\n            #vis struct #name #generics #where_clause {\n                #prop_builder_fields\n            }\n\n            impl #generics From<#name #generics> for Vec<#name #generics> #where_clause {\n                fn from(value: #name #generics) -> Self {\n                    vec![value]\n                }\n            }\n\n            /*impl #impl_generics ::leptos::Props for #name #generics #where_clause {\n                type Builder = #builder_name #generics;\n                fn builder() -> Self::Builder {\n                    #name::builder()\n                }\n            }\n\n            impl #impl_generics ::leptos::DynAttrs for #name #generics #where_clause {\n                fn dyn_attrs(mut self, v: Vec<(&'static str, ::leptos::Attribute)>) -> Self {\n                    #dyn_attrs_props\n                    self\n                }\n            }*/\n        };\n\n        tokens.append_all(output)\n    }\n}\n\nstruct Prop {\n    docs: Docs,\n    prop_opts: PropOpt,\n    name: Ident,\n    ty: Type,\n}\n\nimpl Prop {\n    fn new(arg: Field) -> Self {\n        let prop_opts =\n            PropOpt::from_attributes(&arg.attrs).unwrap_or_else(|e| {\n                // TODO: replace with `.unwrap_or_abort()` once https://gitlab.com/CreepySkeleton/proc-macro-error/-/issues/17 is fixed\n                abort!(e.span(), e.to_string());\n            });\n\n        let name = if let Some(i) = arg.ident {\n            i\n        } else {\n            abort!(\n                arg.ident,\n                \"only `prop: bool` style types are allowed within the \\\n                 `#[slot]` macro\"\n            );\n        };\n\n        Self {\n            docs: Docs::new(&arg.attrs),\n            prop_opts,\n            name,\n            ty: arg.ty,\n        }\n    }\n}\n\n#[derive(Clone, Debug, FromAttr)]\n#[attribute(ident = prop)]\nstruct PropOpt {\n    #[attribute(conflicts = [optional_no_strip, strip_option])]\n    pub optional: bool,\n    #[attribute(conflicts = [optional, strip_option])]\n    pub optional_no_strip: bool,\n    #[attribute(conflicts = [optional, optional_no_strip])]\n    pub strip_option: bool,\n    #[attribute(example = \"5 * 10\")]\n    pub default: Option<syn::Expr>,\n    pub into: bool,\n    pub attrs: bool,\n}\n\nstruct TypedBuilderOpts<'a> {\n    default: bool,\n    default_with_value: Option<syn::Expr>,\n    strip_option: bool,\n    into: bool,\n    ty: &'a Type,\n}\n\nimpl<'a> TypedBuilderOpts<'a> {\n    pub fn from_opts(opts: &PropOpt, ty: &'a Type) -> Self {\n        Self {\n            default: opts.optional || opts.optional_no_strip || opts.attrs,\n            default_with_value: opts.default.clone(),\n            strip_option: opts.strip_option || opts.optional && is_option(ty),\n            into: opts.into,\n            ty,\n        }\n    }\n}\n\nimpl ToTokens for TypedBuilderOpts<'_> {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        let default = if let Some(v) = &self.default_with_value {\n            let v = v.to_token_stream().to_string();\n            quote! { default_code=#v, }\n        } else if self.default {\n            quote! { default, }\n        } else {\n            quote! {}\n        };\n\n        // If self.strip_option && self.into, then the strip_option will be represented as part of the transform closure.\n        let strip_option = if self.strip_option && !self.into {\n            quote! { strip_option, }\n        } else {\n            quote! {}\n        };\n\n        let into = if self.into {\n            if !self.strip_option {\n                let ty = &self.ty;\n                quote! {\n                    fn transform<__IntoReactiveValueMarker>(value: impl ::leptos::prelude::IntoReactiveValue<#ty, __IntoReactiveValueMarker>) -> #ty {\n                        value.into_reactive_value()\n                    },\n                }\n            } else {\n                let ty = unwrap_option(self.ty);\n                quote! {\n                    fn transform<__IntoReactiveValueMarker>(value: impl ::leptos::prelude::IntoReactiveValue<#ty, __IntoReactiveValueMarker>) -> Option<#ty> {\n                        Some(value.into_reactive_value())\n                    },\n                }\n            }\n        } else {\n            quote! {}\n        };\n\n        let setter = if !strip_option.is_empty() || !into.is_empty() {\n            quote! { setter(#strip_option #into) }\n        } else {\n            quote! {}\n        };\n\n        let output = if !default.is_empty() || !setter.is_empty() {\n            quote! { #[builder(#default #setter)] }\n        } else {\n            quote! {}\n        };\n\n        tokens.append_all(output);\n    }\n}\n\nfn prop_builder_fields(vis: &Visibility, props: &[Prop]) -> TokenStream {\n    props\n        .iter()\n        .map(|prop| {\n            let Prop {\n                docs,\n                name,\n                prop_opts,\n                ty,\n            } = prop;\n\n            let builder_attrs = TypedBuilderOpts::from_opts(prop_opts, ty);\n\n            let builder_docs = prop_to_doc(prop, PropDocStyle::Inline);\n\n            quote! {\n                #docs\n                #builder_docs\n                #builder_attrs\n                #vis #name: #ty,\n            }\n        })\n        .collect()\n}\n\nfn generate_prop_docs(props: &[Prop]) -> TokenStream {\n    let required_prop_docs = props\n        .iter()\n        .filter(|Prop { prop_opts, .. }| {\n            !(prop_opts.optional || prop_opts.optional_no_strip)\n        })\n        .map(|p| prop_to_doc(p, PropDocStyle::List))\n        .collect::<TokenStream>();\n\n    let optional_prop_docs = props\n        .iter()\n        .filter(|Prop { prop_opts, .. }| {\n            prop_opts.optional || prop_opts.optional_no_strip\n        })\n        .map(|p| prop_to_doc(p, PropDocStyle::List))\n        .collect::<TokenStream>();\n\n    let required_prop_docs = if !required_prop_docs.is_empty() {\n        quote! {\n            #[doc = \"# Required Props\"]\n            #required_prop_docs\n        }\n    } else {\n        quote! {}\n    };\n\n    let optional_prop_docs = if !optional_prop_docs.is_empty() {\n        quote! {\n            #[doc = \"# Optional Props\"]\n            #optional_prop_docs\n        }\n    } else {\n        quote! {}\n    };\n\n    quote! {\n        #required_prop_docs\n        #optional_prop_docs\n    }\n}\n\n#[derive(Clone, Copy)]\nenum PropDocStyle {\n    List,\n    Inline,\n}\n\nfn prop_to_doc(\n    Prop {\n        docs,\n        name,\n        ty,\n        prop_opts,\n    }: &Prop,\n    style: PropDocStyle,\n) -> TokenStream {\n    let ty = if (prop_opts.optional || prop_opts.strip_option) && is_option(ty)\n    {\n        unwrap_option(ty)\n    } else {\n        ty.to_owned()\n    };\n\n    let type_item: syn::Item = parse_quote! {\n        type SomeType = #ty;\n    };\n\n    let file = syn::File {\n        shebang: None,\n        attrs: vec![],\n        items: vec![type_item],\n    };\n\n    let pretty_ty = prettyplease::unparse(&file);\n\n    let pretty_ty = &pretty_ty[16..&pretty_ty.len() - 2];\n\n    match style {\n        PropDocStyle::List => {\n            let arg_ty_doc = LitStr::new(\n                &if !prop_opts.into {\n                    format!(\"- **{}**: [`{}`]\", quote!(#name), pretty_ty)\n                } else {\n                    format!(\n                        \"- **{}**: `impl`[`Into<{}>`]\",\n                        quote!(#name),\n                        pretty_ty\n                    )\n                },\n                name.span(),\n            );\n\n            let arg_user_docs = docs.padded();\n\n            quote! {\n                #[doc = #arg_ty_doc]\n                #arg_user_docs\n            }\n        }\n        PropDocStyle::Inline => {\n            let arg_ty_doc = LitStr::new(\n                &if !prop_opts.into {\n                    format!(\n                        \"**{}**: [`{}`]{}\",\n                        quote!(#name),\n                        pretty_ty,\n                        docs.typed_builder()\n                    )\n                } else {\n                    format!(\n                        \"**{}**: `impl`[`Into<{}>`]{}\",\n                        quote!(#name),\n                        pretty_ty,\n                        docs.typed_builder()\n                    )\n                },\n                name.span(),\n            );\n\n            quote! {\n                #[builder(setter(doc = #arg_ty_doc))]\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "leptos_macro/src/view/component_builder.rs",
    "content": "use super::{\n    fragment_to_tokens, utils::is_nostrip_optional_and_update_key, TagType,\n};\nuse crate::view::{\n    attribute_absolute, text_to_tokens, utils::filter_prefixed_attrs,\n};\nuse proc_macro2::{Ident, TokenStream, TokenTree};\nuse quote::{format_ident, quote, quote_spanned};\nuse rstml::node::{\n    CustomNode, KeyedAttributeValue, Node, NodeAttribute, NodeBlock,\n    NodeElement, NodeName,\n};\nuse std::collections::HashMap;\nuse syn::{\n    spanned::Spanned, Expr, ExprPath, ExprRange, Item, RangeLimits, Stmt,\n};\n\npub(crate) fn component_to_tokens(\n    node: &mut NodeElement<impl CustomNode>,\n    global_class: Option<&TokenTree>,\n    disable_inert_html: bool,\n) -> TokenStream {\n    #[allow(unused)] // TODO this is used by hot-reloading\n    #[cfg(debug_assertions)]\n    let component_name = super::ident_from_tag_name(node.name());\n\n    // an attribute that contains {..} can be used to split props from attributes\n    // anything before it is a prop, unless it uses the special attribute syntaxes\n    // (attr:, style:, on:, prop:, etc.)\n    // anything after it is a plain HTML attribute to be spread onto the prop\n    let spread_marker = node\n        .attributes()\n        .iter()\n        .position(|node| match node {\n            NodeAttribute::Block(NodeBlock::ValidBlock(block)) => {\n                matches!(\n                    block.stmts.first(),\n                    Some(Stmt::Expr(\n                        Expr::Range(ExprRange {\n                            start: None,\n                            limits: RangeLimits::HalfOpen(_),\n                            end: None,\n                            ..\n                        }),\n                        _,\n                    ))\n                )\n            }\n            _ => false,\n        })\n        .unwrap_or_else(|| node.attributes().len());\n\n    // Initially using uncloned mutable reference, as the node.key might be mutated during prop extraction (for nostrip:)\n    let mut attrs = node\n        .attributes_mut()\n        .iter_mut()\n        .filter_map(|node| {\n            if let NodeAttribute::Attribute(node) = node {\n                Some(node)\n            } else {\n                None\n            }\n        })\n        .collect::<Vec<_>>();\n\n    let mut required_props = vec![];\n    let mut optional_props = vec![];\n    for (_, attr) in attrs.iter_mut().enumerate().filter(|(idx, attr)| {\n        idx < &spread_marker && {\n            let attr_key = attr.key.to_string();\n            !is_attr_let(&attr.key)\n                && !attr_key.starts_with(\"clone:\")\n                && !attr_key.starts_with(\"class:\")\n                && !attr_key.starts_with(\"style:\")\n                && !attr_key.starts_with(\"attr:\")\n                && !attr_key.starts_with(\"prop:\")\n                && !attr_key.starts_with(\"on:\")\n                && !attr_key.starts_with(\"use:\")\n        }\n    }) {\n        let optional = is_nostrip_optional_and_update_key(&mut attr.key);\n        let name = &attr.key;\n\n        let value = attr\n            .value()\n            .map(|v| {\n                quote! { #v }\n            })\n            .unwrap_or_else(|| quote! { #name });\n\n        if optional {\n            optional_props.push(quote! {\n                props.#name = { #value }.map(::leptos::prelude::IntoReactiveValue::into_reactive_value);\n            })\n        } else {\n            required_props.push(quote! {\n                .#name(#[allow(unused_braces)] { #value })\n            })\n        }\n    }\n\n    // Drop the mutable reference to the node, go to an owned clone:\n    let attrs = attrs.into_iter().map(|a| a.clone()).collect::<Vec<_>>();\n\n    let items_to_bind = attrs\n        .iter()\n        .filter_map(|attr| {\n            if !is_attr_let(&attr.key) {\n                return None;\n            }\n\n            let KeyedAttributeValue::Binding(binding) = &attr.possible_value\n            else {\n                if let Some(ident) = attr.key.to_string().strip_prefix(\"let:\") {\n                    let span = match &attr.key {\n                        NodeName::Punctuated(path) => path[1].span(),\n                        _ => unreachable!(),\n                    };\n                    let ident1 = format_ident!(\"{ident}\", span = span);\n                    return Some(quote_spanned! { span => #ident1 });\n                } else {\n                    return None;\n                }\n            };\n\n            let inputs = &binding.inputs;\n            Some(quote! { #inputs })\n        })\n        .collect::<Vec<_>>();\n\n    let items_to_clone = filter_prefixed_attrs(attrs.iter(), \"clone:\");\n\n    // include all attribute that are either\n    // 1) blocks ({..attrs} or {attrs}),\n    // 2) start with attr: and can be used as actual attributes, or\n    // 3) the custom attribute types (on:, class:, style:, prop:, use:)\n    let spreads = node\n        .attributes()\n        .iter()\n        .enumerate()\n        .filter_map(|(idx, attr)| {\n            if idx == spread_marker {\n                return None;\n            }\n\n            if let NodeAttribute::Block(block) = attr {\n                let dotted = if let NodeBlock::ValidBlock(block) = block {\n                    match block.stmts.first() {\n                        Some(Stmt::Expr(\n                            Expr::Range(ExprRange {\n                                start: None,\n                                limits: RangeLimits::HalfOpen(_),\n                                end: Some(end),\n                                ..\n                            }),\n                            _,\n                        )) => Some(quote! { #end }),\n                        _ => None,\n                    }\n                } else {\n                    None\n                };\n                Some(dotted.unwrap_or_else(|| {\n                    quote! {\n                        #node\n                    }\n                }))\n            } else if let NodeAttribute::Attribute(node) = attr {\n                attribute_absolute(node, idx >= spread_marker)\n            } else {\n                None\n            }\n        })\n        .collect::<Vec<_>>();\n\n    let spreads = (!(spreads.is_empty())).then(|| {\n        if cfg!(feature = \"__internal_erase_components\") {\n            quote! {\n                .add_any_attr({\n                    vec![#(::leptos::attr::any_attribute::IntoAnyAttribute::into_any_attr(#spreads),)*]\n                })\n            }\n        } else {\n            quote! {\n                .add_any_attr((#(#spreads,)*))\n            }\n        }\n    });\n\n    /*let directives = attrs\n        .clone()\n        .filter_map(|attr| {\n            attr.key\n                .to_string()\n                .strip_prefix(\"use:\")\n                .map(|ident| directive_call_from_attribute_node(attr, ident))\n        })\n        .collect::<Vec<_>>();\n\n    let events_and_directives =\n        events.into_iter().chain(directives).collect::<Vec<_>>(); */\n\n    let mut slots = HashMap::new();\n    let children = if node.children.is_empty() {\n        quote! {}\n    } else if let Some(children) = maybe_optimised_component_children(\n        &node.children,\n        &items_to_bind,\n        &items_to_clone,\n    ) {\n        children\n    } else {\n        let children = fragment_to_tokens(\n            &mut node.children,\n            TagType::Unknown,\n            Some(&mut slots),\n            global_class,\n            None,\n            disable_inert_html,\n        );\n\n        // TODO view marker for hot-reloading\n        /*\n        cfg_if::cfg_if! {\n            if #[cfg(debug_assertions)] {\n                let marker = format!(\"<{component_name}/>-children\");\n                // For some reason spanning for `.children` breaks, unless `#view_marker`\n                // is also covered by `children.span()`.\n                let view_marker = quote_spanned!(children.span()=> .with_view_marker(#marker));\n            } else {\n                let view_marker = quote! {};\n            }\n        }\n        */\n\n        if let Some(children) = children {\n            let bindables =\n                items_to_bind.iter().map(|ident| quote! { #ident, });\n\n            let clonables = items_to_clone_to_tokens(&items_to_clone);\n\n            if bindables.len() > 0 {\n                quote_spanned! {children.span()=>\n                    .children({\n                        #(#clonables)*\n\n                        move |#(#bindables)*| #children\n                    })\n                }\n            } else {\n                quote_spanned! {children.span()=>\n                    .children({\n                        #(#clonables)*\n\n                        ::leptos::children::ToChildren::to_children(move || #children)\n                    })\n                }\n            }\n        } else {\n            quote! {}\n        }\n    };\n\n    let slots = slots.drain().map(|(slot, mut values)| {\n        let span = values\n            .last()\n            .expect(\"List of slots must not be empty\")\n            .span();\n        let slot = Ident::new(&slot, span);\n        let value = if values.len() > 1 {\n            quote_spanned! {span=>\n                ::std::vec![\n                    #(#values)*\n                ]\n            }\n        } else {\n            values.remove(0)\n        };\n\n        quote! { .#slot(#value) }\n    });\n\n    let generics = &node.open_tag.generics;\n    let generics = if generics.lt_token.is_some() {\n        quote! { ::#generics }\n    } else {\n        quote! {}\n    };\n\n    let name = node.name();\n    #[allow(unused_mut)] // used in debug\n    let mut component = quote! {\n        {\n            #[allow(unreachable_code)]\n            #[allow(unused_mut)]\n            #[allow(clippy::let_and_return)]\n            ::leptos::component::component_view(\n                #[allow(clippy::needless_borrows_for_generic_args)]\n                &#name,\n                {\n                    let mut props = ::leptos::component::component_props_builder(&#name #generics)\n                        #(#required_props)*\n                        #(#slots)*\n                        #children\n                        .build();\n                    #(#optional_props)*\n                    props\n                }\n            )\n            #spreads\n        }\n    };\n\n    // (Temporarily?) removed\n    // See note on the function itself below.\n    /* #[cfg(debug_assertions)]\n    IdeTagHelper::add_component_completion(&mut component, node); */\n\n    component\n}\n\nfn is_attr_let(key: &NodeName) -> bool {\n    if key.to_string().starts_with(\"let:\") {\n        true\n    } else if let NodeName::Path(ExprPath { path, .. }) = key {\n        path.segments.len() == 1 && path.segments[0].ident == \"let\"\n    } else {\n        false\n    }\n}\n\npub fn items_to_clone_to_tokens(\n    items_to_clone: &[Ident],\n) -> impl Iterator<Item = TokenStream> + '_ {\n    items_to_clone.iter().map(|ident| {\n        let ident_ref = quote_spanned!(ident.span()=> &#ident);\n        quote! { let #ident = ::core::clone::Clone::clone(#ident_ref); }\n    })\n}\n\n/// By default all children are placed in an outer closure || #children.\n/// This is to work with all the variants of the leptos::children::ToChildren::to_children trait.\n/// Strings are optimised to be passed without the wrapping closure, providing significant compile time and binary size improvements.\npub fn maybe_optimised_component_children(\n    children: &[Node<impl CustomNode>],\n    items_to_bind: &[TokenStream],\n    items_to_clone: &[Ident],\n) -> Option<TokenStream> {\n    // If there are bindables will have to be in a closure:\n    if !items_to_bind.is_empty() {\n        return None;\n    }\n\n    // Filter out comments:\n    let mut children_iter = children\n        .iter()\n        .filter(|child| !matches!(child, Node::Comment(_)));\n\n    let children = if let Some(child) = children_iter.next() {\n        // If more than one child after filtering out comments, don't think we can optimise:\n        if children_iter.next().is_some() {\n            return None;\n        }\n        match child {\n            Node::Text(text) => text_to_tokens(&text.value),\n            Node::RawText(raw) => {\n                let text = raw.to_string_best();\n                let text = syn::LitStr::new(&text, raw.span());\n                text_to_tokens(&text)\n            }\n            // Specifically allow std macros that produce strings:\n            Node::Block(NodeBlock::ValidBlock(block)) => {\n                fn is_supported(mac: &syn::Macro) -> bool {\n                    for string_macro in [\"format\", \"include_str\"] {\n                        if mac.path.is_ident(string_macro) {\n                            return true;\n                        }\n                    }\n                    false\n                }\n                if block.stmts.len() > 1 {\n                    return None;\n                } else if let Some(stmt) = block.stmts.first() {\n                    match stmt {\n                        Stmt::Macro(mac) => {\n                            // eprintln!(\"Macro: {:?}\", mac.mac.path);\n                            if is_supported(&mac.mac) {\n                                quote! { #block }\n                            } else {\n                                return None;\n                            }\n                        }\n                        Stmt::Item(Item::Macro(mac)) => {\n                            // eprintln!(\"Item Macro: {:?}\", mac.mac.path);\n                            if is_supported(&mac.mac) {\n                                quote! { #block }\n                            } else {\n                                return None;\n                            }\n                        }\n                        Stmt::Expr(Expr::Macro(mac), _) => {\n                            // eprintln!(\"Expr Macro: {:?}\", mac.mac.path);\n                            if is_supported(&mac.mac) {\n                                quote! { #block }\n                            } else {\n                                return None;\n                            }\n                        }\n                        _ => return None,\n                    }\n                } else {\n                    return Some(quote! {});\n                }\n            }\n            _ => return None,\n        }\n    } else {\n        return None;\n    };\n\n    // // Debug check to see how many use this optimisation:\n    // static COUNT: std::sync::atomic::AtomicUsize =\n    //     std::sync::atomic::AtomicUsize::new(0);\n    // COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);\n    // eprintln!(\n    //     \"Optimised children: {}\",\n    //     COUNT.load(std::sync::atomic::Ordering::Relaxed)\n    // );\n\n    let clonables = items_to_clone_to_tokens(items_to_clone);\n    Some(quote_spanned! {children.span()=>\n        .children({\n            #(#clonables)*\n\n            ::leptos::children::ToChildren::to_children(::leptos::children::ChildrenOptContainer(#children))\n        })\n    })\n}\n"
  },
  {
    "path": "leptos_macro/src/view/mod.rs",
    "content": "mod component_builder;\nmod slot_helper;\nmod utils;\n\nuse self::{\n    component_builder::component_to_tokens,\n    slot_helper::{get_slot, slot_to_tokens},\n};\nuse convert_case::{\n    Case::{Snake, UpperCamel},\n    Casing,\n};\nuse convert_case_extras::is_case;\nuse leptos_hot_reload::parsing::{is_component_node, value_to_string};\nuse proc_macro2::{Ident, Span, TokenStream, TokenTree};\nuse proc_macro_error2::abort;\nuse quote::{format_ident, quote, quote_spanned, ToTokens};\nuse rstml::node::{\n    CustomNode, KVAttributeValue, KeyedAttribute, Node, NodeAttribute,\n    NodeBlock, NodeElement, NodeName, NodeNameFragment,\n};\nuse std::{\n    cmp::Ordering,\n    collections::{HashMap, HashSet, VecDeque},\n};\nuse syn::{\n    punctuated::Pair::{End, Punctuated},\n    spanned::Spanned,\n    Expr::{self, Tuple},\n    ExprArray, ExprLit, ExprPath, ExprRange, Lit, LitStr, RangeLimits, Stmt,\n};\n\n#[derive(Clone, Copy, PartialEq, Eq)]\npub(crate) enum TagType {\n    Unknown,\n    Html,\n    Svg,\n    Math,\n}\n\npub fn render_view(\n    nodes: &mut [Node],\n    global_class: Option<&TokenTree>,\n    view_marker: Option<String>,\n    disable_inert_html: bool,\n) -> Option<TokenStream> {\n    let disable_inert_html = disable_inert_html || global_class.is_some();\n\n    let (base, should_add_view) = match nodes.len() {\n        0 => {\n            let span = Span::call_site();\n            (\n                Some(quote_spanned! {\n                    span => ()\n                }),\n                false,\n            )\n        }\n        1 => (\n            node_to_tokens(\n                &mut nodes[0],\n                TagType::Unknown,\n                None,\n                global_class,\n                view_marker.as_deref(),\n                true,\n                disable_inert_html,\n            ),\n            // only add View wrapper and view marker to a regular HTML\n            // element or component, not to a <{..} /> attribute list\n            match &nodes[0] {\n                Node::Element(node) => !is_spread_marker(node),\n                _ => false,\n            },\n        ),\n        _ => (\n            fragment_to_tokens(\n                nodes,\n                TagType::Unknown,\n                None,\n                global_class,\n                view_marker.as_deref(),\n                disable_inert_html,\n            ),\n            true,\n        ),\n    };\n    base.map(|view| {\n        if !should_add_view {\n            view\n        } else if let Some(vm) = view_marker {\n            quote! {\n                ::leptos::prelude::View::new(\n                    #view\n                )\n                .with_view_marker(#vm)\n            }\n        } else {\n            quote! {\n                ::leptos::prelude::View::new(\n                    #view\n                )\n            }\n        }\n    })\n}\n\nfn is_inert_element(orig_node: &Node<impl CustomNode>) -> bool {\n    // do not use this if the top-level node is not an Element,\n    // or if it's an element with no children and no attrs\n    match orig_node {\n        Node::Element(el) => {\n            if el.attributes().is_empty() && el.children.is_empty() {\n                return false;\n            }\n\n            // also doesn't work if the top-level element is a MathML element\n            let el_name = el.name().to_string();\n            if is_math_ml_element(&el_name) {\n                return false;\n            }\n        }\n        _ => return false,\n    }\n\n    // otherwise, walk over all the nodes to make sure everything is inert\n    let mut nodes = VecDeque::from([orig_node]);\n\n    while let Some(current_element) = nodes.pop_front() {\n        match current_element {\n            Node::Text(_) | Node::RawText(_) => {}\n            Node::Element(node) => {\n                if is_component_node(node) {\n                    return false;\n                }\n                if is_spread_marker(node) {\n                    return false;\n                }\n\n                match node.name() {\n                    NodeName::Block(_) => return false,\n                    _ => {\n                        // check all attributes\n                        for attr in node.attributes() {\n                            match attr {\n                                NodeAttribute::Block(_) => return false,\n                                NodeAttribute::Attribute(attr) => {\n                                    let static_key =\n                                        !matches!(attr.key, NodeName::Block(_));\n\n                                    let static_value = match attr\n                                        .possible_value\n                                        .to_value()\n                                    {\n                                        None => true,\n                                        Some(value) => {\n                                            matches!(&value.value, KVAttributeValue::Expr(expr) if {\n                                                if let Expr::Lit(lit) = expr {\n                                                    let key = attr.key.to_string();\n                                                    if key.starts_with(\"style:\") || key.starts_with(\"prop:\") || key.starts_with(\"on:\") || key.starts_with(\"use:\") || key.starts_with(\"bind\") {\n                                                        false\n                                                    } else {\n                                                        matches!(&lit.lit, Lit::Str(_))\n                                                    }\n                                                } else {\n                                                    false\n                                                }\n                                            })\n                                        }\n                                    };\n\n                                    if !static_key || !static_value {\n                                        return false;\n                                    }\n                                }\n                            }\n                        }\n\n                        // check all children\n                        nodes.extend(&node.children);\n                    }\n                }\n            }\n            _ => return false,\n        }\n    }\n\n    true\n}\n\nenum Item<'a, T> {\n    Node(&'a Node<T>, bool),\n    ClosingTag(String),\n}\n\nenum InertElementBuilder<'a> {\n    GlobalClass {\n        global_class: &'a TokenTree,\n        strs: Vec<GlobalClassItem<'a>>,\n        buffer: String,\n    },\n    NoGlobalClass {\n        buffer: String,\n    },\n}\n\nimpl ToTokens for InertElementBuilder<'_> {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        match self {\n            InertElementBuilder::GlobalClass { strs, .. } => {\n                tokens.extend(quote! {\n                    [#(#strs),*].join(\"\")\n                });\n            }\n            InertElementBuilder::NoGlobalClass { buffer } => {\n                tokens.extend(quote! {\n                    #buffer\n                })\n            }\n        }\n    }\n}\n\nenum GlobalClassItem<'a> {\n    Global(&'a TokenTree),\n    String(String),\n}\n\nimpl ToTokens for GlobalClassItem<'_> {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        let addl_tokens = match self {\n            GlobalClassItem::Global(v) => v.to_token_stream(),\n            GlobalClassItem::String(v) => v.to_token_stream(),\n        };\n        tokens.extend(addl_tokens);\n    }\n}\n\nimpl<'a> InertElementBuilder<'a> {\n    fn new(global_class: Option<&'a TokenTree>) -> Self {\n        match global_class {\n            None => Self::NoGlobalClass {\n                buffer: String::new(),\n            },\n            Some(global_class) => Self::GlobalClass {\n                global_class,\n                strs: Vec::new(),\n                buffer: String::new(),\n            },\n        }\n    }\n\n    fn push(&mut self, c: char) {\n        match self {\n            InertElementBuilder::GlobalClass { buffer, .. } => buffer.push(c),\n            InertElementBuilder::NoGlobalClass { buffer } => buffer.push(c),\n        }\n    }\n\n    fn push_str(&mut self, s: &str) {\n        match self {\n            InertElementBuilder::GlobalClass { buffer, .. } => {\n                buffer.push_str(s)\n            }\n            InertElementBuilder::NoGlobalClass { buffer } => buffer.push_str(s),\n        }\n    }\n\n    fn push_class(&mut self, class: &str) {\n        match self {\n            InertElementBuilder::GlobalClass {\n                global_class,\n                strs,\n                buffer,\n            } => {\n                buffer.push_str(\" class=\\\"\");\n                strs.push(GlobalClassItem::String(std::mem::take(buffer)));\n                strs.push(GlobalClassItem::Global(global_class));\n                buffer.push(' ');\n                buffer.push_str(class);\n                buffer.push('\"');\n            }\n            InertElementBuilder::NoGlobalClass { buffer } => {\n                buffer.push_str(\" class=\\\"\");\n                buffer.push_str(class);\n                buffer.push('\"');\n            }\n        }\n    }\n\n    fn finish(&mut self) {\n        match self {\n            InertElementBuilder::GlobalClass { strs, buffer, .. } => {\n                strs.push(GlobalClassItem::String(std::mem::take(buffer)));\n            }\n            InertElementBuilder::NoGlobalClass { .. } => {}\n        }\n    }\n}\n\nfn inert_element_to_tokens(\n    node: &Node<impl CustomNode>,\n    escape_text: bool,\n    global_class: Option<&TokenTree>,\n) -> TokenStream {\n    let mut html = InertElementBuilder::new(global_class);\n    let mut nodes = VecDeque::from([Item::Node(node, escape_text)]);\n\n    while let Some(current) = nodes.pop_front() {\n        match current {\n            Item::ClosingTag(tag) => {\n                // closing tag\n                html.push_str(\"</\");\n                html.push_str(&tag);\n                html.push('>');\n            }\n            Item::Node(current, escape) => {\n                match current {\n                    Node::RawText(raw) => {\n                        let text = raw.to_string_best();\n                        let text = if escape {\n                            html_escape::encode_text(&text)\n                        } else {\n                            text.into()\n                        };\n                        html.push_str(&text);\n                    }\n                    Node::Text(text) => {\n                        let text = text.value_string();\n                        let text = if escape {\n                            html_escape::encode_text(&text)\n                        } else {\n                            text.into()\n                        };\n                        html.push_str(&text);\n                    }\n                    Node::Element(node) => {\n                        let self_closing = is_self_closing(node);\n                        let el_name = node.name().to_string();\n                        let escape = el_name != \"script\"\n                            && el_name != \"style\"\n                            && el_name != \"textarea\";\n\n                        // opening tag\n                        html.push('<');\n                        html.push_str(&el_name);\n\n                        for attr in node.attributes() {\n                            if let NodeAttribute::Attribute(attr) = attr {\n                                let attr_name = attr.key.to_string();\n                                // trim r# from raw identifiers like r#as\n                                let attr_name =\n                                    attr_name.trim_start_matches(\"r#\");\n                                if attr_name != \"class\" {\n                                    html.push(' ');\n                                    html.push_str(attr_name);\n                                }\n\n                                if let Some(value) =\n                                    attr.possible_value.to_value()\n                                {\n                                    if let KVAttributeValue::Expr(Expr::Lit(\n                                        lit,\n                                    )) = &value.value\n                                    {\n                                        if let Lit::Str(txt) = &lit.lit {\n                                            let value = txt.value();\n                                            let value = html_escape::encode_double_quoted_attribute(&value);\n                                            if attr_name == \"class\" {\n                                                html.push_class(&value);\n                                            } else {\n                                                html.push_str(\"=\\\"\");\n                                                html.push_str(&value);\n                                                html.push('\"');\n                                            }\n                                        }\n                                    }\n                                };\n                            }\n                        }\n\n                        html.push('>');\n\n                        // render all children\n                        if !self_closing {\n                            nodes.push_front(Item::ClosingTag(el_name));\n                            let children = node.children.iter().rev();\n                            for child in children {\n                                nodes.push_front(Item::Node(child, escape));\n                            }\n                        }\n                    }\n                    _ => {}\n                }\n            }\n        }\n    }\n\n    html.finish();\n\n    quote! {\n        ::leptos::tachys::html::InertElement::new(#html)\n    }\n}\n\n/// # Note\n/// Should not be used on top level `<svg>` elements.\n/// Use [`inert_element_to_tokens`] instead.\nfn inert_svg_element_to_tokens(\n    node: &Node<impl CustomNode>,\n    escape_text: bool,\n    global_class: Option<&TokenTree>,\n) -> TokenStream {\n    let mut html = InertElementBuilder::new(global_class);\n    let mut nodes = VecDeque::from([Item::Node(node, escape_text)]);\n\n    while let Some(current) = nodes.pop_front() {\n        match current {\n            Item::ClosingTag(tag) => {\n                // closing tag\n                html.push_str(\"</\");\n                html.push_str(&tag);\n                html.push('>');\n            }\n            Item::Node(current, escape) => {\n                match current {\n                    Node::RawText(raw) => {\n                        let text = raw.to_string_best();\n                        let text = if escape {\n                            html_escape::encode_text(&text)\n                        } else {\n                            text.into()\n                        };\n                        html.push_str(&text);\n                    }\n                    Node::Text(text) => {\n                        let text = text.value_string();\n                        let text = if escape {\n                            html_escape::encode_text(&text)\n                        } else {\n                            text.into()\n                        };\n                        html.push_str(&text);\n                    }\n                    Node::Element(node) => {\n                        let self_closing = is_self_closing(node);\n                        let el_name = node.name().to_string();\n                        // strip trailing underscores, for identifiers such as SVG use_\n                        let el_name = el_name\n                            .strip_suffix('_')\n                            .map(str::to_string)\n                            .unwrap_or(el_name);\n\n                        let escape = el_name != \"script\"\n                            && el_name != \"style\"\n                            && el_name != \"textarea\";\n\n                        // opening tag\n                        html.push('<');\n                        html.push_str(&el_name);\n\n                        for attr in node.attributes() {\n                            if let NodeAttribute::Attribute(attr) = attr {\n                                let attr_name = attr.key.to_string();\n                                // trim r# from raw identifiers like r#as\n                                let attr_name =\n                                    attr_name.trim_start_matches(\"r#\");\n                                if attr_name != \"class\" {\n                                    html.push(' ');\n                                    html.push_str(attr_name);\n                                }\n\n                                if let Some(value) =\n                                    attr.possible_value.to_value()\n                                {\n                                    if let KVAttributeValue::Expr(Expr::Lit(\n                                        lit,\n                                    )) = &value.value\n                                    {\n                                        if let Lit::Str(txt) = &lit.lit {\n                                            let value = txt.value();\n                                            let value = html_escape::encode_double_quoted_attribute(&value);\n                                            if attr_name == \"class\" {\n                                                html.push_class(&value);\n                                            } else {\n                                                html.push_str(\"=\\\"\");\n                                                html.push_str(&value);\n                                                html.push('\"');\n                                            }\n                                        }\n                                    }\n                                };\n                            }\n                        }\n\n                        html.push('>');\n\n                        // render all children\n                        if !self_closing {\n                            nodes.push_front(Item::ClosingTag(el_name));\n                            let children = node.children.iter().rev();\n                            for child in children {\n                                nodes.push_front(Item::Node(child, escape));\n                            }\n                        }\n                    }\n                    _ => {}\n                }\n            }\n        }\n    }\n\n    html.finish();\n\n    quote! {\n        ::leptos::tachys::svg::InertElement::new(#html)\n    }\n}\n\nfn element_children_to_tokens(\n    nodes: &mut [Node<impl CustomNode>],\n    parent_type: TagType,\n    parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,\n    global_class: Option<&TokenTree>,\n    view_marker: Option<&str>,\n    disable_inert_html: bool,\n) -> Option<TokenStream> {\n    let children = children_to_tokens(\n        nodes,\n        parent_type,\n        parent_slots,\n        global_class,\n        view_marker,\n        false,\n        disable_inert_html,\n    );\n    if children.is_empty() {\n        None\n    } else if children.len() == 1 {\n        let child = &children[0];\n        Some(quote! {\n            .child(\n                #[allow(unused_braces)]\n                { #child }\n            )\n        })\n    } else if cfg!(feature = \"__internal_erase_components\") {\n        Some(quote! {\n            .child(\n                ::leptos::tachys::view::iterators::StaticVec::from(vec![#(\n                    ::leptos::prelude::IntoMaybeErased::into_maybe_erased(#children)\n                ),*])\n            )\n        })\n    } else if children.len() > 16 {\n        // implementations of various traits used in routing and rendering are implemented for\n        // tuples of sizes 0, 1, 2, 3, ... N. N varies but is > 16. The traits are also implemented\n        // for tuples of tuples, so if we have more than 16 items, we can split them out into\n        // multiple tuples.\n        let chunks = children.chunks(16).map(|children| {\n            quote! {\n                (#(#children),*)\n            }\n        });\n        Some(quote! {\n            .child(\n                (#(#chunks),*)\n            )\n        })\n    } else {\n        Some(quote! {\n            .child(\n                (#(#children),*)\n            )\n        })\n    }\n}\n\nfn fragment_to_tokens(\n    nodes: &mut [Node<impl CustomNode>],\n    parent_type: TagType,\n    parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,\n    global_class: Option<&TokenTree>,\n    view_marker: Option<&str>,\n    disable_inert_html: bool,\n) -> Option<TokenStream> {\n    let children = children_to_tokens(\n        nodes,\n        parent_type,\n        parent_slots,\n        global_class,\n        view_marker,\n        true,\n        disable_inert_html,\n    );\n    if children.is_empty() {\n        None\n    } else if children.len() == 1 {\n        children.into_iter().next()\n    } else if cfg!(feature = \"__internal_erase_components\") {\n        Some(quote! {\n            ::leptos::tachys::view::iterators::StaticVec::from(vec![#(\n                ::leptos::prelude::IntoMaybeErased::into_maybe_erased(#children)\n            ),*])\n        })\n    } else if children.len() > 16 {\n        // implementations of various traits used in routing and rendering are implemented for\n        // tuples of sizes 0, 1, 2, 3, ... N. N varies but is > 16. The traits are also implemented\n        // for tuples of tuples, so if we have more than 16 items, we can split them out into\n        // multiple tuples.\n        let chunks = children.chunks(16).map(|children| {\n            quote! {\n                (#(#children),*)\n            }\n        });\n        Some(quote! {\n             (#(#chunks),*)\n        })\n    } else {\n        Some(quote! {\n            (#(#children),*)\n        })\n    }\n}\n\nfn children_to_tokens(\n    nodes: &mut [Node<impl CustomNode>],\n    parent_type: TagType,\n    parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,\n    global_class: Option<&TokenTree>,\n    view_marker: Option<&str>,\n    top_level: bool,\n    disable_inert_html: bool,\n) -> Vec<TokenStream> {\n    if nodes.len() == 1 {\n        match node_to_tokens(\n            &mut nodes[0],\n            parent_type,\n            parent_slots,\n            global_class,\n            view_marker,\n            top_level,\n            disable_inert_html,\n        ) {\n            Some(tokens) => vec![tokens],\n            None => vec![],\n        }\n    } else {\n        let mut slots = HashMap::new();\n        let nodes = nodes\n            .iter_mut()\n            .filter_map(|node| {\n                node_to_tokens(\n                    node,\n                    TagType::Unknown,\n                    Some(&mut slots),\n                    global_class,\n                    view_marker,\n                    top_level,\n                    disable_inert_html,\n                )\n            })\n            .collect();\n        if let Some(parent_slots) = parent_slots {\n            for (slot, mut values) in slots.drain() {\n                parent_slots\n                    .entry(slot)\n                    .and_modify(|entry| entry.append(&mut values))\n                    .or_insert(values);\n            }\n        }\n        nodes\n    }\n}\n\nfn node_to_tokens(\n    node: &mut Node<impl CustomNode>,\n    parent_type: TagType,\n    parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,\n    global_class: Option<&TokenTree>,\n    view_marker: Option<&str>,\n    top_level: bool,\n    disable_inert_html: bool,\n) -> Option<TokenStream> {\n    let is_inert = !disable_inert_html && is_inert_element(node);\n\n    match node {\n        Node::Comment(_) => None,\n        Node::Doctype(node) => {\n            let value = node.value.to_string_best();\n            Some(quote! { ::leptos::tachys::html::doctype(#value) })\n        }\n        Node::Fragment(fragment) => fragment_to_tokens(\n            &mut fragment.children,\n            parent_type,\n            parent_slots,\n            global_class,\n            view_marker,\n            disable_inert_html,\n        ),\n        Node::Block(block) => {\n            Some(quote! { ::leptos::prelude::IntoRender::into_render(#block) })\n        }\n        Node::Text(text) => Some(text_to_tokens(&text.value)),\n        Node::RawText(raw) => {\n            let text = raw.to_string_best();\n            let text = syn::LitStr::new(&text, raw.span());\n            Some(text_to_tokens(&text))\n        }\n        Node::Element(el_node) => {\n            if !top_level && is_inert {\n                let el_name = el_node.name().to_string();\n                let escape = el_name != \"script\"\n                    && el_name != \"style\"\n                    && el_name != \"textarea\";\n\n                let el_name = el_node.name().to_string();\n                if is_svg_element(&el_name) && el_name != \"svg\" {\n                    Some(inert_svg_element_to_tokens(\n                        node,\n                        escape,\n                        global_class,\n                    ))\n                } else {\n                    Some(inert_element_to_tokens(node, escape, global_class))\n                }\n            } else {\n                element_to_tokens(\n                    el_node,\n                    parent_type,\n                    parent_slots,\n                    global_class,\n                    view_marker,\n                    disable_inert_html,\n                )\n            }\n        }\n        Node::Custom(node) => Some(node.to_token_stream()),\n    }\n}\n\nfn text_to_tokens(text: &LitStr) -> TokenStream {\n    // on nightly, can use static string optimization\n    if cfg!(all(feature = \"nightly\", rustc_nightly)) {\n        quote! {\n            ::leptos::tachys::view::static_types::Static::<#text>\n        }\n    }\n    // otherwise, just use the literal string\n    else {\n        quote! { #text }\n    }\n}\n\npub(crate) fn element_to_tokens(\n    node: &mut NodeElement<impl CustomNode>,\n    mut parent_type: TagType,\n    parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,\n    global_class: Option<&TokenTree>,\n    view_marker: Option<&str>,\n    disable_inert_html: bool,\n) -> Option<TokenStream> {\n    // attribute sorting:\n    //\n    // the `class` and `style` attributes overwrite individual `class:` and `style:` attributes\n    // when they are set. as a result, we're going to sort the attributes so that `class` and\n    // `style` always come before all other attributes.\n\n    // if there's a spread marker, we don't want to move `class` or `style` before it\n    // so let's only sort attributes that come *before* a spread marker\n    let spread_position = node\n        .attributes()\n        .iter()\n        .position(|n| match n {\n            NodeAttribute::Block(node) => as_spread_attr(node).is_some(),\n            _ => false,\n        })\n        .unwrap_or_else(|| node.attributes().len());\n\n    // now, sort the attributes\n    node.attributes_mut()[0..spread_position].sort_by(|a, b| {\n        let key_a = match a {\n            NodeAttribute::Attribute(attr) => match &attr.key {\n                NodeName::Path(attr) => {\n                    attr.path.segments.first().map(|n| n.ident.to_string())\n                }\n                _ => None,\n            },\n            _ => None,\n        };\n        let key_b = match b {\n            NodeAttribute::Attribute(attr) => match &attr.key {\n                NodeName::Path(attr) => {\n                    attr.path.segments.first().map(|n| n.ident.to_string())\n                }\n                _ => None,\n            },\n            _ => None,\n        };\n\n        if let NodeAttribute::Attribute(a) = a {\n            if let Some(Tuple(_)) = a.value() {\n                return Ordering::Greater;\n            }\n        }\n        if let NodeAttribute::Attribute(b) = b {\n            if let Some(Tuple(_)) = b.value() {\n                return Ordering::Less;\n            }\n        }\n\n        match (key_a.as_deref(), key_b.as_deref()) {\n            (Some(\"class\"), Some(\"class\")) | (Some(\"style\"), Some(\"style\")) => {\n                Ordering::Equal\n            }\n            (Some(\"class\"), _) | (Some(\"style\"), _) => Ordering::Less,\n            (_, Some(\"class\")) | (_, Some(\"style\")) => Ordering::Greater,\n            _ => Ordering::Equal,\n        }\n    });\n\n    // check for duplicate attribute names and emit an error for all subsequent ones\n    let mut names = HashSet::new();\n\n    // allow multiple class=(...) or style=(...) attributes\n    fn allow_multiples(name: &str, attr: &KeyedAttribute) -> bool {\n        (name == \"class\" || name == \"style\")\n            && matches!(attr.value(), Some(Expr::Tuple(..)))\n    }\n\n    for attr in node.attributes() {\n        if let NodeAttribute::Attribute(attr) = attr {\n            let mut name = attr.key.to_string();\n            match tuple_name(&name, attr) {\n                TupleName::None => {}\n                TupleName::Str(tuple_name) => {\n                    name.push(':');\n                    name.push_str(&tuple_name);\n                }\n                TupleName::Array(names) => {\n                    for tuple_name in names {\n                        name.push(':');\n                        name.push_str(&tuple_name);\n                    }\n                }\n            }\n            if names.contains(&name) && !allow_multiples(&name, attr) {\n                proc_macro_error2::emit_error!(\n                    attr.span(),\n                    format!(\"This element already has a `{name}` attribute.\")\n                );\n            } else {\n                names.insert(name);\n            }\n        }\n    }\n\n    let name = node.name();\n    if is_component_node(node) {\n        if let Some(slot) = get_slot(node) {\n            let slot = slot.clone();\n            slot_to_tokens(\n                node,\n                &slot,\n                parent_slots,\n                global_class,\n                disable_inert_html,\n            );\n            None\n        } else {\n            Some(component_to_tokens(node, global_class, disable_inert_html))\n        }\n    } else if is_spread_marker(node) {\n        let mut attributes = Vec::new();\n        let mut additions = Vec::new();\n        for node in node.attributes() {\n            match node {\n                NodeAttribute::Block(block) => {\n                    if let NodeBlock::ValidBlock(block) = block {\n                        match block.stmts.first() {\n                            Some(Stmt::Expr(\n                                Expr::Range(ExprRange {\n                                    start: None,\n                                    limits: RangeLimits::HalfOpen(_),\n                                    end: Some(end),\n                                    ..\n                                }),\n                                _,\n                            )) => {\n                                additions.push(quote! { #end });\n                            }\n                            _ => {\n                                additions.push(quote! { #block });\n                            }\n                        }\n                    } else {\n                        additions.push(quote! { #block });\n                    }\n                }\n                NodeAttribute::Attribute(node) => {\n                    if let Some(content) = attribute_absolute(node, true) {\n                        attributes.push(content);\n                    }\n                }\n            }\n        }\n\n        if cfg!(feature = \"__internal_erase_components\") {\n            Some(quote! {\n                vec![#(#attributes.into_any_attr(),)*]\n                #(.add_any_attr(#additions))*\n            })\n        } else {\n            Some(quote! {\n                (#(#attributes,)*)\n                #(.add_any_attr(#additions))*\n            })\n        }\n    } else {\n        let tag = name.to_string();\n        // collect close_tag name to emit semantic information for IDE.\n        /* TODO restore this\n        let mut ide_helper_close_tag = IdeTagHelper::new();\n        let close_tag = node.close_tag.as_ref().map(|c| &c.name);*/\n        let is_custom = is_custom_element(&tag);\n        let name = if is_custom {\n            let name = node.name().to_string();\n            // link custom ident to name span for IDE docs\n            let custom = Ident::new(\"custom\", name.span());\n            quote_spanned! { node.name().span() => ::leptos::tachys::html::element::#custom(#name) }\n        } else if is_svg_element(&tag) {\n            parent_type = TagType::Svg;\n            let name = if tag == \"use\" || tag == \"use_\" {\n                Ident::new_raw(\"use\", name.span()).to_token_stream()\n            } else {\n                name.to_token_stream()\n            };\n            quote_spanned! { node.name().span() => ::leptos::tachys::svg::#name() }\n        } else if is_math_ml_element(&tag) {\n            parent_type = TagType::Math;\n            quote_spanned! { node.name().span() => ::leptos::tachys::mathml::#name() }\n        } else if is_ambiguous_element(&tag) {\n            match parent_type {\n                TagType::Unknown => {\n                    // We decided this warning was too aggressive, but I'll leave it here in case we want it later\n                    /* proc_macro_error2::emit_warning!(name.span(), \"The view macro is assuming this is an HTML element, \\\n                    but it is ambiguous; if it is an SVG or MathML element, prefix with svg:: or math::\"); */\n                    quote_spanned! { node.name().span() =>\n                        ::leptos::tachys::html::element::#name()\n                    }\n                }\n                TagType::Html => {\n                    quote_spanned! { node.name().span() => ::leptos::tachys::html::element::#name() }\n                }\n                TagType::Svg => {\n                    quote_spanned! { node.name().span() => ::leptos::tachys::svg::#name() }\n                }\n                TagType::Math => {\n                    quote_spanned! { node.name().span() => ::leptos::tachys::math::#name() }\n                }\n            }\n        } else {\n            parent_type = TagType::Html;\n            quote_spanned! { name.span() => ::leptos::tachys::html::element::#name() }\n        };\n\n        /* TODO restore this\n        if let Some(close_tag) = close_tag {\n            ide_helper_close_tag.save_tag_completion(close_tag)\n        } */\n\n        let attributes = node.attributes();\n        let attributes = if attributes.len() == 1 {\n            Some(attribute_to_tokens(\n                parent_type,\n                &attributes[0],\n                global_class,\n                is_custom,\n            ))\n        } else {\n            let nodes = attributes.iter().map(|node| {\n                attribute_to_tokens(parent_type, node, global_class, is_custom)\n            });\n            Some(quote! {\n                #(#nodes)*\n            })\n        };\n\n        let global_class_expr = global_class.map(|class| {\n            quote! { .class((#class, true)) }\n        });\n\n        let self_closing = is_self_closing(node);\n        let children = if !self_closing {\n            element_children_to_tokens(\n                &mut node.children,\n                parent_type,\n                parent_slots,\n                global_class,\n                view_marker,\n                disable_inert_html,\n            )\n        } else {\n            if !node.children.is_empty() {\n                let name = node.name();\n                proc_macro_error2::emit_error!(\n                    name.span(),\n                    format!(\n                        \"Self-closing elements like <{name}> cannot have \\\n                         children.\"\n                    )\n                );\n            };\n            None\n        };\n\n        // attributes are placed second because this allows `inner_html`\n        // to object if there are already children\n        Some(quote! {\n            #name\n            #children\n            #attributes\n            #global_class_expr\n        })\n    }\n}\n\nfn is_spread_marker(node: &NodeElement<impl CustomNode>) -> bool {\n    match node.name() {\n        NodeName::Block(block) => matches!(\n            block.stmts.first(),\n            Some(Stmt::Expr(\n                Expr::Range(ExprRange {\n                    start: None,\n                    limits: RangeLimits::HalfOpen(_),\n                    end: None,\n                    ..\n                }),\n                _,\n            ))\n        ),\n        _ => false,\n    }\n}\n\nfn as_spread_attr(node: &NodeBlock) -> Option<Option<&Expr>> {\n    if let NodeBlock::ValidBlock(block) = node {\n        match block.stmts.first() {\n            Some(Stmt::Expr(\n                Expr::Range(ExprRange {\n                    start: None,\n                    limits: RangeLimits::HalfOpen(_),\n                    end,\n                    ..\n                }),\n                _,\n            )) => Some(end.as_deref()),\n            _ => None,\n        }\n    } else {\n        None\n    }\n}\n\nfn attribute_to_tokens(\n    tag_type: TagType,\n    node: &NodeAttribute,\n    global_class: Option<&TokenTree>,\n    is_custom: bool,\n) -> TokenStream {\n    match node {\n        NodeAttribute::Block(node) => as_spread_attr(node)\n            .flatten()\n            .map(|end| {\n                quote! {\n                    .add_any_attr(#end)\n                }\n            })\n            .unwrap_or_else(|| {\n                quote! {\n                    .add_any_attr(#[allow(unused_braces)] { #node })\n                }\n            }),\n        NodeAttribute::Attribute(node) => {\n            let name = node.key.to_string();\n            if name == \"node_ref\" {\n                let node_ref = match &node.key {\n                    NodeName::Path(path) => path.path.get_ident(),\n                    _ => unreachable!(),\n                };\n                let value = attribute_value(node, false);\n                quote! {\n                    .#node_ref(#value)\n                }\n            } else if let Some(name) = name.strip_prefix(\"use:\") {\n                directive_call_from_attribute_node(node, name)\n            } else if let Some(name) = name.strip_prefix(\"on:\") {\n                event_to_tokens(name, node)\n            } else if let Some(name) = name.strip_prefix(\"bind:\") {\n                two_way_binding_to_tokens(name, node)\n            } else if let Some(name) = name.strip_prefix(\"class:\") {\n                let class = match &node.key {\n                    NodeName::Punctuated(parts) => &parts[0],\n                    _ => unreachable!(),\n                };\n                class_to_tokens(node, class.into_token_stream(), Some(name))\n            } else if name == \"class\" {\n                let class = match &node.key {\n                    NodeName::Path(path) => path.path.get_ident(),\n                    _ => unreachable!(),\n                };\n                class_to_tokens(node, class.into_token_stream(), None)\n            } else if let Some(name) = name.strip_prefix(\"style:\") {\n                let style = match &node.key {\n                    NodeName::Punctuated(parts) => &parts[0],\n                    _ => unreachable!(),\n                };\n                style_to_tokens(node, style.into_token_stream(), Some(name))\n            } else if name == \"style\" {\n                let style = match &node.key {\n                    NodeName::Path(path) => path.path.get_ident(),\n                    _ => unreachable!(),\n                };\n                style_to_tokens(node, style.into_token_stream(), None)\n            } else if let Some(name) = name.strip_prefix(\"prop:\") {\n                let prop = match &node.key {\n                    NodeName::Punctuated(parts) => &parts[0],\n                    _ => unreachable!(),\n                };\n                prop_to_tokens(node, prop.into_token_stream(), name)\n            }\n            // circumstances in which we just do unchecked attributes\n            // 1) custom elements, which can have any attributes\n            // 2) custom attributes and data attributes (so, anything with - in it)\n            else if is_custom ||\n                (name.contains('-') && !name.starts_with(\"aria-\"))\n                // TODO check: do we actually provide SVG attributes?\n                // we don't provide statically-checked methods for SVG attributes\n                || (tag_type == TagType::Svg && name != \"inner_html\")\n            {\n                let value = attribute_value(node, true);\n                quote! {\n                    .attr(#name, #value)\n                }\n            } else {\n                let key = attribute_name(&node.key);\n                let value = attribute_value(node, true);\n\n                // special case of global_class and class attribute\n                if &node.key.to_string() == \"class\"\n                    && global_class.is_some()\n                    && node.value().and_then(value_to_string).is_none()\n                {\n                    let span = node.key.span();\n                    proc_macro_error2::emit_error!(span, \"Combining a global class (view! { class = ... }) \\\n            and a dynamic `class=` attribute on an element causes runtime inconsistencies. You can \\\n            toggle individual classes dynamically with the `class:name=value` syntax. \\n\\nSee this issue \\\n            for more information and an example: https://github.com/leptos-rs/leptos/issues/773\")\n                };\n\n                quote! {\n                    .#key(#value)\n                }\n            }\n        }\n    }\n}\n\n/// Returns attribute values with an absolute path\npub(crate) fn attribute_absolute(\n    node: &KeyedAttribute,\n    after_spread: bool,\n) -> Option<TokenStream> {\n    let key = node.key.to_string();\n    let contains_dash = key.contains('-');\n    let attr_colon = key.starts_with(\"attr:\")\n        || key.starts_with(\"style:\")\n        || key.starts_with(\"class:\")\n        || key.starts_with(\"prop:\")\n        || key.starts_with(\"use:\");\n    // anything that follows the x:y pattern\n    match &node.key {\n        NodeName::Punctuated(parts) if !contains_dash || attr_colon => {\n            if parts.len() >= 2 {\n                let id = &parts[0];\n                match id {\n                    NodeNameFragment::Ident(id) => {\n                        // ignore `let:` and `clone:`\n                        if id == \"let\" || id == \"clone\" {\n                            None\n                        } else if id == \"attr\" {\n                            let value = attribute_value(node, true);\n                            let multipart = parts.len() > 2;\n                            let key = &parts[1];\n                            let key_name = key.to_string();\n                            if key_name == \"class\" || key_name == \"style\" {\n                                Some(\n                                    quote! { ::leptos::tachys::html::#key::#key(#value) },\n                                )\n                            } else if key_name == \"aria\" {\n                                let value = attribute_value(node, true);\n                                let mut parts_iter = parts.iter();\n                                parts_iter.next();\n                                let fn_name = parts_iter.map(|p| p.to_string()).collect::<Vec<String>>().join(\"_\");\n                                let key = Ident::new(&fn_name, key.span());\n                                Some(\n                                    quote! { ::leptos::tachys::html::attribute::#key(#value) },\n                                )\n                            } else if multipart {\n                                // e.g., attr:data-foo=\"bar\"\n                                let key_name = parts.pairs().skip(1).map(|p| match p {\n                                    Punctuated(n, p) => format!(\"{n}{p}\"),\n                                    End(n) => n.to_string(),\n                                }).collect::<String>();\n                                Some(\n                                    quote! { ::leptos::tachys::html::attribute::custom::custom_attribute(#key_name, #value) },\n                                )\n                            } else {\n                                Some(\n                                    quote! { ::leptos::tachys::html::attribute::#key(#value) },\n                                )\n                            }\n                        } else if id == \"use\" {\n                            let key = &parts[1];\n                            let param = if let Some(value) = node.value() {\n                                quote!(#value)\n                            } else {\n                                quote_spanned!(node.key.span()=> ().into())\n                            };\n                            Some(\n                                quote! {\n                                    ::leptos::tachys::html::directive::directive(\n                                        #key,\n                                        #[allow(clippy::useless_conversion)] #param\n                                    )\n                                },\n                            )\n                        } else if id == \"style\" || id == \"class\" {\n                            let value = attribute_value(node, false);\n                            let key = &node.key.to_string();\n                            let key = key\n                                .replacen(\"style:\", \"\", 1)\n                                .replacen(\"class:\", \"\", 1);\n                            Some(\n                                quote! { ::leptos::tachys::html::#id::#id((#key, #value)) },\n                            )\n                        } else if id == \"prop\" {\n                            let value = attribute_value(node, false);\n                            let key = &node.key.to_string();\n                            let key = key.replacen(\"prop:\", \"\", 1);\n                            Some(\n                                quote! { ::leptos::tachys::html::property::#id(#key, #value) },\n                            )\n                        } else if id == \"on\" {\n                            let key = &node.key.to_string();\n                            let key = key.replacen(\"on:\", \"\", 1);\n                            let (on, ty, handler) =\n                                event_type_and_handler(&key, node);\n                            Some(\n                                quote! { ::leptos::tachys::html::event::#on(#ty, #handler) },\n                            )\n                        } else {\n                            proc_macro_error2::abort!(\n                                id.span(),\n                                &format!(\n                                    \"`{id}:` syntax is not supported on \\\n                                     components\"\n                                )\n                            );\n                        }\n                    }\n                    _ => None,\n                }\n            } else {\n                None\n            }\n        }\n        _ => after_spread.then(|| {\n            let key = attribute_name(&node.key);\n            let value = &node.value();\n            let name = &node.key.to_string();\n            if name == \"class\" || name == \"style\" {\n                quote! {\n                    ::leptos::tachys::html::#key::#key(#value)\n                }\n            }\n            else if name.contains('-') && !name.starts_with(\"aria-\") {\n                quote! {\n                    ::leptos::tachys::html::attribute::custom::custom_attribute(#name, #value)\n                }\n            }\n            else if name == \"node_ref\" {\n                quote! {\n                    ::leptos::tachys::html::node_ref::#key(#value)\n                }\n            }\n            else {\n                quote! {\n                    ::leptos::tachys::html::attribute::#key(#value)\n                }\n            }\n        }),\n    }\n}\n\npub(crate) fn two_way_binding_to_tokens(\n    name: &str,\n    node: &KeyedAttribute,\n) -> TokenStream {\n    let value = attribute_value(node, false);\n\n    let ident =\n        format_ident!(\"{}\", name.to_case(UpperCamel), span = node.key.span());\n\n    if name == \"group\" {\n        quote! {\n            .bind(leptos::tachys::reactive_graph::bind::#ident, #value)\n        }\n    } else {\n        quote! {\n            .bind(::leptos::attr::#ident, #value)\n        }\n    }\n}\n\npub(crate) fn event_to_tokens(\n    name: &str,\n    node: &KeyedAttribute,\n) -> TokenStream {\n    let (on, event_type, handler) = event_type_and_handler(name, node);\n\n    quote! {\n        .#on(#event_type, #handler)\n    }\n}\n\npub(crate) fn event_type_and_handler(\n    name: &str,\n    node: &KeyedAttribute,\n) -> (TokenStream, TokenStream, TokenStream) {\n    let handler = attribute_value(node, false);\n\n    let (event_type, is_custom, options) = parse_event_name(name);\n\n    let event_name_ident = match &node.key {\n        NodeName::Punctuated(parts) => {\n            if parts.len() >= 2 {\n                Some(&parts[1])\n            } else {\n                None\n            }\n        }\n        _ => unreachable!(),\n    };\n    let undelegated_ident = match &node.key {\n        NodeName::Punctuated(parts) => {\n            parts.iter().find(|part| part.to_string() == \"undelegated\")\n        }\n        _ => unreachable!(),\n    };\n    let capture_ident = match &node.key {\n        NodeName::Punctuated(parts) => {\n            parts.iter().find(|part| part.to_string() == \"capture\")\n        }\n        _ => unreachable!(),\n    };\n    let on = match &node.key {\n        NodeName::Punctuated(parts) => &parts[0],\n        _ => unreachable!(),\n    };\n    let on = if options.targeted {\n        Ident::new(\"on_target\", on.span()).to_token_stream()\n    } else {\n        on.to_token_stream()\n    };\n    let event_type = if is_custom {\n        event_type\n    } else if let Some(ev_name) = event_name_ident {\n        quote! { #ev_name }\n    } else {\n        event_type\n    };\n\n    let event_type = quote! {\n        ::leptos::tachys::html::event::#event_type\n    };\n    let event_type = if options.captured {\n        let capture = if let Some(capture) = capture_ident {\n            quote! { #capture }\n        } else {\n            quote! { capture }\n        };\n        quote! { ::leptos::tachys::html::event::#capture(#event_type) }\n    } else {\n        event_type\n    };\n\n    let event_type = if options.undelegated {\n        let undelegated = if let Some(undelegated) = undelegated_ident {\n            quote! { #undelegated }\n        } else {\n            quote! { undelegated }\n        };\n        quote! { ::leptos::tachys::html::event::#undelegated(#event_type) }\n    } else {\n        event_type\n    };\n\n    (on, event_type, handler)\n}\n\nfn class_to_tokens(\n    node: &KeyedAttribute,\n    class: TokenStream,\n    class_name: Option<&str>,\n) -> TokenStream {\n    // case of class=([\"foo\", \"bar\"], /* something */)\n    // just expands to multiple uses of class:\n    if let Some(Tuple(tuple)) = node.value() {\n        if tuple.elems.len() == 2 {\n            let name = &tuple.elems[0];\n            let value = &tuple.elems[1];\n            if let Expr::Array(ExprArray { elems, .. }) = name {\n                return elems\n                    .iter()\n                    .map(|elem| match elem {\n                        Expr::Lit(ExprLit {\n                            lit: Lit::Str(s), ..\n                        }) => quote! {\n                            .#class((#s, #value))\n                        },\n                        _ => proc_macro_error2::abort!(\n                            elem.span(),\n                            \"invalid name\"\n                        ),\n                    })\n                    .collect();\n            }\n        }\n    }\n\n    // default case\n    let value = attribute_value(node, false);\n    if let Some(class_name) = class_name {\n        quote! {\n            .#class((#class_name, #value))\n        }\n    } else {\n        quote! {\n            .#class(#value)\n        }\n    }\n}\n\nfn style_to_tokens(\n    node: &KeyedAttribute,\n    style: TokenStream,\n    style_name: Option<&str>,\n) -> TokenStream {\n    let value = attribute_value(node, false);\n    if let Some(style_name) = style_name {\n        quote! {\n            .#style((#style_name, #value))\n        }\n    } else {\n        quote! {\n            .#style(#value)\n        }\n    }\n}\n\nfn prop_to_tokens(\n    node: &KeyedAttribute,\n    prop: TokenStream,\n    key: &str,\n) -> TokenStream {\n    let value = attribute_value(node, false);\n    quote! {\n        .#prop(#key, #value)\n    }\n}\n\nfn is_custom_element(tag: &str) -> bool {\n    tag.contains('-')\n}\n\nfn is_self_closing(node: &NodeElement<impl CustomNode>) -> bool {\n    // self-closing tags\n    // https://developer.mozilla.org/en-US/docs/Glossary/Empty_element\n    [\n        \"area\", \"base\", \"br\", \"col\", \"embed\", \"hr\", \"img\", \"input\", \"link\",\n        \"meta\", \"param\", \"source\", \"track\", \"wbr\",\n    ]\n    .binary_search(&node.name().to_string().as_str())\n    .is_ok()\n}\n\nfn is_svg_element(tag: &str) -> bool {\n    // Keep list alphabetized for binary search\n    [\n        \"animate\",\n        \"animateMotion\",\n        \"animateTransform\",\n        \"circle\",\n        \"clipPath\",\n        \"defs\",\n        \"desc\",\n        \"discard\",\n        \"ellipse\",\n        \"feBlend\",\n        \"feColorMatrix\",\n        \"feComponentTransfer\",\n        \"feComposite\",\n        \"feConvolveMatrix\",\n        \"feDiffuseLighting\",\n        \"feDisplacementMap\",\n        \"feDistantLight\",\n        \"feDropShadow\",\n        \"feFlood\",\n        \"feFuncA\",\n        \"feFuncB\",\n        \"feFuncG\",\n        \"feFuncR\",\n        \"feGaussianBlur\",\n        \"feImage\",\n        \"feMerge\",\n        \"feMergeNode\",\n        \"feMorphology\",\n        \"feOffset\",\n        \"fePointLight\",\n        \"feSpecularLighting\",\n        \"feSpotLight\",\n        \"feTile\",\n        \"feTurbulence\",\n        \"filter\",\n        \"foreignObject\",\n        \"g\",\n        \"hatch\",\n        \"hatchpath\",\n        \"image\",\n        \"line\",\n        \"linearGradient\",\n        \"marker\",\n        \"mask\",\n        \"metadata\",\n        \"mpath\",\n        \"path\",\n        \"pattern\",\n        \"polygon\",\n        \"polyline\",\n        \"radialGradient\",\n        \"rect\",\n        \"set\",\n        \"stop\",\n        \"svg\",\n        \"switch\",\n        \"symbol\",\n        \"text\",\n        \"textPath\",\n        \"tspan\",\n        \"use\",\n        \"use_\",\n        \"view\",\n    ]\n    .binary_search(&tag)\n    .is_ok()\n}\n\nfn is_math_ml_element(tag: &str) -> bool {\n    // Keep list alphabetized for binary search\n    [\n        \"annotation\",\n        \"maction\",\n        \"math\",\n        \"menclose\",\n        \"merror\",\n        \"mfenced\",\n        \"mfrac\",\n        \"mi\",\n        \"mmultiscripts\",\n        \"mn\",\n        \"mo\",\n        \"mover\",\n        \"mpadded\",\n        \"mphantom\",\n        \"mprescripts\",\n        \"mroot\",\n        \"mrow\",\n        \"ms\",\n        \"mspace\",\n        \"msqrt\",\n        \"mstyle\",\n        \"msub\",\n        \"msubsup\",\n        \"msup\",\n        \"mtable\",\n        \"mtd\",\n        \"mtext\",\n        \"mtr\",\n        \"munder\",\n        \"munderover\",\n        \"semantics\",\n    ]\n    .binary_search(&tag)\n    .is_ok()\n}\n\nfn is_ambiguous_element(tag: &str) -> bool {\n    tag == \"a\" || tag == \"script\" || tag == \"title\"\n}\n\nfn parse_event(event_name: &str) -> (String, EventNameOptions) {\n    let undelegated = event_name.contains(\":undelegated\");\n    let targeted = event_name.contains(\":target\");\n    let captured = event_name.contains(\":capture\");\n    let event_name = event_name\n        .replace(\":undelegated\", \"\")\n        .replace(\":target\", \"\")\n        .replace(\":capture\", \"\");\n    (\n        event_name,\n        EventNameOptions {\n            undelegated,\n            targeted,\n            captured,\n        },\n    )\n}\n\n/// Escapes Rust keywords that are also HTML attribute names\n/// to their raw-identifier form.\nfn attribute_name(name: &NodeName) -> TokenStream {\n    let s = name.to_string();\n    if s == \"as\" || s == \"async\" || s == \"loop\" || s == \"for\" || s == \"type\" {\n        Ident::new_raw(&s, name.span()).to_token_stream()\n    } else if s.starts_with(\"aria-\") {\n        Ident::new(&s.replace('-', \"_\"), name.span()).to_token_stream()\n    } else {\n        name.to_token_stream()\n    }\n}\n\nfn attribute_value(\n    attr: &KeyedAttribute,\n    is_attribute_proper: bool,\n) -> TokenStream {\n    match attr.possible_value.to_value() {\n        None => quote! { true },\n        Some(value) => match &value.value {\n            KVAttributeValue::Expr(expr) => {\n                if let Expr::Lit(lit) = expr {\n                    if cfg!(all(feature = \"nightly\", rustc_nightly)) {\n                        if let Lit::Str(str) = &lit.lit {\n                            return quote! {\n                                ::leptos::tachys::view::static_types::Static::<#str>\n                            };\n                        }\n                    }\n                }\n\n                if matches!(expr, Expr::Lit(_)) || !is_attribute_proper {\n                    quote! {\n                        #expr\n                    }\n                } else {\n                    quote! {\n                        ::leptos::prelude::IntoAttributeValue::into_attribute_value(#expr)\n                    }\n                }\n            }\n            // any value in braces: expand as-is to give proper r-a support\n            KVAttributeValue::InvalidBraced(block) => {\n                if is_attribute_proper {\n                    quote! {\n                        ::leptos::prelude::IntoAttributeValue::into_attribute_value(#block)\n                    }\n                } else {\n                    quote! {\n                        #block\n                    }\n                }\n            }\n        },\n    }\n}\n\n// Keep list alphabetized for binary search\nconst TYPED_EVENTS: [&str; 127] = [\n    \"DOMContentLoaded\",\n    \"abort\",\n    \"afterprint\",\n    \"animationcancel\",\n    \"animationend\",\n    \"animationiteration\",\n    \"animationstart\",\n    \"auxclick\",\n    \"beforeinput\",\n    \"beforeprint\",\n    \"beforeunload\",\n    \"blur\",\n    \"canplay\",\n    \"canplaythrough\",\n    \"change\",\n    \"click\",\n    \"close\",\n    \"compositionend\",\n    \"compositionstart\",\n    \"compositionupdate\",\n    \"contextmenu\",\n    \"copy\",\n    \"cuechange\",\n    \"cut\",\n    \"dblclick\",\n    \"devicemotion\",\n    \"deviceorientation\",\n    \"drag\",\n    \"dragend\",\n    \"dragenter\",\n    \"dragleave\",\n    \"dragover\",\n    \"dragstart\",\n    \"drop\",\n    \"durationchange\",\n    \"emptied\",\n    \"ended\",\n    \"error\",\n    \"focus\",\n    \"focusin\",\n    \"focusout\",\n    \"formdata\",\n    \"fullscreenchange\",\n    \"fullscreenerror\",\n    \"gamepadconnected\",\n    \"gamepaddisconnected\",\n    \"gotpointercapture\",\n    \"hashchange\",\n    \"input\",\n    \"invalid\",\n    \"keydown\",\n    \"keypress\",\n    \"keyup\",\n    \"languagechange\",\n    \"load\",\n    \"loadeddata\",\n    \"loadedmetadata\",\n    \"loadstart\",\n    \"lostpointercapture\",\n    \"message\",\n    \"messageerror\",\n    \"mousedown\",\n    \"mouseenter\",\n    \"mouseleave\",\n    \"mousemove\",\n    \"mouseout\",\n    \"mouseover\",\n    \"mouseup\",\n    \"offline\",\n    \"online\",\n    \"orientationchange\",\n    \"pagehide\",\n    \"pageshow\",\n    \"paste\",\n    \"pause\",\n    \"play\",\n    \"playing\",\n    \"pointercancel\",\n    \"pointerdown\",\n    \"pointerenter\",\n    \"pointerleave\",\n    \"pointerlockchange\",\n    \"pointerlockerror\",\n    \"pointermove\",\n    \"pointerout\",\n    \"pointerover\",\n    \"pointerup\",\n    \"popstate\",\n    \"progress\",\n    \"ratechange\",\n    \"readystatechange\",\n    \"rejectionhandled\",\n    \"reset\",\n    \"resize\",\n    \"scroll\",\n    \"scrollend\",\n    \"securitypolicyviolation\",\n    \"seeked\",\n    \"seeking\",\n    \"select\",\n    \"selectionchange\",\n    \"selectstart\",\n    \"slotchange\",\n    \"stalled\",\n    \"storage\",\n    \"submit\",\n    \"suspend\",\n    \"timeupdate\",\n    \"toggle\",\n    \"touchcancel\",\n    \"touchend\",\n    \"touchmove\",\n    \"touchstart\",\n    \"transitioncancel\",\n    \"transitionend\",\n    \"transitionrun\",\n    \"transitionstart\",\n    \"unhandledrejection\",\n    \"unload\",\n    \"visibilitychange\",\n    \"volumechange\",\n    \"waiting\",\n    \"webkitanimationend\",\n    \"webkitanimationiteration\",\n    \"webkitanimationstart\",\n    \"webkittransitionend\",\n    \"wheel\",\n];\n\nconst CUSTOM_EVENT: &str = \"Custom\";\n\n#[derive(Debug)]\npub(crate) struct EventNameOptions {\n    undelegated: bool,\n    targeted: bool,\n    captured: bool,\n}\n\npub(crate) fn parse_event_name(\n    name: &str,\n) -> (TokenStream, bool, EventNameOptions) {\n    let (name, options) = parse_event(name);\n\n    let (event_type, is_custom) = TYPED_EVENTS\n        .binary_search(&name.as_str())\n        .map(|_| (name.as_str(), false))\n        .unwrap_or((CUSTOM_EVENT, true));\n\n    let Ok(event_type) = event_type.parse::<TokenStream>() else {\n        abort!(event_type, \"couldn't parse event name\");\n    };\n\n    let event_type = if is_custom {\n        quote! { Custom::new(#name) }\n    } else {\n        event_type\n    };\n    (event_type, is_custom, options)\n}\n\nfn convert_to_snake_case(name: String) -> String {\n    if !is_case(&name, Snake) {\n        name.to_case(Snake)\n    } else {\n        name\n    }\n}\n\npub(crate) fn ident_from_tag_name(tag_name: &NodeName) -> Ident {\n    match tag_name {\n        NodeName::Path(path) => path\n            .path\n            .segments\n            .iter()\n            .next_back()\n            .map(|segment| segment.ident.clone())\n            .expect(\"element needs to have a name\"),\n        NodeName::Block(_) => {\n            let span = tag_name.span();\n            proc_macro_error2::emit_error!(\n                span,\n                \"blocks not allowed in tag-name position\"\n            );\n            Ident::new(\"\", span)\n        }\n        _ => Ident::new(\n            &tag_name.to_string().replace(['-', ':'], \"_\"),\n            tag_name.span(),\n        ),\n    }\n}\n\npub(crate) fn full_path_from_tag_name(tag_name: &NodeName) -> Option<ExprPath> {\n    match tag_name {\n        NodeName::Path(path) => Some(path.clone()),\n        NodeName::Block(_) => {\n            let span = tag_name.span();\n            proc_macro_error2::emit_error!(\n                span,\n                \"blocks not allowed in tag-name position\"\n            );\n            None\n        }\n        _ => {\n            let span = tag_name.span();\n            proc_macro_error2::emit_error!(\n                span,\n                \"punctuated names not allowed in slots\"\n            );\n            None\n        }\n    }\n}\n\npub(crate) fn directive_call_from_attribute_node(\n    attr: &KeyedAttribute,\n    directive_name: &str,\n) -> TokenStream {\n    let handler = syn::Ident::new(directive_name, attr.key.span());\n\n    let param = if let Some(value) = attr.value() {\n        quote!(#value)\n    } else {\n        quote_spanned!(attr.key.span()=> ().into())\n    };\n\n    quote! { .directive(#handler, #[allow(clippy::useless_conversion)] #param) }\n}\n\nfn tuple_name(name: &str, node: &KeyedAttribute) -> TupleName {\n    if name == \"style\" || name == \"class\" {\n        if let Some(Tuple(tuple)) = node.value() {\n            {\n                if tuple.elems.len() == 2 {\n                    let style_name = &tuple.elems[0];\n                    if let Expr::Lit(ExprLit {\n                        lit: Lit::Str(s), ..\n                    }) = style_name\n                    {\n                        return TupleName::Str(s.value());\n                    } else if let Expr::Array(ExprArray { elems, .. }) =\n                        style_name\n                    {\n                        return TupleName::Array(\n                            elems\n                                .iter()\n                                .filter_map(|elem| match elem {\n                                    Expr::Lit(ExprLit {\n                                        lit: Lit::Str(s),\n                                        ..\n                                    }) => Some(s.value()),\n                                    _ => proc_macro_error2::abort!(\n                                        elem.span(),\n                                        \"invalid name\"\n                                    ),\n                                })\n                                .collect(),\n                        );\n                    }\n                }\n            }\n        }\n    }\n\n    TupleName::None\n}\n\n#[derive(Debug, PartialEq, Eq)]\nenum TupleName {\n    None,\n    Str(String),\n    Array(Vec<String>),\n}\n"
  },
  {
    "path": "leptos_macro/src/view/slot_helper.rs",
    "content": "use super::{\n    component_builder::maybe_optimised_component_children,\n    convert_to_snake_case, full_path_from_tag_name,\n};\nuse crate::view::{fragment_to_tokens, utils::filter_prefixed_attrs, TagType};\nuse proc_macro2::{Ident, TokenStream, TokenTree};\nuse quote::{quote, quote_spanned};\nuse rstml::node::{CustomNode, KeyedAttribute, NodeAttribute, NodeElement};\nuse std::collections::HashMap;\nuse syn::spanned::Spanned;\n\npub(crate) fn slot_to_tokens(\n    node: &mut NodeElement<impl CustomNode>,\n    slot: &KeyedAttribute,\n    parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,\n    global_class: Option<&TokenTree>,\n    disable_inert_html: bool,\n) {\n    let name = slot.key.to_string();\n    let name = name.trim();\n    let name = convert_to_snake_case(if name.starts_with(\"slot:\") {\n        name.replacen(\"slot:\", \"\", 1)\n    } else {\n        node.name().to_string()\n    });\n\n    let component_path = full_path_from_tag_name(node.name());\n\n    let Some(parent_slots) = parent_slots else {\n        proc_macro_error2::emit_error!(\n            node.name().span(),\n            \"slots cannot be used inside HTML elements\"\n        );\n        return;\n    };\n\n    let attrs = node\n        .attributes()\n        .iter()\n        .filter_map(|node| {\n            if let NodeAttribute::Attribute(node) = node {\n                if is_slot(node) {\n                    None\n                } else {\n                    Some(node)\n                }\n            } else {\n                None\n            }\n        })\n        .cloned()\n        .collect::<Vec<_>>();\n\n    let props = attrs\n        .iter()\n        .filter(|attr| {\n            !attr.key.to_string().starts_with(\"let:\")\n                && !attr.key.to_string().starts_with(\"clone:\")\n                && !attr.key.to_string().starts_with(\"attr:\")\n        })\n        .map(|attr| {\n            let name = &attr.key;\n\n            let value = attr\n                .value()\n                .map(|v| {\n                    quote! { #v }\n                })\n                .unwrap_or_else(|| quote! { #name });\n\n            quote! {\n                .#name(#[allow(unused_braces)] { #value })\n            }\n        });\n\n    let items_to_bind = filter_prefixed_attrs(attrs.iter(), \"let:\")\n        .into_iter()\n        .map(|ident| quote! { #ident })\n        .collect::<Vec<_>>();\n\n    let items_to_clone = filter_prefixed_attrs(attrs.iter(), \"clone:\");\n\n    let dyn_attrs = attrs\n        .iter()\n        .filter(|attr| attr.key.to_string().starts_with(\"attr:\"))\n        .filter_map(|attr| {\n            let name = &attr.key.to_string();\n            let name = name.strip_prefix(\"attr:\");\n            let value = attr.value().map(|v| {\n                quote! { #v }\n            })?;\n            Some(quote! { (#name, #value) })\n        })\n        .collect::<Vec<_>>();\n\n    let dyn_attrs = if dyn_attrs.is_empty() {\n        quote! {}\n    } else {\n        quote! { .dyn_attrs(vec![#(#dyn_attrs),*]) }\n    };\n\n    let mut slots = HashMap::new();\n    let children = if node.children.is_empty() {\n        quote! {}\n    } else if let Some(children) = maybe_optimised_component_children(\n        &node.children,\n        &items_to_bind,\n        &items_to_clone,\n    ) {\n        children\n    } else {\n        let children = fragment_to_tokens(\n            &mut node.children,\n            TagType::Unknown,\n            Some(&mut slots),\n            global_class,\n            None,\n            disable_inert_html,\n        );\n\n        // TODO view markers for hot-reloading\n        /*\n         cfg_if::cfg_if! {\n            if #[cfg(debug_assertions)] {\n                let marker = format!(\"<{component_name}/>-children\");\n                // For some reason spanning for `.children` breaks, unless `#view_marker`\n                // is also covered by `children.span()`.\n                let view_marker = quote_spanned!(children.span()=> .with_view_marker(#marker));\n            } else {\n                let view_marker = quote! {};\n            }\n        }\n        */\n        let view_marker = quote! {};\n\n        if let Some(children) = children {\n            let bindables =\n                items_to_bind.iter().map(|ident| quote! { #ident, });\n\n            let clonables = items_to_clone.iter().map(|ident| {\n                quote_spanned! {ident.span()=>\n                    let #ident = ::core::clone::Clone::clone(&#ident);\n                }\n            });\n\n            if bindables.len() > 0 {\n                quote_spanned! {children.span()=>\n                    .children({\n                        #(#clonables)*\n\n                        move |#(#bindables)*| #children #view_marker\n                    })\n                }\n            } else {\n                quote_spanned! {children.span()=>\n                    .children({\n                        #(#clonables)*\n\n                        ::leptos::children::ToChildren::to_children(move || #children #view_marker)\n                    })\n                }\n            }\n        } else {\n            quote! {}\n        }\n    };\n\n    let slots = slots.drain().map(|(slot, mut values)| {\n        let span = values\n            .last()\n            .expect(\"List of slots must not be empty\")\n            .span();\n        let slot = Ident::new(&slot, span);\n        let value = if values.len() > 1 {\n            quote! {\n                ::std::vec![\n                    #(#values)*\n                ]\n            }\n        } else {\n            values.remove(0)\n        };\n\n        quote! { .#slot(#value) }\n    });\n\n    let build = quote_spanned! {node.name().span()=>\n        .build()\n    };\n\n    let slot = quote_spanned! {node.span()=>\n        {\n            let slot = #component_path::builder()\n                #(#props)*\n                #(#slots)*\n                #children\n                #build\n                #dyn_attrs;\n\n            #[allow(unreachable_code, clippy::useless_conversion)]\n            slot.into()\n        },\n    };\n\n    // We need to move \"allow\" out of \"quote_spanned\" because it breaks hovering in rust-analyzer\n    let slot = quote!(#[allow(unused_braces)] #slot);\n\n    parent_slots\n        .entry(name)\n        .and_modify(|entry| entry.push(slot.clone()))\n        .or_insert(vec![slot]);\n}\n\npub(crate) fn is_slot(node: &KeyedAttribute) -> bool {\n    let key = node.key.to_string();\n    let key = key.trim();\n    key == \"slot\" || key.starts_with(\"slot:\")\n}\n\npub(crate) fn get_slot(\n    node: &NodeElement<impl CustomNode>,\n) -> Option<&KeyedAttribute> {\n    node.attributes().iter().find_map(|node| {\n        if let NodeAttribute::Attribute(node) = node {\n            if is_slot(node) {\n                Some(node)\n            } else {\n                None\n            }\n        } else {\n            None\n        }\n    })\n}\n"
  },
  {
    "path": "leptos_macro/src/view/snapshots/leptos_macro__view__tests__client_template__full_span__counter_component.snap",
    "content": "---\nsource: leptos_macro/src/view/tests.rs\nexpression: result\n---\nTokenStream [\n    Punct {\n        char: ':',\n        spacing: Joint,\n        span: bytes(10..82),\n    },\n    Punct {\n        char: ':',\n        spacing: Alone,\n        span: bytes(10..82),\n    },\n    Ident {\n        sym: leptos,\n        span: bytes(10..82),\n    },\n    Punct {\n        char: ':',\n        spacing: Joint,\n        span: bytes(10..82),\n    },\n    Punct {\n        char: ':',\n        spacing: Alone,\n        span: bytes(10..82),\n    },\n    Ident {\n        sym: component_view,\n        span: bytes(10..82),\n    },\n    Group {\n        delimiter: Parenthesis,\n        stream: TokenStream [\n            Punct {\n                char: '#',\n                spacing: Alone,\n                span: bytes(10..82),\n            },\n            Group {\n                delimiter: Bracket,\n                stream: TokenStream [\n                    Ident {\n                        sym: allow,\n                        span: bytes(10..82),\n                    },\n                    Group {\n                        delimiter: Parenthesis,\n                        stream: TokenStream [\n                            Ident {\n                                sym: clippy,\n                                span: bytes(10..82),\n                            },\n                            Punct {\n                                char: ':',\n                                spacing: Joint,\n                                span: bytes(10..82),\n                            },\n                            Punct {\n                                char: ':',\n                                spacing: Alone,\n                                span: bytes(10..82),\n                            },\n                            Ident {\n                                sym: needless_borrows_for_generic_args,\n                                span: bytes(10..82),\n                            },\n                        ],\n                        span: bytes(10..82),\n                    },\n                ],\n                span: bytes(10..82),\n            },\n            Punct {\n                char: '&',\n                spacing: Alone,\n                span: bytes(11..24),\n            },\n            Ident {\n                sym: SimpleCounter,\n                span: bytes(11..24),\n            },\n            Punct {\n                char: ',',\n                spacing: Alone,\n                span: bytes(10..82),\n            },\n            Punct {\n                char: ':',\n                spacing: Joint,\n                span: bytes(11..24),\n            },\n            Punct {\n                char: ':',\n                spacing: Alone,\n                span: bytes(11..24),\n            },\n            Ident {\n                sym: leptos,\n                span: bytes(11..24),\n            },\n            Punct {\n                char: ':',\n                spacing: Joint,\n                span: bytes(11..24),\n            },\n            Punct {\n                char: ':',\n                spacing: Alone,\n                span: bytes(11..24),\n            },\n            Ident {\n                sym: component_props_builder,\n                span: bytes(11..24),\n            },\n            Group {\n                delimiter: Parenthesis,\n                stream: TokenStream [\n                    Punct {\n                        char: '&',\n                        spacing: Alone,\n                        span: bytes(11..24),\n                    },\n                    Ident {\n                        sym: SimpleCounter,\n                        span: bytes(11..24),\n                    },\n                ],\n                span: bytes(11..24),\n            },\n            Punct {\n                char: '.',\n                spacing: Alone,\n                span: bytes(37..52),\n            },\n            Ident {\n                sym: initial_value,\n                span: bytes(37..50),\n            },\n            Group {\n                delimiter: Parenthesis,\n                stream: TokenStream [\n                    Punct {\n                        char: '#',\n                        spacing: Alone,\n                        span: bytes(37..52),\n                    },\n                    Group {\n                        delimiter: Bracket,\n                        stream: TokenStream [\n                            Ident {\n                                sym: allow,\n                                span: bytes(37..52),\n                            },\n                            Group {\n                                delimiter: Parenthesis,\n                                stream: TokenStream [\n                                    Ident {\n                                        sym: unused_braces,\n                                        span: bytes(37..52),\n                                    },\n                                ],\n                                span: bytes(37..52),\n                            },\n                        ],\n                        span: bytes(37..52),\n                    },\n                    Group {\n                        delimiter: Brace,\n                        stream: TokenStream [\n                            Literal {\n                                lit: 0,\n                                span: bytes(51..52),\n                            },\n                        ],\n                        span: bytes(51..52),\n                    },\n                ],\n                span: bytes(37..52),\n            },\n            Punct {\n                char: '.',\n                spacing: Alone,\n                span: bytes(65..71),\n            },\n            Ident {\n                sym: step,\n                span: bytes(65..69),\n            },\n            Group {\n                delimiter: Parenthesis,\n                stream: TokenStream [\n                    Punct {\n                        char: '#',\n                        spacing: Alone,\n                        span: bytes(65..71),\n                    },\n                    Group {\n                        delimiter: Bracket,\n                        stream: TokenStream [\n                            Ident {\n                                sym: allow,\n                                span: bytes(65..71),\n                            },\n                            Group {\n                                delimiter: Parenthesis,\n                                stream: TokenStream [\n                                    Ident {\n                                        sym: unused_braces,\n                                        span: bytes(65..71),\n                                    },\n                                ],\n                                span: bytes(65..71),\n                            },\n                        ],\n                        span: bytes(65..71),\n                    },\n                    Group {\n                        delimiter: Brace,\n                        stream: TokenStream [\n                            Literal {\n                                lit: 1,\n                                span: bytes(70..71),\n                            },\n                        ],\n                        span: bytes(70..71),\n                    },\n                ],\n                span: bytes(65..71),\n            },\n            Punct {\n                char: '.',\n                spacing: Alone,\n                span: bytes(11..24),\n            },\n            Ident {\n                sym: build,\n                span: bytes(11..24),\n            },\n            Group {\n                delimiter: Parenthesis,\n                stream: TokenStream [],\n                span: bytes(11..24),\n            },\n        ],\n        span: bytes(10..82),\n    },\n]\n"
  },
  {
    "path": "leptos_macro/src/view/utils.rs",
    "content": "use proc_macro2::Ident;\nuse quote::format_ident;\nuse rstml::node::{KeyedAttribute, NodeName};\nuse syn::{spanned::Spanned, ExprPath};\n\npub fn filter_prefixed_attrs<'a, A>(attrs: A, prefix: &str) -> Vec<Ident>\nwhere\n    A: IntoIterator<Item = &'a KeyedAttribute> + Clone,\n{\n    attrs\n        .into_iter()\n        .filter_map(|attr| {\n            attr.key\n                .to_string()\n                .strip_prefix(prefix)\n                .map(|ident| format_ident!(\"{ident}\", span = attr.key.span()))\n        })\n        .collect()\n}\n\n/// Handle nostrip: prefix:\n/// if there strip from the name, and return true to indicate that\n/// the prop should be an Option<T> and shouldn't be called on the builder if None,\n/// if Some(T) then T supplied to the builder.\npub fn is_nostrip_optional_and_update_key(key: &mut NodeName) -> bool {\n    let maybe_cleaned_name_and_span = if let NodeName::Punctuated(punct) = &key\n    {\n        if punct.len() == 2 {\n            if let Some(cleaned_name) = key.to_string().strip_prefix(\"nostrip:\")\n            {\n                punct\n                    .get(1)\n                    .map(|segment| (cleaned_name.to_string(), segment.span()))\n            } else {\n                None\n            }\n        } else {\n            None\n        }\n    } else {\n        None\n    };\n    if let Some((cleaned_name, span)) = maybe_cleaned_name_and_span {\n        *key = NodeName::Path(ExprPath {\n            attrs: vec![],\n            qself: None,\n            path: format_ident!(\"{}\", cleaned_name, span = span).into(),\n        });\n        true\n    } else {\n        false\n    }\n}\n"
  },
  {
    "path": "leptos_macro/tests/component.rs",
    "content": "use core::num::NonZeroUsize;\nuse leptos::prelude::*;\n\n#[derive(PartialEq, Debug)]\nstruct UserInfo {\n    user_id: String,\n    email: String,\n}\n\n#[derive(PartialEq, Debug)]\nstruct Admin(bool);\n\n#[component]\nfn Component(\n    #[prop(optional)] optional: bool,\n    #[prop(optional, into)] optional_into: Option<String>,\n    #[prop(optional_no_strip)] optional_no_strip: Option<String>,\n    #[prop(strip_option)] strip_option: Option<u8>,\n    #[prop(default = NonZeroUsize::new(10).unwrap())] default: NonZeroUsize,\n    #[prop(into)] into: String,\n    impl_trait: impl Fn() -> i32 + 'static,\n    #[prop(name = \"data\")] UserInfo { email, user_id }: UserInfo,\n    #[prop(name = \"tuple\")] (name, id): (String, i32),\n    #[prop(name = \"tuple_struct\")] Admin(is_admin): Admin,\n    #[prop(name = \"outside_name\")] inside_name: i32,\n) -> impl IntoView {\n    _ = optional;\n    _ = optional_into;\n    _ = optional_no_strip;\n    _ = strip_option;\n    _ = default;\n    _ = into;\n    _ = impl_trait;\n    _ = email;\n    _ = user_id;\n    _ = id;\n    _ = name;\n    _ = is_admin;\n    _ = inside_name;\n}\n\n#[test]\nfn component() {\n    let cp = ComponentProps::builder()\n        .into(\"\")\n        .strip_option(9)\n        .impl_trait(|| 42)\n        .data(UserInfo {\n            email: \"em@il\".into(),\n            user_id: \"1\".into(),\n        })\n        .tuple((\"Joe\".into(), 12))\n        .tuple_struct(Admin(true))\n        .outside_name(1)\n        .build();\n    assert!(!cp.optional);\n    assert_eq!(cp.optional_into, None);\n    assert_eq!(cp.optional_no_strip, None);\n    assert_eq!(cp.strip_option, Some(9));\n    assert_eq!(cp.default, NonZeroUsize::new(10).unwrap());\n    assert_eq!(cp.into, \"\");\n    assert_eq!((cp.impl_trait)(), 42);\n    assert_eq!(\n        cp.data,\n        UserInfo {\n            email: \"em@il\".into(),\n            user_id: \"1\".into(),\n        }\n    );\n    assert_eq!(cp.tuple, (\"Joe\".into(), 12));\n    assert_eq!(cp.tuple_struct, Admin(true));\n    assert_eq!(cp.outside_name, 1);\n}\n\n#[test]\nfn component_nostrip() {\n    // Should compile (using nostrip:optional_into in second <Component />)\n    view! {\n        <Component\n            optional_into=\"foo\"\n            strip_option=9\n            into=\"\"\n            impl_trait=|| 42\n            data=UserInfo {\n                email: \"em@il\".into(),\n                user_id: \"1\".into(),\n            }\n            tuple=(\"Joe\".into(), 12)\n            tuple_struct=Admin(true)\n            outside_name=1\n        />\n        <Component\n            nostrip:optional_into=Some(\"foo\")\n            strip_option=9\n            into=\"\"\n            impl_trait=|| 42\n            data=UserInfo {\n                email: \"em@il\".into(),\n                user_id: \"1\".into(),\n            }\n            tuple=(\"Joe\".into(), 12)\n            tuple_struct=Admin(true)\n            outside_name=1\n        />\n    };\n}\n\n#[component]\nfn WithLifetime<'a>(data: &'a str) -> impl IntoView {\n    _ = data;\n    \"static lifetime\"\n}\n\n#[test]\nfn returns_static_lifetime() {\n    #[allow(unused)]\n    fn can_return_impl_intoview_from_body() -> impl IntoView {\n        let val = String::from(\"non_static_lifetime\");\n        WithLifetime(WithLifetimeProps::builder().data(&val).build())\n    }\n}\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n#[component]\npub fn IntoReactiveValueTestComponentSignal(\n    #[prop(into)] arg1: Signal<String>,\n    #[prop(into)] arg2: Signal<String>,\n    #[prop(into)] arg3: Signal<String>,\n    #[prop(into)] arg4: Signal<usize>,\n    #[prop(into)] arg5: Signal<usize>,\n    #[prop(into)] arg6: Signal<usize>,\n    #[prop(into)] arg7: Signal<Option<usize>>,\n    #[prop(into)] arg8: ArcSignal<String>,\n    #[prop(into)] arg9: ArcSignal<String>,\n    #[prop(into)] arg10: ArcSignal<String>,\n    #[prop(into)] arg11: ArcSignal<usize>,\n    #[prop(into)] arg12: ArcSignal<usize>,\n    #[prop(into)] arg13: ArcSignal<usize>,\n    #[prop(into)] arg14: ArcSignal<Option<usize>>,\n    // Optionals:\n    #[prop(into, optional)] arg15: Option<Signal<usize>>,\n    #[prop(into, optional)] arg16_purposely_omitted: Option<Signal<usize>>,\n    #[prop(into, optional)] arg17: Option<Signal<usize>>,\n    #[prop(into, strip_option)] arg18: Option<Signal<usize>>,\n) -> impl IntoView {\n    move || {\n        view! {\n            <div>\n                <p>{arg1.get()}</p>\n                <p>{arg2.get()}</p>\n                <p>{arg3.get()}</p>\n                <p>{arg4.get()}</p>\n                <p>{arg5.get()}</p>\n                <p>{arg6.get()}</p>\n                <p>{arg7.get()}</p>\n                <p>{arg8.get()}</p>\n                <p>{arg9.get()}</p>\n                <p>{arg10.get()}</p>\n                <p>{arg11.get()}</p>\n                <p>{arg12.get()}</p>\n                <p>{arg13.get()}</p>\n                <p>{arg14.get()}</p>\n                <p>{arg15.get()}</p>\n                <p>{arg16_purposely_omitted.get()}</p>\n                <p>{arg17.get()}</p>\n                <p>{arg18.get()}</p>\n            </div>\n        }\n    }\n}\n\n#[component]\npub fn IntoReactiveValueTestComponentCallback(\n    #[prop(into)] arg1: Callback<(), String>,\n    #[prop(into)] arg2: Callback<usize, String>,\n    #[prop(into)] arg3: Callback<(usize,), String>,\n    #[prop(into)] arg4: Callback<(usize, String), String>,\n    #[prop(into)] arg5: UnsyncCallback<(), String>,\n    #[prop(into)] arg6: UnsyncCallback<usize, String>,\n    #[prop(into)] arg7: UnsyncCallback<(usize,), String>,\n    #[prop(into)] arg8: UnsyncCallback<(usize, String), String>,\n) -> impl IntoView {\n    move || {\n        view! {\n            <div>\n                <p>{arg1.run(())}</p>\n                <p>{arg2.run(1)}</p>\n                <p>{arg3.run((2,))}</p>\n                <p>{arg4.run((3, \"three\".into()))}</p>\n                <p>{arg5.run(())}</p>\n                <p>{arg6.run(1)}</p>\n                <p>{arg7.run((2,))}</p>\n                <p>{arg8.run((3, \"three\".into()))}</p>\n            </div>\n        }\n    }\n}\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n#[test]\nfn test_into_reactive_value_signal() {\n    let _ = view! {\n        <IntoReactiveValueTestComponentSignal\n            arg1=move || \"I was a reactive closure!\"\n            arg2=\"I was a basic str!\"\n            arg3=Signal::stored(\"I was already a signal!\")\n            arg4=move || 2\n            arg5=3\n            arg6=Signal::stored(4)\n            arg7=|| 2\n            arg8=move || \"I was a reactive closure!\"\n            arg9=\"I was a basic str!\"\n            arg10=ArcSignal::stored(\"I was already a signal!\".to_string())\n            arg11=move || 2\n            arg12=3\n            arg13=ArcSignal::stored(4)\n            arg14=|| 2\n            arg15=|| 2\n            nostrip:arg17=Some(|| 2)\n            arg18=|| 2\n        />\n    };\n}\n\n#[test]\nfn test_into_reactive_value_callback() {\n    let _ = view! {\n        <IntoReactiveValueTestComponentCallback\n            arg1=|| \"I was a callback static str!\"\n            arg2=|_n| \"I was a callback static str!\"\n            arg3=|(_n,)| \"I was a callback static str!\"\n            arg4=|(_n, _s)| \"I was a callback static str!\"\n            arg5=|| \"I was a callback static str!\"\n            arg6=|_n| \"I was a callback static str!\"\n            arg7=|(_n,)| \"I was a callback static str!\"\n            arg8=|(_n, _s)| \"I was a callback static str!\"\n        />\n    };\n}\n\n// an attempt to catch unhygienic macros regression\nmod macro_hygiene {\n    // To ensure no relative module path to leptos inside macros.\n    mod leptos {}\n\n    // doing this separately to below due to this being the smallest\n    // unit with the lowest import surface.\n    #[test]\n    fn view() {\n        use ::leptos::IntoView;\n        use ::leptos_macro::{component, view};\n\n        #[component]\n        fn Component() -> impl IntoView {\n            view! {\n                {()}\n                {()}\n            }\n        }\n    }\n\n    // may extend this test with other items as necessary.\n    #[test]\n    fn view_into_any() {\n        use ::leptos::{\n            prelude::{ElementChild, IntoAny},\n            IntoView,\n        };\n        use ::leptos_macro::{component, view};\n\n        #[component]\n        fn Component() -> impl IntoView {\n            view! { <div>{().into_any()} {()}</div> }\n        }\n    }\n}\n"
  },
  {
    "path": "leptos_macro/tests/memo/red.rs",
    "content": "use leptos::prelude::RwSignal;\nuse leptos_macro::memo;\n\n#[derive(Default, PartialEq)]\npub struct OuterState {\n    count: i32,\n    inner: InnerState,\n}\n\n#[derive(Clone, PartialEq, Default)]\npub struct InnerState {\n    inner_count: i32,\n    inner_name: String,\n}\n\nfn main() {\n    let outer_signal = RwSignal::new(OuterState::default());\n\n    let _ = memo!();\n\n    let _ = memo!(outer_signal);\n\n    let _ = memo!(outer_signal.);\n\n    let _ = memo!(outer_signal.inner.);\n}\n"
  },
  {
    "path": "leptos_macro/tests/memo/red.stderr",
    "content": "error: unexpected end of input, expected identifier\n  --> tests/memo/red.rs:19:13\n   |\n19 |     let _ = memo!();\n   |             ^^^^^^^\n   |\n   = note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: expected `.`\n  --> tests/memo/red.rs:21:13\n   |\n21 |     let _ = memo!(outer_signal);\n   |             ^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: unexpected end of input, expected identifier or integer\n  --> tests/memo/red.rs:23:13\n   |\n23 |     let _ = memo!(outer_signal.);\n   |             ^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: unexpected end of input, expected identifier or integer\n  --> tests/memo/red.rs:25:13\n   |\n25 |     let _ = memo!(outer_signal.inner.);\n   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "leptos_macro/tests/memo.rs",
    "content": "use leptos::prelude::RwSignal;\nuse leptos_macro::memo;\n\n#[derive(Default)]\npub struct OuterState {\n    count: i32,\n    inner: InnerState,\n}\n\n#[derive(Clone, PartialEq, Default)]\npub struct InnerState {\n    inner_count: i32,\n    inner_tuple: InnerTuple,\n}\n\n#[derive(Clone, PartialEq, Default)]\npub struct InnerTuple(String);\n\n#[test]\nfn green() {\n    let outer_signal = RwSignal::new(OuterState::default());\n\n    let _ = memo!(outer_signal.count);\n\n    let _ = memo!(outer_signal.inner.inner_count);\n    let _ = memo!(outer_signal.inner.inner_tuple.0);\n}\n\n#[test]\nfn red() {\n    let t = trybuild::TestCases::new();\n    t.compile_fail(\"tests/memo/red.rs\")\n}\n"
  },
  {
    "path": "leptos_macro/tests/params.rs",
    "content": "use leptos::prelude::*;\nuse leptos_router::params::Params;\n\n#[derive(PartialEq, Debug, Params)]\nstruct UserInfo {\n    user_id: Option<String>,\n    email: Option<String>,\n    r#type: Option<i32>,\n    not_found: Option<i32>,\n}\n\n#[test]\nfn params_test() {\n    let mut map = leptos_router::params::ParamsMap::new();\n    map.insert(\"user_id\", \"12\".to_owned());\n    map.insert(\"email\", \"em@il\".to_owned());\n    map.insert(\"type\", \"12\".to_owned());\n    let user_info = UserInfo::from_map(&map).unwrap();\n    assert_eq!(\n        UserInfo {\n            email: Some(\"em@il\".to_owned()),\n            user_id: Some(\"12\".to_owned()),\n            r#type: Some(12),\n            not_found: None,\n        },\n        user_info\n    );\n}\n"
  },
  {
    "path": "leptos_macro/tests/server.rs",
    "content": "#[cfg(not(feature = \"ssr\"))]\npub mod tests {\n    use leptos::{\n        server,\n        server_fn::{codec, Http, ServerFn, ServerFnError},\n    };\n    use std::any::TypeId;\n\n    #[test]\n    fn server_default() {\n        #[server]\n        pub async fn my_server_action() -> Result<(), ServerFnError> {\n            Ok(())\n        }\n        assert_eq!(\n            <MyServerAction as ServerFn>::PATH\n                .trim_end_matches(char::is_numeric),\n            \"/api/my_server_action\"\n        );\n        assert_eq!(\n            TypeId::of::<<MyServerAction as ServerFn>::Protocol>(),\n            TypeId::of::<Http<codec::PostUrl, codec::Json>>()\n        );\n    }\n\n    #[test]\n    fn server_full_legacy() {\n        #[server(FooBar, \"/foo/bar\", \"Cbor\", \"my_path\")]\n        pub async fn my_server_action() -> Result<(), ServerFnError> {\n            Ok(())\n        }\n        assert_eq!(<FooBar as ServerFn>::PATH, \"/foo/bar/my_path\");\n        assert_eq!(\n            TypeId::of::<<FooBar as ServerFn>::Protocol>(),\n            TypeId::of::<Http<codec::Cbor, codec::Cbor>>()\n        );\n    }\n\n    #[test]\n    fn server_all_keywords() {\n        #[server(endpoint = \"my_path\", encoding = \"Cbor\", prefix = \"/foo/bar\", name = FooBar)]\n        pub async fn my_server_action() -> Result<(), ServerFnError> {\n            Ok(())\n        }\n        assert_eq!(<FooBar as ServerFn>::PATH, \"/foo/bar/my_path\");\n        assert_eq!(\n            TypeId::of::<<FooBar as ServerFn>::Protocol>(),\n            TypeId::of::<Http<codec::Cbor, codec::Cbor>>()\n        );\n    }\n\n    #[test]\n    fn server_mix() {\n        #[server(FooBar, endpoint = \"my_path\")]\n        pub async fn my_server_action() -> Result<(), ServerFnError> {\n            Ok(())\n        }\n        assert_eq!(<FooBar as ServerFn>::PATH, \"/api/my_path\");\n        assert_eq!(\n            TypeId::of::<<FooBar as ServerFn>::Protocol>(),\n            TypeId::of::<Http<codec::PostUrl, codec::Json>>()\n        );\n    }\n\n    #[test]\n    fn server_name() {\n        #[server(name = FooBar)]\n        pub async fn my_server_action() -> Result<(), ServerFnError> {\n            Ok(())\n        }\n        assert_eq!(\n            <FooBar as ServerFn>::PATH.trim_end_matches(char::is_numeric),\n            \"/api/my_server_action\"\n        );\n        assert_eq!(\n            TypeId::of::<<FooBar as ServerFn>::Protocol>(),\n            TypeId::of::<Http<codec::PostUrl, codec::Json>>()\n        );\n    }\n\n    #[test]\n    fn server_prefix() {\n        #[server(prefix = \"/foo/bar\")]\n        pub async fn my_server_action() -> Result<(), ServerFnError> {\n            Ok(())\n        }\n        assert_eq!(\n            <MyServerAction as ServerFn>::PATH\n                .trim_end_matches(char::is_numeric),\n            \"/foo/bar/my_server_action\"\n        );\n        assert_eq!(\n            TypeId::of::<<MyServerAction as ServerFn>::Protocol>(),\n            TypeId::of::<Http<codec::PostUrl, codec::Json>>()\n        );\n    }\n\n    #[test]\n    fn server_encoding() {\n        #[server(encoding = \"GetJson\")]\n        pub async fn my_server_action() -> Result<(), ServerFnError> {\n            Ok(())\n        }\n        assert_eq!(\n            <MyServerAction as ServerFn>::PATH\n                .trim_end_matches(char::is_numeric),\n            \"/api/my_server_action\"\n        );\n        assert_eq!(\n            TypeId::of::<<MyServerAction as ServerFn>::Protocol>(),\n            TypeId::of::<Http<codec::GetUrl, codec::Json>>()\n        );\n    }\n\n    #[test]\n    fn server_endpoint() {\n        #[server(endpoint = \"/path/to/my/endpoint\")]\n        pub async fn my_server_action() -> Result<(), ServerFnError> {\n            Ok(())\n        }\n        assert_eq!(\n            <MyServerAction as ServerFn>::PATH,\n            \"/api/path/to/my/endpoint\"\n        );\n        assert_eq!(\n            TypeId::of::<<MyServerAction as ServerFn>::Protocol>(),\n            TypeId::of::<Http<codec::PostUrl, codec::Json>>()\n        );\n    }\n}\n"
  },
  {
    "path": "leptos_macro/tests/slice/red.rs",
    "content": "use leptos_macro::slice;\nuse leptos::prelude::RwSignal;\n\n#[derive(Default, PartialEq)]\npub struct OuterState {\n    count: i32,\n    inner: InnerState,\n}\n\n#[derive(Clone, PartialEq, Default)]\npub struct InnerState {\n    inner_count: i32,\n    inner_name: String,\n}\n\nfn main() {\n    let outer_signal = RwSignal::new(OuterState::default());\n\n    let (_, _) = slice!();\n\n    let (_, _) = slice!(outer_signal);\n\n    let (_, _) = slice!(outer_signal.);\n\n    let (_, _) = slice!(outer_signal.inner.);\n}\n"
  },
  {
    "path": "leptos_macro/tests/slice/red.stderr",
    "content": "error: unexpected end of input, expected identifier\n  --> tests/slice/red.rs:19:18\n   |\n19 |     let (_, _) = slice!();\n   |                  ^^^^^^^^\n   |\n   = note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: expected `.`\n  --> tests/slice/red.rs:21:18\n   |\n21 |     let (_, _) = slice!(outer_signal);\n   |                  ^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: unexpected end of input, expected identifier or integer\n  --> tests/slice/red.rs:23:18\n   |\n23 |     let (_, _) = slice!(outer_signal.);\n   |                  ^^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: unexpected end of input, expected identifier or integer\n  --> tests/slice/red.rs:25:18\n   |\n25 |     let (_, _) = slice!(outer_signal.inner.);\n   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "leptos_macro/tests/slice.rs",
    "content": "use leptos::prelude::RwSignal;\nuse leptos_macro::slice;\n\n#[derive(Default)]\npub struct OuterState {\n    count: i32,\n    inner: InnerState,\n}\n\n#[derive(Clone, PartialEq, Default)]\npub struct InnerState {\n    inner_count: i32,\n    inner_tuple: InnerTuple,\n}\n\n#[derive(Clone, PartialEq, Default)]\npub struct InnerTuple(String);\n\n#[test]\nfn green() {\n    let outer_signal = RwSignal::new(OuterState::default());\n\n    let (_, _) = slice!(outer_signal.count);\n\n    let (_, _) = slice!(outer_signal.inner.inner_count);\n    let (_, _) = slice!(outer_signal.inner.inner_tuple.0);\n}\n\n#[test]\nfn red() {\n    let t = trybuild::TestCases::new();\n    t.compile_fail(\"tests/slice/red.rs\")\n}\n"
  },
  {
    "path": "leptos_macro/tests/ui/component.rs",
    "content": "use leptos::prelude::*;\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn missing_scope() {}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn missing_return_type() {}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn unknown_prop_option(#[prop(hello)] test: bool) -> impl IntoView {\n    _ = test;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn optional_and_optional_no_strip(\n    #[prop(optional, optional_no_strip)] conflicting: bool,\n) -> impl IntoView {\n    _ = conflicting;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn optional_and_strip_option(\n    #[prop(optional, strip_option)] conflicting: bool,\n) -> impl IntoView {\n    _ = conflicting;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn optional_no_strip_and_strip_option(\n    #[prop(optional_no_strip, strip_option)] conflicting: bool,\n) -> impl IntoView {\n    _ = conflicting;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn default_without_value(#[prop(default)] default: bool) -> impl IntoView {\n    _ = default;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn default_with_invalid_value(\n    #[prop(default= |)] default: bool,\n) -> impl IntoView {\n    _ = default;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[component]\nfn destructure_without_name((default, value): (bool, i32)) -> impl IntoView {\n    _ = default;\n    _ = value;\n}\n\nfn main() {}\n"
  },
  {
    "path": "leptos_macro/tests/ui/component.stderr",
    "content": "error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into`, `attrs` and `name`\n  --> tests/ui/component.rs:10:31\n   |\n10 | fn unknown_prop_option(#[prop(hello)] test: bool) -> impl IntoView {\n   |                               ^^^^^\n\nerror: `optional` conflicts with mutually exclusive `optional_no_strip`\n  --> tests/ui/component.rs:16:12\n   |\n16 |     #[prop(optional, optional_no_strip)] conflicting: bool,\n   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: `optional` conflicts with mutually exclusive `strip_option`\n  --> tests/ui/component.rs:23:12\n   |\n23 |     #[prop(optional, strip_option)] conflicting: bool,\n   |            ^^^^^^^^^^^^^^^^^^^^^^\n\nerror: `optional_no_strip` conflicts with mutually exclusive `strip_option`\n  --> tests/ui/component.rs:30:12\n   |\n30 |     #[prop(optional_no_strip, strip_option)] conflicting: bool,\n   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: unexpected end of input, expected `=` or `(`\n\n       = help: try `#[prop(default = 5 * 10)]`\n  --> tests/ui/component.rs:35:1\n   |\n35 | #[component]\n   | ^^^^^^^^^^^^\n   |\n   = note: this error originates in the attribute macro `component` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: unexpected end of input, expected one of: identifier, `::`, `<`, `_`, literal, `const`, `ref`, `mut`, `&`, parentheses, square brackets, `..`, `const`\n\n       = help: try `#[prop(default = 5 * 10)]`\n  --> tests/ui/component.rs:40:1\n   |\n40 | #[component]\n   | ^^^^^^^^^^^^\n   |\n   = note: this error originates in the attribute macro `component` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: destructured props must be given a name e.g. #[prop(name = \"data\")]\n  --> tests/ui/component.rs:48:29\n   |\n48 | fn destructure_without_name((default, value): (bool, i32)) -> impl IntoView {\n   |                             ^^^^^^^^^^^^^^^^\n"
  },
  {
    "path": "leptos_macro/tests/ui/component_absolute.rs",
    "content": "#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[::leptos::component]\nfn missing_return_type() {}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[::leptos::component]\nfn unknown_prop_option(#[prop(hello)] test: bool) -> impl ::leptos::IntoView {\n    _ = test;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[::leptos::component]\nfn optional_and_optional_no_strip(\n    #[prop(optional, optional_no_strip)] conflicting: bool,\n) -> impl IntoView {\n    _ = conflicting;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[::leptos::component]\nfn optional_and_strip_option(\n    #[prop(optional, strip_option)] conflicting: bool,\n) -> impl ::leptos::IntoView {\n    _ = conflicting;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[::leptos::component]\nfn optional_no_strip_and_strip_option(\n    #[prop(optional_no_strip, strip_option)] conflicting: bool,\n) -> impl ::leptos::IntoView {\n    _ = conflicting;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[::leptos::component]\nfn default_without_value(\n    #[prop(default)] default: bool,\n) -> impl ::leptos::IntoView {\n    _ = default;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[::leptos::component]\nfn default_with_invalid_value(\n    #[prop(default= |)] default: bool,\n) -> impl ::leptos::IntoView {\n    _ = default;\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\n#[::leptos::component]\npub fn using_the_view_macro() -> impl ::leptos::IntoView {\n    leptos::view! { \"ok\" }\n}\n\nfn main() {}\n"
  },
  {
    "path": "leptos_macro/tests/ui/component_absolute.stderr",
    "content": "error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into`, `attrs` and `name`\n --> tests/ui/component_absolute.rs:5:31\n  |\n5 | fn unknown_prop_option(#[prop(hello)] test: bool) -> impl ::leptos::IntoView {\n  |                               ^^^^^\n\nerror: `optional` conflicts with mutually exclusive `optional_no_strip`\n  --> tests/ui/component_absolute.rs:11:12\n   |\n11 |     #[prop(optional, optional_no_strip)] conflicting: bool,\n   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: `optional` conflicts with mutually exclusive `strip_option`\n  --> tests/ui/component_absolute.rs:18:12\n   |\n18 |     #[prop(optional, strip_option)] conflicting: bool,\n   |            ^^^^^^^^^^^^^^^^^^^^^^\n\nerror: `optional_no_strip` conflicts with mutually exclusive `strip_option`\n  --> tests/ui/component_absolute.rs:25:12\n   |\n25 |     #[prop(optional_no_strip, strip_option)] conflicting: bool,\n   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: unexpected end of input, expected `=` or `(`\n\n       = help: try `#[prop(default = 5 * 10)]`\n  --> tests/ui/component_absolute.rs:30:1\n   |\n30 | #[::leptos::component]\n   | ^^^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the attribute macro `::leptos::component` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: unexpected end of input, expected one of: identifier, `::`, `<`, `_`, literal, `const`, `ref`, `mut`, `&`, parentheses, square brackets, `..`, `const`\n\n       = help: try `#[prop(default = 5 * 10)]`\n  --> tests/ui/component_absolute.rs:37:1\n   |\n37 | #[::leptos::component]\n   | ^^^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the attribute macro `::leptos::component` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "leptos_macro/tests/ui/server.rs",
    "content": "use leptos::prelude::*;\n\n#[server(endpoint = \"my_path\", FooBar)]\npub async fn positional_argument_follows_keyword_argument(\n) -> Result<(), ServerFnError> {\n    Ok(())\n}\n\n#[server(endpoint = \"first\", endpoint = \"second\")]\npub async fn keyword_argument_repeated() -> Result<(), ServerFnError> {\n    Ok(())\n}\n\n#[server(Foo, Bar)]\npub async fn expected_string_literal() -> Result<(), ServerFnError> {\n    Ok(())\n}\n#[server(Foo, Bar, bazz)]\npub async fn expected_string_literal_2() -> Result<(), ServerFnError> {\n    Ok(())\n}\n\n#[server(\"Foo\")]\npub async fn expected_identifier() -> Result<(), ServerFnError> {\n    Ok(())\n}\n\n#[server(Foo Bar)]\npub async fn expected_comma() -> Result<(), ServerFnError> {\n    Ok(())\n}\n\n#[server(FooBar, \"/foo/bar\", \"Cbor\", \"my_path\", \"extra\")]\npub async fn unexpected_extra_argument() -> Result<(), ServerFnError> {\n    Ok(())\n}\n\n#[server(encoding = \"wrong\")]\npub async fn encoding_not_found() -> Result<(), ServerFnError> {\n    Ok(())\n}\n\nfn main() {}\n"
  },
  {
    "path": "leptos_macro/tests/ui/server.stderr",
    "content": "error: positional argument follows keyword argument\n --> tests/ui/server.rs:3:32\n  |\n3 | #[server(endpoint = \"my_path\", FooBar)]\n  |                                ^^^^^^\n\nerror: keyword argument repeated: `endpoint`\n --> tests/ui/server.rs:9:30\n  |\n9 | #[server(endpoint = \"first\", endpoint = \"second\")]\n  |                              ^^^^^^^^\n\nerror: expected string literal\n  --> tests/ui/server.rs:14:15\n   |\n14 | #[server(Foo, Bar)]\n   |               ^^^\n\nerror: expected string literal\n  --> tests/ui/server.rs:18:15\n   |\n18 | #[server(Foo, Bar, bazz)]\n   |               ^^^\n\nerror: expected identifier\n  --> tests/ui/server.rs:23:10\n   |\n23 | #[server(\"Foo\")]\n   |          ^^^^^\n\nerror: expected `,`\n  --> tests/ui/server.rs:28:14\n   |\n28 | #[server(Foo Bar)]\n   |              ^^^\n\nerror: unexpected extra argument\n  --> tests/ui/server.rs:33:49\n   |\n33 | #[server(FooBar, \"/foo/bar\", \"Cbor\", \"my_path\", \"extra\")]\n   |                                                 ^^^^^^^\n\nerror: Encoding not found.\n  --> tests/ui/server.rs:38:21\n   |\n38 | #[server(encoding = \"wrong\")]\n   |                     ^^^^^^^\n"
  },
  {
    "path": "leptos_macro/tests/ui.rs",
    "content": "#[cfg(not(feature = \"__internal_erase_components\"))]\n#[test]\nfn ui() {\n    let t = trybuild::TestCases::new();\n    #[cfg(all(feature = \"nightly\", rustc_nightly))]\n    t.compile_fail(\"tests/ui/component.rs\");\n    #[cfg(all(feature = \"nightly\", rustc_nightly))]\n    t.compile_fail(\"tests/ui/component_absolute.rs\");\n    t.compile_fail(\"tests/ui/server.rs\");\n}\n"
  },
  {
    "path": "leptos_server/Cargo.toml",
    "content": "[package]\nname = \"leptos_server\"\nversion = \"0.8.7\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"RPC for the Leptos web framework.\"\nreadme = \"../README.md\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nbase64 = { workspace = true, default-features = true }\ncodee = { features = [\"json_serde\"], workspace = true, default-features = true }\nhydration_context = { workspace = true }\nreactive_graph = { workspace = true, features = [\"hydration\"] }\nserver_fn = { workspace = true }\ntracing = { optional = true, workspace = true, default-features = true }\nfutures = { workspace = true, default-features = true }\n\nany_spawner = { workspace = true }\nor_poisoned = { workspace = true }\ntachys = { workspace = true, optional = true, features = [\"reactive_graph\"] }\nsend_wrapper = { workspace = true, default-features = true }\n\n# serialization formats\nserde = { workspace = true, default-features = true }\njs-sys = { optional = true, workspace = true, default-features = true }\nwasm-bindgen = { workspace = true, optional = true, default-features = true }\nserde_json = { workspace = true, default-features = true }\n\n[features]\nssr = []\nhydration = []\nminiserde = [\"codee/miniserde\"]\nrkyv = [\"codee/rkyv\"]\nserde-wasm-bindgen = [\"codee/json_serde_wasm\"]\nserde-lite = [\"codee/serde_lite\"]\ntachys = [\"dep:tachys\"]\ntracing = [\"dep:tracing\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\"]\nmax_combination_size = 2\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\", \"--cfg\", \"docsrs\"]\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(leptos_debuginfo)'] }\n"
  },
  {
    "path": "leptos_server/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "leptos_server/src/action.rs",
    "content": "use reactive_graph::{\n    actions::{Action, ArcAction},\n    owner::use_context,\n    traits::DefinedAt,\n};\nuse server_fn::{\n    error::{FromServerFnError, ServerFnUrlError},\n    ServerFn,\n};\nuse std::{ops::Deref, panic::Location, sync::Arc};\n\n/// An error that can be caused by a server action.\n///\n/// This is used for propagating errors from the server to the client when JS/WASM are not\n/// supported.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ServerActionError {\n    path: Arc<str>,\n    err: Arc<str>,\n}\n\nimpl ServerActionError {\n    /// Creates a new error associated with the given path.\n    pub fn new(path: &str, err: &str) -> Self {\n        Self {\n            path: path.into(),\n            err: err.into(),\n        }\n    }\n\n    /// The path with which this error is associated.\n    pub fn path(&self) -> &str {\n        &self.path\n    }\n\n    /// The error message.\n    pub fn err(&self) -> &str {\n        &self.err\n    }\n}\n\n/// An [`ArcAction`] that can be used to call a server function.\npub struct ArcServerAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    inner: ArcAction<S, Result<S::Output, S::Error>>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<S> ArcServerAction<S>\nwhere\n    S: ServerFn + Clone + Send + Sync + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n    S::Error: FromServerFnError,\n{\n    /// Creates a new [`ArcAction`] that will call the server function `S` when dispatched.\n    #[track_caller]\n    pub fn new() -> Self {\n        let err = use_context::<ServerActionError>().and_then(|error| {\n            (error.path() == S::PATH)\n                .then(|| ServerFnUrlError::<S::Error>::decode_err(error.err()))\n                .map(Err)\n        });\n        Self {\n            inner: ArcAction::new_with_value(err, |input: &S| {\n                S::run_on_client(input.clone())\n            }),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<S> Deref for ArcServerAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    type Target = ArcAction<S, Result<S::Output, S::Error>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl<S> Clone for ArcServerAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n        }\n    }\n}\n\nimpl<S> Default for ArcServerAction<S>\nwhere\n    S: ServerFn + Clone + Send + Sync + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<S> DefinedAt for ArcServerAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\n/// An [`Action`] that can be used to call a server function.\npub struct ServerAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    inner: Action<S, Result<S::Output, S::Error>>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<S> ServerAction<S>\nwhere\n    S: ServerFn + Send + Sync + Clone + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n{\n    /// Creates a new [`Action`] that will call the server function `S` when dispatched.\n    pub fn new() -> Self {\n        let err = use_context::<ServerActionError>().and_then(|error| {\n            (error.path() == S::PATH)\n                .then(|| ServerFnUrlError::<S::Error>::decode_err(error.err()))\n                .map(Err)\n        });\n        Self {\n            inner: Action::new_with_value(err, |input: &S| {\n                S::run_on_client(input.clone())\n            }),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<S> Clone for ServerAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<S> Copy for ServerAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n}\n\nimpl<S> Deref for ServerAction<S>\nwhere\n    S: ServerFn + Clone + Send + Sync + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n{\n    type Target = Action<S, Result<S::Output, S::Error>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl<S> From<ServerAction<S>> for Action<S, Result<S::Output, S::Error>>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn from(value: ServerAction<S>) -> Self {\n        value.inner\n    }\n}\n\nimpl<S> Default for ServerAction<S>\nwhere\n    S: ServerFn + Clone + Send + Sync + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<S> DefinedAt for ServerAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "leptos_server/src/lib.rs",
    "content": "//! Utilities for communicating between the server and the client with Leptos.\n\n#![deny(missing_docs)]\n#![forbid(unsafe_code)]\n\nmod action;\npub use action::*;\nuse std::borrow::Borrow;\nmod local_resource;\npub use local_resource::*;\nmod multi_action;\npub use multi_action::*;\nmod once_resource;\npub use once_resource::*;\nmod resource;\npub use resource::*;\nmod shared;\n\nuse base64::{engine::general_purpose::STANDARD_NO_PAD, DecodeError, Engine};\n/// Re-export of the `codee` crate.\npub use codee;\npub use shared::*;\n\n/// Encodes data into a string.\npub trait IntoEncodedString {\n    /// Encodes the data.\n    fn into_encoded_string(self) -> String;\n}\n\n/// Decodes data from a string.\npub trait FromEncodedStr {\n    /// The decoded data.\n    type DecodedType<'a>: Borrow<Self>;\n\n    /// The type of an error encountered during decoding.\n    type DecodingError;\n\n    /// Decodes the string.\n    fn from_encoded_str(\n        data: &str,\n    ) -> Result<Self::DecodedType<'_>, Self::DecodingError>;\n}\n\nimpl IntoEncodedString for String {\n    fn into_encoded_string(self) -> String {\n        self\n    }\n}\n\nimpl FromEncodedStr for str {\n    type DecodedType<'a> = &'a str;\n    type DecodingError = ();\n\n    fn from_encoded_str(\n        data: &str,\n    ) -> Result<Self::DecodedType<'_>, Self::DecodingError> {\n        Ok(data)\n    }\n}\n\nimpl IntoEncodedString for Vec<u8> {\n    fn into_encoded_string(self) -> String {\n        STANDARD_NO_PAD.encode(self)\n    }\n}\n\nimpl FromEncodedStr for [u8] {\n    type DecodedType<'a> = Vec<u8>;\n    type DecodingError = DecodeError;\n\n    fn from_encoded_str(\n        data: &str,\n    ) -> Result<Self::DecodedType<'_>, Self::DecodingError> {\n        STANDARD_NO_PAD.decode(data)\n    }\n}\n\n#[cfg(feature = \"tachys\")]\nmod view_implementations {\n    use crate::Resource;\n    use reactive_graph::traits::Read;\n    use std::future::Future;\n    use tachys::{\n        html::attribute::{any_attribute::AnyAttribute, Attribute},\n        hydration::Cursor,\n        reactive_graph::{RenderEffectState, Suspend, SuspendState},\n        ssr::StreamBuilder,\n        view::{\n            add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,\n        },\n    };\n\n    impl<T, Ser> Render for Resource<T, Ser>\n    where\n        T: Render + Send + Sync + Clone,\n        Ser: Send + 'static,\n    {\n        type State = RenderEffectState<SuspendState<T>>;\n\n        fn build(self) -> Self::State {\n            (move || Suspend::new(async move { self.await })).build()\n        }\n\n        fn rebuild(self, state: &mut Self::State) {\n            (move || Suspend::new(async move { self.await })).rebuild(state)\n        }\n    }\n\n    impl<T, Ser> AddAnyAttr for Resource<T, Ser>\n    where\n        T: RenderHtml + Send + Sync + Clone,\n        Ser: Send + 'static,\n    {\n        type Output<SomeNewAttr: Attribute> = Box<\n            dyn FnMut() -> Suspend<\n                <T as AddAnyAttr>::Output<\n                    <SomeNewAttr::CloneableOwned as Attribute>::CloneableOwned,\n                >,\n            >\n            + Send\n        >;\n\n        fn add_any_attr<NewAttr: Attribute>(\n            self,\n            attr: NewAttr,\n        ) -> Self::Output<NewAttr>\n        where\n            Self::Output<NewAttr>: RenderHtml,\n        {\n            (move || Suspend::new(async move { self.await })).add_any_attr(attr)\n        }\n    }\n\n    impl<T, Ser> RenderHtml for Resource<T, Ser>\n    where\n        T: RenderHtml + Send + Sync + Clone,\n        Ser: Send + 'static,\n    {\n        type AsyncOutput = Option<T>;\n        type Owned = Self;\n\n        const MIN_LENGTH: usize = 0;\n\n        fn dry_resolve(&mut self) {\n            self.read();\n        }\n\n        fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send {\n            (move || Suspend::new(async move { self.await })).resolve()\n        }\n\n        fn to_html_with_buf(\n            self,\n            buf: &mut String,\n            position: &mut Position,\n            escape: bool,\n            mark_branches: bool,\n            extra_attrs: Vec<AnyAttribute>,\n        ) {\n            (move || Suspend::new(async move { self.await })).to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n        }\n\n        fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n            self,\n            buf: &mut StreamBuilder,\n            position: &mut Position,\n            escape: bool,\n            mark_branches: bool,\n            extra_attrs: Vec<AnyAttribute>,\n        ) where\n            Self: Sized,\n        {\n            (move || Suspend::new(async move { self.await }))\n                .to_html_async_with_buf::<OUT_OF_ORDER>(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n        }\n\n        fn hydrate<const FROM_SERVER: bool>(\n            self,\n            cursor: &Cursor,\n            position: &PositionState,\n        ) -> Self::State {\n            (move || Suspend::new(async move { self.await }))\n                .hydrate::<FROM_SERVER>(cursor, position)\n        }\n\n        fn into_owned(self) -> Self::Owned {\n            self\n        }\n    }\n}\n"
  },
  {
    "path": "leptos_server/src/local_resource.rs",
    "content": "use reactive_graph::{\n    computed::{\n        suspense::LocalResourceNotifier, ArcAsyncDerived, AsyncDerived,\n        AsyncDerivedFuture,\n    },\n    graph::{\n        AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,\n        ToAnySource, ToAnySubscriber,\n    },\n    owner::use_context,\n    send_wrapper_ext::SendOption,\n    signal::{\n        guards::{AsyncPlain, Mapped, ReadGuard},\n        ArcRwSignal, RwSignal,\n    },\n    traits::{\n        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,\n        Update, With, Write,\n    },\n};\nuse std::{\n    future::{pending, Future, IntoFuture},\n    ops::{Deref, DerefMut},\n    panic::Location,\n};\n\n/// A reference-counted resource that only loads its data locally on the client.\npub struct ArcLocalResource<T> {\n    data: ArcAsyncDerived<T>,\n    refetch: ArcRwSignal<usize>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<T> Clone for ArcLocalResource<T> {\n    fn clone(&self) -> Self {\n        Self {\n            data: self.data.clone(),\n            refetch: self.refetch.clone(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n        }\n    }\n}\n\nimpl<T> Deref for ArcLocalResource<T> {\n    type Target = ArcAsyncDerived<T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.data\n    }\n}\n\nimpl<T> ArcLocalResource<T> {\n    /// Creates the resource.\n    ///\n    /// This will only begin loading data if you are on the client (i.e., if you do not have the\n    /// `ssr` feature activated).\n    #[track_caller]\n    pub fn new<Fut>(fetcher: impl Fn() -> Fut + 'static) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        let fetcher = move || {\n            let fut = fetcher();\n            async move {\n                // in SSR mode, this will simply always be pending\n                // if we try to read from it, we will trigger Suspense automatically to fall back\n                // so this will never need to return anything\n                if cfg!(feature = \"ssr\") {\n                    pending().await\n                } else {\n                    // LocalResources that are immediately available can cause a hydration error,\n                    // because the future *looks* like it is already ready (and therefore would\n                    // already have been rendered to html on the server), but in fact was ignored\n                    // on the server. the simplest way to avoid this is to ensure that we always\n                    // wait a tick before resolving any value for a localresource.\n                    any_spawner::Executor::tick().await;\n                    fut.await\n                }\n            }\n        };\n        let refetch = ArcRwSignal::new(0);\n\n        Self {\n            data: if cfg!(feature = \"ssr\") {\n                ArcAsyncDerived::new_mock(fetcher)\n            } else {\n                let refetch = refetch.clone();\n                ArcAsyncDerived::new_unsync(move || {\n                    refetch.track();\n                    fetcher()\n                })\n            },\n            refetch,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Re-runs the async function.\n    pub fn refetch(&self) {\n        *self.refetch.write() += 1;\n    }\n\n    /// Synchronously, reactively reads the current value of the resource and applies the function\n    /// `f` to its value if it is `Some(_)`.\n    #[track_caller]\n    pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>\n    where\n        T: 'static,\n    {\n        self.data.try_with(|n| n.as_ref().map(f))?\n    }\n}\n\nimpl<T, E> ArcLocalResource<Result<T, E>>\nwhere\n    T: 'static,\n    E: Clone + 'static,\n{\n    /// Applies the given function when a resource that returns `Result<T, E>`\n    /// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`\n    /// calls over the `Option<Result<_, _>>` returned by the resource.\n    ///\n    /// This is useful when used with features like server functions, in conjunction\n    /// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are\n    /// left to handle the `None` and `Err(_)` states.\n    #[track_caller]\n    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {\n        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))\n    }\n}\n\nimpl<T> IntoFuture for ArcLocalResource<T>\nwhere\n    T: Clone + 'static,\n{\n    type Output = T;\n    type IntoFuture = AsyncDerivedFuture<T>;\n\n    fn into_future(self) -> Self::IntoFuture {\n        if let Some(mut notifier) = use_context::<LocalResourceNotifier>() {\n            notifier.notify();\n        } else if cfg!(feature = \"ssr\") {\n            panic!(\n                \"Reading from a LocalResource outside Suspense in `ssr` mode \\\n                 will cause the response to hang, because LocalResources are \\\n                 always pending on the server.\"\n            );\n        }\n        self.data.into_future()\n    }\n}\n\nimpl<T> DefinedAt for ArcLocalResource<T> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> Notify for ArcLocalResource<T>\nwhere\n    T: 'static,\n{\n    fn notify(&self) {\n        self.data.notify()\n    }\n}\n\nimpl<T> Write for ArcLocalResource<T>\nwhere\n    T: 'static,\n{\n    type Value = Option<T>;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.data.try_write()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.data.try_write_untracked()\n    }\n}\n\nimpl<T> ReadUntracked for ArcLocalResource<T>\nwhere\n    T: 'static,\n{\n    type Value =\n        ReadGuard<Option<T>, Mapped<AsyncPlain<SendOption<T>>, Option<T>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        if let Some(mut notifier) = use_context::<LocalResourceNotifier>() {\n            notifier.notify();\n        }\n        self.data.try_read_untracked()\n    }\n}\n\nimpl<T: 'static> IsDisposed for ArcLocalResource<T> {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T: 'static> ToAnySource for ArcLocalResource<T> {\n    fn to_any_source(&self) -> AnySource {\n        self.data.to_any_source()\n    }\n}\n\nimpl<T: 'static> ToAnySubscriber for ArcLocalResource<T> {\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        self.data.to_any_subscriber()\n    }\n}\n\nimpl<T> Source for ArcLocalResource<T> {\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        self.data.add_subscriber(subscriber)\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        self.data.remove_subscriber(subscriber);\n    }\n\n    fn clear_subscribers(&self) {\n        self.data.clear_subscribers();\n    }\n}\n\nimpl<T> ReactiveNode for ArcLocalResource<T> {\n    fn mark_dirty(&self) {\n        self.data.mark_dirty();\n    }\n\n    fn mark_check(&self) {\n        self.data.mark_check();\n    }\n\n    fn mark_subscribers_check(&self) {\n        self.data.mark_subscribers_check();\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        self.data.update_if_necessary()\n    }\n}\n\nimpl<T> Subscriber for ArcLocalResource<T> {\n    fn add_source(&self, source: AnySource) {\n        self.data.add_source(source);\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        self.data.clear_sources(subscriber);\n    }\n}\n\n/// A resource that only loads its data locally on the client.\npub struct LocalResource<T> {\n    data: AsyncDerived<T>,\n    refetch: RwSignal<usize>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<T> Deref for LocalResource<T> {\n    type Target = AsyncDerived<T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.data\n    }\n}\n\nimpl<T> Clone for LocalResource<T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T> Copy for LocalResource<T> {}\n\nimpl<T> LocalResource<T> {\n    /// Creates the resource.\n    ///\n    /// This will only begin loading data if you are on the client (i.e., if you do not have the\n    /// `ssr` feature activated).\n    #[track_caller]\n    pub fn new<Fut>(fetcher: impl Fn() -> Fut + 'static) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        let fetcher = move || {\n            let fut = fetcher();\n            async move {\n                // in SSR mode, this will simply always be pending\n                // if we try to read from it, we will trigger Suspense automatically to fall back\n                // so this will never need to return anything\n                if cfg!(feature = \"ssr\") {\n                    pending().await\n                } else {\n                    // LocalResources that are immediately available can cause a hydration error,\n                    // because the future *looks* like it is already ready (and therefore would\n                    // already have been rendered to html on the server), but in fact was ignored\n                    // on the server. the simplest way to avoid this is to ensure that we always\n                    // wait a tick before resolving any value for a localresource.\n                    any_spawner::Executor::tick().await;\n                    fut.await\n                }\n            }\n        };\n        let refetch = RwSignal::new(0);\n\n        Self {\n            data: if cfg!(feature = \"ssr\") {\n                AsyncDerived::new_mock(fetcher)\n            } else {\n                AsyncDerived::new_unsync_threadsafe_storage(move || {\n                    refetch.track();\n                    fetcher()\n                })\n            },\n            refetch,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Re-runs the async function.\n    pub fn refetch(&self) {\n        self.refetch.try_update(|n| *n += 1);\n    }\n\n    /// Synchronously, reactively reads the current value of the resource and applies the function\n    /// `f` to its value if it is `Some(_)`.\n    #[track_caller]\n    pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>\n    where\n        T: 'static,\n    {\n        self.data.try_with(|n| n.as_ref().map(f))?\n    }\n}\n\nimpl<T, E> LocalResource<Result<T, E>>\nwhere\n    T: 'static,\n    E: Clone + 'static,\n{\n    /// Applies the given function when a resource that returns `Result<T, E>`\n    /// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`\n    /// calls over the `Option<Result<_, _>>` returned by the resource.\n    ///\n    /// This is useful when used with features like server functions, in conjunction\n    /// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are\n    /// left to handle the `None` and `Err(_)` states.\n    #[track_caller]\n    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {\n        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))\n    }\n}\n\nimpl<T> IntoFuture for LocalResource<T>\nwhere\n    T: Clone + 'static,\n{\n    type Output = T;\n    type IntoFuture = AsyncDerivedFuture<T>;\n\n    fn into_future(self) -> Self::IntoFuture {\n        if let Some(mut notifier) = use_context::<LocalResourceNotifier>() {\n            notifier.notify();\n        } else if cfg!(feature = \"ssr\") {\n            panic!(\n                \"Reading from a LocalResource outside Suspense in `ssr` mode \\\n                 will cause the response to hang, because LocalResources are \\\n                 always pending on the server.\"\n            );\n        }\n        self.data.into_future()\n    }\n}\n\nimpl<T> DefinedAt for LocalResource<T> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> Notify for LocalResource<T>\nwhere\n    T: 'static,\n{\n    fn notify(&self) {\n        self.data.notify()\n    }\n}\n\nimpl<T> Write for LocalResource<T>\nwhere\n    T: 'static,\n{\n    type Value = Option<T>;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.data.try_write()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.data.try_write_untracked()\n    }\n}\n\nimpl<T> ReadUntracked for LocalResource<T>\nwhere\n    T: 'static,\n{\n    type Value =\n        ReadGuard<Option<T>, Mapped<AsyncPlain<SendOption<T>>, Option<T>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        if let Some(mut notifier) = use_context::<LocalResourceNotifier>() {\n            notifier.notify();\n        }\n        self.data.try_read_untracked()\n    }\n}\n\nimpl<T: 'static> IsDisposed for LocalResource<T> {\n    fn is_disposed(&self) -> bool {\n        self.data.is_disposed()\n    }\n}\n\nimpl<T: 'static> ToAnySource for LocalResource<T>\nwhere\n    T: 'static,\n{\n    fn to_any_source(&self) -> AnySource {\n        self.data.to_any_source()\n    }\n}\n\nimpl<T: 'static> ToAnySubscriber for LocalResource<T>\nwhere\n    T: 'static,\n{\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        self.data.to_any_subscriber()\n    }\n}\n\nimpl<T> Source for LocalResource<T>\nwhere\n    T: 'static,\n{\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        self.data.add_subscriber(subscriber)\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        self.data.remove_subscriber(subscriber);\n    }\n\n    fn clear_subscribers(&self) {\n        self.data.clear_subscribers();\n    }\n}\n\nimpl<T> ReactiveNode for LocalResource<T>\nwhere\n    T: 'static,\n{\n    fn mark_dirty(&self) {\n        self.data.mark_dirty();\n    }\n\n    fn mark_check(&self) {\n        self.data.mark_check();\n    }\n\n    fn mark_subscribers_check(&self) {\n        self.data.mark_subscribers_check();\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        self.data.update_if_necessary()\n    }\n}\n\nimpl<T> Subscriber for LocalResource<T>\nwhere\n    T: 'static,\n{\n    fn add_source(&self, source: AnySource) {\n        self.data.add_source(source);\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        self.data.clear_sources(subscriber);\n    }\n}\n\nimpl<T: 'static> From<ArcLocalResource<T>> for LocalResource<T> {\n    fn from(arc: ArcLocalResource<T>) -> Self {\n        Self {\n            data: arc.data.into(),\n            refetch: arc.refetch.into(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: arc.defined_at,\n        }\n    }\n}\n\nimpl<T: 'static> From<LocalResource<T>> for ArcLocalResource<T> {\n    fn from(local: LocalResource<T>) -> Self {\n        Self {\n            data: local.data.into(),\n            refetch: local.refetch.into(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: local.defined_at,\n        }\n    }\n}\n"
  },
  {
    "path": "leptos_server/src/multi_action.rs",
    "content": "use reactive_graph::{\n    actions::{ArcMultiAction, MultiAction},\n    traits::DefinedAt,\n};\nuse server_fn::ServerFn;\nuse std::{ops::Deref, panic::Location};\n\n/// An [`ArcMultiAction`] that can be used to call a server function.\npub struct ArcServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    inner: ArcMultiAction<S, Result<S::Output, S::Error>>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<S> ArcServerMultiAction<S>\nwhere\n    S: ServerFn + Clone + Send + Sync + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n{\n    /// Creates a new [`ArcMultiAction`] which, when dispatched, will call the server function `S`.\n    #[track_caller]\n    pub fn new() -> Self {\n        Self {\n            inner: ArcMultiAction::new(|input: &S| {\n                S::run_on_client(input.clone())\n            }),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<S> Deref for ArcServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    type Target = ArcMultiAction<S, Result<S::Output, S::Error>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl<S> Clone for ArcServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n        }\n    }\n}\n\nimpl<S> Default for ArcServerMultiAction<S>\nwhere\n    S: ServerFn + Clone + Send + Sync + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<S> DefinedAt for ArcServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\n/// A [`MultiAction`] that can be used to call a server function.\npub struct ServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    inner: MultiAction<S, Result<S::Output, S::Error>>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<S> From<ServerMultiAction<S>>\n    for MultiAction<S, Result<S::Output, S::Error>>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn from(value: ServerMultiAction<S>) -> Self {\n        value.inner\n    }\n}\n\nimpl<S> ServerMultiAction<S>\nwhere\n    S: ServerFn + Send + Sync + Clone + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n{\n    /// Creates a new [`MultiAction`] which, when dispatched, will call the server function `S`.\n    pub fn new() -> Self {\n        Self {\n            inner: MultiAction::new(|input: &S| {\n                S::run_on_client(input.clone())\n            }),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<S> Clone for ServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<S> Copy for ServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n}\n\nimpl<S> Deref for ServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n    S::Error: 'static,\n{\n    type Target = MultiAction<S, Result<S::Output, S::Error>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl<S> Default for ServerMultiAction<S>\nwhere\n    S: ServerFn + Clone + Send + Sync + 'static,\n    S::Output: Send + Sync + 'static,\n    S::Error: Send + Sync + 'static,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<S> DefinedAt for ServerMultiAction<S>\nwhere\n    S: ServerFn + 'static,\n    S::Output: 'static,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "leptos_server/src/once_resource.rs",
    "content": "use crate::{\n    initial_value, FromEncodedStr, IntoEncodedString,\n    IS_SUPPRESSING_RESOURCE_LOAD,\n};\n#[cfg(feature = \"rkyv\")]\nuse codee::binary::RkyvCodec;\n#[cfg(feature = \"serde-wasm-bindgen\")]\nuse codee::string::JsonSerdeWasmCodec;\n#[cfg(feature = \"miniserde\")]\nuse codee::string::MiniserdeCodec;\n#[cfg(feature = \"serde-lite\")]\nuse codee::SerdeLite;\nuse codee::{\n    string::{FromToStringCodec, JsonSerdeCodec},\n    Decoder, Encoder,\n};\nuse core::{fmt::Debug, marker::PhantomData};\nuse futures::{Future, FutureExt};\nuse or_poisoned::OrPoisoned;\nuse reactive_graph::{\n    computed::{\n        suspense::SuspenseContext, AsyncDerivedReadyFuture, ScopedFuture,\n    },\n    diagnostics::{SpecialNonReactiveFuture, SpecialNonReactiveZone},\n    graph::{AnySource, ToAnySource},\n    owner::{use_context, ArenaItem, Owner},\n    prelude::*,\n    signal::{\n        guards::{Plain, ReadGuard},\n        ArcTrigger,\n    },\n    unwrap_signal,\n};\nuse std::{\n    future::IntoFuture,\n    mem,\n    panic::Location,\n    pin::Pin,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc, RwLock,\n    },\n    task::{Context, Poll, Waker},\n};\n\n/// A reference-counted resource that only loads once.\n///\n/// Resources allow asynchronously loading data and serializing it from the server to the client,\n/// so that it loads on the server, and is then deserialized on the client. This improves\n/// performance by beginning data loading on the server when the request is made, rather than\n/// beginning it on the client after WASM has been loaded.\n///\n/// You can access the value of the resource either synchronously using `.get()` or asynchronously\n/// using `.await`.\n#[derive(Debug)]\npub struct ArcOnceResource<T, Ser = JsonSerdeCodec> {\n    trigger: ArcTrigger,\n    value: Arc<RwLock<Option<T>>>,\n    wakers: Arc<RwLock<Vec<Waker>>>,\n    suspenses: Arc<RwLock<Vec<SuspenseContext>>>,\n    loading: Arc<AtomicBool>,\n    ser: PhantomData<fn() -> Ser>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<T, Ser> Clone for ArcOnceResource<T, Ser> {\n    fn clone(&self) -> Self {\n        Self {\n            trigger: self.trigger.clone(),\n            value: self.value.clone(),\n            wakers: self.wakers.clone(),\n            suspenses: self.suspenses.clone(),\n            loading: self.loading.clone(),\n            ser: self.ser,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n        }\n    }\n}\n\nimpl<T, Ser> ArcOnceResource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n    Ser: Encoder<T> + Decoder<T>,\n    <Ser as Encoder<T>>::Error: Debug,\n    <Ser as Decoder<T>>::Error: Debug,\n    <<Ser as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <Ser as Encoder<T>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding `Ser`. If `blocking` is `true`, this is a blocking\n    /// resource.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_with_options(\n        fut: impl Future<Output = T> + Send + 'static,\n        #[allow(unused)] // this is used with `feature = \"ssr\"`\n        blocking: bool,\n    ) -> Self {\n        let shared_context = Owner::current_shared_context();\n        let id = shared_context\n            .as_ref()\n            .map(|sc| sc.next_id())\n            .unwrap_or_default();\n\n        let initial = initial_value::<T, Ser>(&id, shared_context.as_ref());\n        let is_ready = initial.is_some();\n        let value = Arc::new(RwLock::new(initial));\n        let wakers = Arc::new(RwLock::new(Vec::<Waker>::new()));\n        let suspenses = Arc::new(RwLock::new(Vec::<SuspenseContext>::new()));\n        let loading = Arc::new(AtomicBool::new(!is_ready));\n        let trigger = ArcTrigger::new();\n\n        let fut = ScopedFuture::new(fut);\n\n        if !is_ready && !IS_SUPPRESSING_RESOURCE_LOAD.load(Ordering::Relaxed) {\n            let value = Arc::clone(&value);\n            let wakers = Arc::clone(&wakers);\n            let loading = Arc::clone(&loading);\n            let trigger = trigger.clone();\n            reactive_graph::spawn(async move {\n                let loaded = fut.await;\n                *value.write().or_poisoned() = Some(loaded);\n                loading.store(false, Ordering::Relaxed);\n                for waker in mem::take(&mut *wakers.write().or_poisoned()) {\n                    waker.wake();\n                }\n                trigger.notify();\n            });\n        }\n\n        let data = Self {\n            trigger,\n            value: value.clone(),\n            loading,\n            wakers,\n            suspenses,\n            ser: PhantomData,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        };\n\n        #[cfg(feature = \"ssr\")]\n        if let Some(shared_context) = shared_context {\n            let value = Arc::clone(&value);\n            let ready_fut = data.ready();\n\n            if blocking {\n                shared_context.defer_stream(Box::pin(data.ready()));\n            }\n\n            if shared_context.get_is_hydrating() {\n                shared_context.write_async(\n                    id,\n                    Box::pin(async move {\n                        ready_fut.await;\n                        let value = value.read().or_poisoned();\n                        let value = value.as_ref().unwrap();\n                        Ser::encode(value).unwrap().into_encoded_string()\n                    }),\n                );\n            }\n        }\n\n        data\n    }\n\n    /// Synchronously, reactively reads the current value of the resource and applies the function\n    /// `f` to its value if it is `Some(_)`.\n    #[track_caller]\n    pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>\n    where\n        T: Send + Sync + 'static,\n    {\n        self.try_with(|n| n.as_ref().map(f))?\n    }\n}\n\nimpl<T, E, Ser> ArcOnceResource<Result<T, E>, Ser>\nwhere\n    Ser: Encoder<Result<T, E>> + Decoder<Result<T, E>>,\n    <Ser as Encoder<Result<T, E>>>::Error: Debug,\n    <Ser as Decoder<Result<T, E>>>::Error: Debug,\n    <<Ser as Decoder<Result<T, E>>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <Ser as Encoder<Result<T, E>>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<Result<T, E>>>::Encoded: FromEncodedStr,\n    T: Send + Sync + 'static,\n    E: Send + Sync + Clone + 'static,\n{\n    /// Applies the given function when a resource that returns `Result<T, E>`\n    /// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`\n    /// calls over the `Option<Result<_, _>>` returned by the resource.\n    ///\n    /// This is useful when used with features like server functions, in conjunction\n    /// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are\n    /// left to handle the `None` and `Err(_)` states.\n    #[track_caller]\n    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {\n        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))\n    }\n}\n\nimpl<T, Ser> ArcOnceResource<T, Ser> {\n    /// Returns a `Future` that is ready when this resource has next finished loading.\n    pub fn ready(&self) -> AsyncDerivedReadyFuture {\n        AsyncDerivedReadyFuture::new(\n            self.to_any_source(),\n            &self.loading,\n            &self.wakers,\n        )\n    }\n}\n\nimpl<T, Ser> DefinedAt for ArcOnceResource<T, Ser> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n    }\n}\n\nimpl<T, Ser> IsDisposed for ArcOnceResource<T, Ser> {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T, Ser> ToAnySource for ArcOnceResource<T, Ser> {\n    fn to_any_source(&self) -> AnySource {\n        self.trigger.to_any_source()\n    }\n}\n\nimpl<T, Ser> Track for ArcOnceResource<T, Ser> {\n    fn track(&self) {\n        self.trigger.track();\n    }\n}\n\nimpl<T, Ser> ReadUntracked for ArcOnceResource<T, Ser>\nwhere\n    T: 'static,\n{\n    type Value = ReadGuard<Option<T>, Plain<Option<T>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        if let Some(suspense_context) = use_context::<SuspenseContext>() {\n            if self.value.read().or_poisoned().is_none() {\n                let handle = suspense_context.task_id();\n                let mut ready =\n                    Box::pin(SpecialNonReactiveFuture::new(self.ready()));\n                match ready.as_mut().now_or_never() {\n                    Some(_) => drop(handle),\n                    None => {\n                        reactive_graph::spawn(async move {\n                            ready.await;\n                            drop(handle);\n                        });\n                    }\n                }\n                self.suspenses.write().or_poisoned().push(suspense_context);\n            }\n        }\n        Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)\n    }\n}\n\nimpl<T, Ser> IntoFuture for ArcOnceResource<T, Ser>\nwhere\n    T: Clone + 'static,\n{\n    type Output = T;\n    type IntoFuture = OnceResourceFuture<T>;\n\n    fn into_future(self) -> Self::IntoFuture {\n        OnceResourceFuture {\n            source: self.to_any_source(),\n            value: Arc::clone(&self.value),\n            loading: Arc::clone(&self.loading),\n            wakers: Arc::clone(&self.wakers),\n            suspenses: Arc::clone(&self.suspenses),\n        }\n    }\n}\n\n/// A [`Future`] that is ready when an\n/// [`ArcAsyncDerived`](reactive_graph::computed::ArcAsyncDerived) is finished loading or reloading,\n/// and contains its value. `.await`ing this clones the value `T`.\npub struct OnceResourceFuture<T> {\n    source: AnySource,\n    value: Arc<RwLock<Option<T>>>,\n    loading: Arc<AtomicBool>,\n    wakers: Arc<RwLock<Vec<Waker>>>,\n    suspenses: Arc<RwLock<Vec<SuspenseContext>>>,\n}\n\nimpl<T> Future for OnceResourceFuture<T>\nwhere\n    T: Clone + 'static,\n{\n    type Output = T;\n\n    #[track_caller]\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let _guard = SpecialNonReactiveZone::enter();\n        let waker = cx.waker();\n        self.source.track();\n\n        if let Some(suspense_context) = use_context::<SuspenseContext>() {\n            self.suspenses.write().or_poisoned().push(suspense_context);\n        }\n\n        if self.loading.load(Ordering::Relaxed) {\n            self.wakers.write().or_poisoned().push(waker.clone());\n            Poll::Pending\n        } else {\n            Poll::Ready(\n                self.value.read().or_poisoned().as_ref().unwrap().clone(),\n            )\n        }\n    }\n}\n\nimpl<T> ArcOnceResource<T, JsonSerdeCodec>\nwhere\n    T: Send + Sync + 'static,\n    JsonSerdeCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeCodec as Encoder<T>>::Error: Debug,\n    <JsonSerdeCodec as Decoder<T>>::Error: Debug,\n    <<JsonSerdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`JsonSerdeCodec`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new(fut: impl Future<Output = T> + Send + 'static) -> Self {\n        ArcOnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`JsonSerdeCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_blocking(fut: impl Future<Output = T> + Send + 'static) -> Self {\n        ArcOnceResource::new_with_options(fut, true)\n    }\n}\n\nimpl<T> ArcOnceResource<T, FromToStringCodec>\nwhere\nT: Send + Sync + 'static,\n    FromToStringCodec: Encoder<T> + Decoder<T>,\n    <FromToStringCodec as Encoder<T>>::Error: Debug, <FromToStringCodec as Decoder<T>>::Error: Debug,\n    <<FromToStringCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`FromToStringCodec`] for encoding/decoding the value.\n    pub fn new_str(\n        fut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        ArcOnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`FromToStringCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    pub fn new_str_blocking(\n        fut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        ArcOnceResource::new_with_options(fut, true)\n    }\n}\n\n#[cfg(feature = \"serde-wasm-bindgen\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"serde-wasm-bindgen\")))]\nimpl<T> ArcOnceResource<T, JsonSerdeWasmCodec>\nwhere\nT: Send + Sync + 'static,\n    JsonSerdeWasmCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeWasmCodec as Encoder<T>>::Error: Debug, <JsonSerdeWasmCodec as Decoder<T>>::Error: Debug,\n    <<JsonSerdeWasmCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`JsonSerdeWasmCodec`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new_serde_wb(\nfut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        ArcOnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`JsonSerdeWasmCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_serde_wb_blocking(\nfut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        ArcOnceResource::new_with_options(fut, true)\n    }\n}\n#[cfg(feature = \"miniserde\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"miniserde\")))]\nimpl<T> ArcOnceResource<T, MiniserdeCodec>\nwhere\n    T: Send + Sync + 'static,\n    MiniserdeCodec: Encoder<T> + Decoder<T>,\n    <MiniserdeCodec as Encoder<T>>::Error: Debug,\n    <MiniserdeCodec as Decoder<T>>::Error: Debug,\n    <<MiniserdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`MiniserdeCodec`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new_miniserde(\n        fut: impl Future<Output = T> + Send + 'static,\n    ) -> Self {\n        ArcOnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`MiniserdeCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_miniserde_blocking(\n        fut: impl Future<Output = T> + Send + 'static,\n    ) -> Self {\n        ArcOnceResource::new_with_options(fut, true)\n    }\n}\n\n#[cfg(feature = \"serde-lite\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"serde-lite\")))]\nimpl<T> ArcOnceResource<T, SerdeLite<JsonSerdeCodec>>\nwhere\nT: Send + Sync + 'static,\n    SerdeLite<JsonSerdeCodec>: Encoder<T> + Decoder<T>,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Error: Debug, <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Error: Debug,\n    <<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,\n    <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`SerdeLite`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new_serde_lite(\nfut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        ArcOnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`SerdeLite`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_serde_lite_blocking(\nfut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        ArcOnceResource::new_with_options(fut, true)\n    }\n}\n\n#[cfg(feature = \"rkyv\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"rkyv\")))]\nimpl<T> ArcOnceResource<T, RkyvCodec>\nwhere\n    T: Send + Sync + 'static,\n    RkyvCodec: Encoder<T> + Decoder<T>,\n    <RkyvCodec as Encoder<T>>::Error: Debug,\n    <RkyvCodec as Decoder<T>>::Error: Debug,\n    <<RkyvCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`RkyvCodec`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new_rkyv(fut: impl Future<Output = T> + Send + 'static) -> Self {\n        ArcOnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`RkyvCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_rkyv_blocking(\n        fut: impl Future<Output = T> + Send + 'static,\n    ) -> Self {\n        ArcOnceResource::new_with_options(fut, true)\n    }\n}\n\n/// A resource that only loads once.\n///\n/// Resources allow asynchronously loading data and serializing it from the server to the client,\n/// so that it loads on the server, and is then deserialized on the client. This improves\n/// performance by beginning data loading on the server when the request is made, rather than\n/// beginning it on the client after WASM has been loaded.\n///\n/// You can access the value of the resource either synchronously using `.get()` or asynchronously\n/// using `.await`.\n#[derive(Debug)]\npub struct OnceResource<T, Ser = JsonSerdeCodec> {\n    inner: ArenaItem<ArcOnceResource<T, Ser>>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<T, Ser> Clone for OnceResource<T, Ser> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, Ser> Copy for OnceResource<T, Ser> {}\n\nimpl<T, Ser> OnceResource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n    Ser: Encoder<T> + Decoder<T>,\n    <Ser as Encoder<T>>::Error: Debug,\n    <Ser as Decoder<T>>::Error: Debug,\n    <<Ser as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <Ser as Encoder<T>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding `Ser`. If `blocking` is `true`, this is a blocking\n    /// resource.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_with_options(\n        fut: impl Future<Output = T> + Send + 'static,\n        blocking: bool,\n    ) -> Self {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let defined_at = Location::caller();\n        Self {\n            inner: ArenaItem::new(ArcOnceResource::new_with_options(\n                fut, blocking,\n            )),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at,\n        }\n    }\n\n    /// Synchronously, reactively reads the current value of the resource and applies the function\n    /// `f` to its value if it is `Some(_)`.\n    pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U> {\n        self.try_with(|n| n.as_ref().map(|n| Some(f(n))))?.flatten()\n    }\n}\n\nimpl<T, E, Ser> OnceResource<Result<T, E>, Ser>\nwhere\n    Ser: Encoder<Result<T, E>> + Decoder<Result<T, E>>,\n    <Ser as Encoder<Result<T, E>>>::Error: Debug,\n    <Ser as Decoder<Result<T, E>>>::Error: Debug,\n    <<Ser as Decoder<Result<T, E>>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <Ser as Encoder<Result<T, E>>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<Result<T, E>>>::Encoded: FromEncodedStr,\n    T: Send + Sync + 'static,\n    E: Send + Sync + Clone + 'static,\n{\n    /// Applies the given function when a resource that returns `Result<T, E>`\n    /// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`\n    /// calls over the `Option<Result<_, _>>` returned by the resource.\n    ///\n    /// This is useful when used with features like server functions, in conjunction\n    /// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are\n    /// left to handle the `None` and `Err(_)` states.\n    #[track_caller]\n    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {\n        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))\n    }\n}\n\nimpl<T, Ser> OnceResource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n    Ser: 'static,\n{\n    /// Returns a `Future` that is ready when this resource has next finished loading.\n    pub fn ready(&self) -> AsyncDerivedReadyFuture {\n        self.inner\n            .try_with_value(|inner| inner.ready())\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T, Ser> DefinedAt for OnceResource<T, Ser> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n    }\n}\n\nimpl<T, Ser> IsDisposed for OnceResource<T, Ser> {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T, Ser> ToAnySource for OnceResource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n    Ser: 'static,\n{\n    fn to_any_source(&self) -> AnySource {\n        self.inner\n            .try_with_value(|inner| inner.to_any_source())\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T, Ser> Track for OnceResource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n    Ser: 'static,\n{\n    fn track(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.track();\n        }\n    }\n}\n\nimpl<T, Ser> ReadUntracked for OnceResource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n    Ser: 'static,\n{\n    type Value = ReadGuard<Option<T>, Plain<Option<T>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.inner\n            .try_with_value(|inner| inner.try_read_untracked())\n            .flatten()\n    }\n}\n\nimpl<T, Ser> IntoFuture for OnceResource<T, Ser>\nwhere\n    T: Clone + Send + Sync + 'static,\n    Ser: 'static,\n{\n    type Output = T;\n    type IntoFuture = OnceResourceFuture<T>;\n\n    fn into_future(self) -> Self::IntoFuture {\n        self.inner\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(self))\n            .into_future()\n    }\n}\n\nimpl<T> OnceResource<T, JsonSerdeCodec>\nwhere\n    T: Send + Sync + 'static,\n    JsonSerdeCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeCodec as Encoder<T>>::Error: Debug,\n    <JsonSerdeCodec as Decoder<T>>::Error: Debug,\n    <<JsonSerdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`JsonSerdeCodec`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new(fut: impl Future<Output = T> + Send + 'static) -> Self {\n        OnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`JsonSerdeCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_blocking(fut: impl Future<Output = T> + Send + 'static) -> Self {\n        OnceResource::new_with_options(fut, true)\n    }\n}\n\nimpl<T> OnceResource<T, FromToStringCodec>\nwhere\nT: Send + Sync + 'static,\n    FromToStringCodec: Encoder<T> + Decoder<T>,\n    <FromToStringCodec as Encoder<T>>::Error: Debug, <FromToStringCodec as Decoder<T>>::Error: Debug,\n    <<FromToStringCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`FromToStringCodec`] for encoding/decoding the value.\n    pub fn new_str(\n        fut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        OnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`FromToStringCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    pub fn new_str_blocking(\n        fut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        OnceResource::new_with_options(fut, true)\n    }\n}\n\n#[cfg(feature = \"serde-wasm-bindgen\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"serde-wasm-bindgen\")))]\nimpl<T> OnceResource<T, JsonSerdeWasmCodec>\nwhere\nT: Send + Sync + 'static,\n    JsonSerdeWasmCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeWasmCodec as Encoder<T>>::Error: Debug, <JsonSerdeWasmCodec as Decoder<T>>::Error: Debug,\n    <<JsonSerdeWasmCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`JsonSerdeWasmCodec`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new_serde_wb(\nfut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        OnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`JsonSerdeWasmCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_serde_wb_blocking(\nfut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        OnceResource::new_with_options(fut, true)\n    }\n}\n#[cfg(feature = \"miniserde\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"miniserde\")))]\nimpl<T> OnceResource<T, MiniserdeCodec>\nwhere\n    T: Send + Sync + 'static,\n    MiniserdeCodec: Encoder<T> + Decoder<T>,\n    <MiniserdeCodec as Encoder<T>>::Error: Debug,\n    <MiniserdeCodec as Decoder<T>>::Error: Debug,\n    <<MiniserdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`MiniserdeCodec`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new_miniserde(\n        fut: impl Future<Output = T> + Send + 'static,\n    ) -> Self {\n        OnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`MiniserdeCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_miniserde_blocking(\n        fut: impl Future<Output = T> + Send + 'static,\n    ) -> Self {\n        OnceResource::new_with_options(fut, true)\n    }\n}\n\n#[cfg(feature = \"serde-lite\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"serde-lite\")))]\nimpl<T> OnceResource<T, SerdeLite<JsonSerdeCodec>>\nwhere\nT: Send + Sync + 'static,\n    SerdeLite<JsonSerdeCodec>: Encoder<T> + Decoder<T>,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Error: Debug, <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Error: Debug,\n    <<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,\n    <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`SerdeLite`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new_serde_lite(\nfut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        OnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`SerdeLite`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_serde_lite_blocking(\nfut: impl Future<Output = T> + Send + 'static\n    ) -> Self\n    {\n        OnceResource::new_with_options(fut, true)\n    }\n}\n\n#[cfg(feature = \"rkyv\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"rkyv\")))]\nimpl<T> OnceResource<T, RkyvCodec>\nwhere\n    T: Send + Sync + 'static,\n    RkyvCodec: Encoder<T> + Decoder<T>,\n    <RkyvCodec as Encoder<T>>::Error: Debug,\n    <RkyvCodec as Decoder<T>>::Error: Debug,\n    <<RkyvCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a resource using [`RkyvCodec`] for encoding/decoding the value.\n    #[track_caller]\n    pub fn new_rkyv(fut: impl Future<Output = T> + Send + 'static) -> Self {\n        OnceResource::new_with_options(fut, false)\n    }\n\n    /// Creates a blocking resource using [`RkyvCodec`] for encoding/decoding the value.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_rkyv_blocking(\n        fut: impl Future<Output = T> + Send + 'static,\n    ) -> Self {\n        OnceResource::new_with_options(fut, true)\n    }\n}\n"
  },
  {
    "path": "leptos_server/src/resource.rs",
    "content": "use crate::{FromEncodedStr, IntoEncodedString};\n#[cfg(feature = \"rkyv\")]\nuse codee::binary::RkyvCodec;\n#[cfg(feature = \"serde-wasm-bindgen\")]\nuse codee::string::JsonSerdeWasmCodec;\n#[cfg(feature = \"miniserde\")]\nuse codee::string::MiniserdeCodec;\n#[cfg(feature = \"serde-lite\")]\nuse codee::SerdeLite;\nuse codee::{\n    string::{FromToStringCodec, JsonSerdeCodec},\n    Decoder, Encoder,\n};\nuse core::{fmt::Debug, marker::PhantomData};\nuse futures::Future;\nuse hydration_context::{SerializedDataId, SharedContext};\nuse reactive_graph::{\n    computed::{\n        ArcAsyncDerived, ArcMemo, AsyncDerived, AsyncDerivedFuture,\n        AsyncDerivedRefFuture,\n    },\n    graph::{Source, ToAnySubscriber},\n    owner::Owner,\n    prelude::*,\n    signal::{ArcRwSignal, RwSignal},\n};\nuse std::{\n    future::{pending, IntoFuture},\n    ops::{Deref, DerefMut},\n    panic::Location,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc,\n    },\n};\n\npub(crate) static IS_SUPPRESSING_RESOURCE_LOAD: AtomicBool =\n    AtomicBool::new(false);\n\n/// Used to prevent resources from actually loading, in environments (like server route generation)\n/// where they are not needed.\npub struct SuppressResourceLoad;\n\nimpl SuppressResourceLoad {\n    /// Prevents resources from loading until this is dropped.\n    pub fn new() -> Self {\n        IS_SUPPRESSING_RESOURCE_LOAD.store(true, Ordering::Relaxed);\n        Self\n    }\n}\n\nimpl Default for SuppressResourceLoad {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Drop for SuppressResourceLoad {\n    fn drop(&mut self) {\n        IS_SUPPRESSING_RESOURCE_LOAD.store(false, Ordering::Relaxed);\n    }\n}\n\n/// A reference-counted asynchronous resource.\n///\n/// Resources allow asynchronously loading data and serializing it from the server to the client,\n/// so that it loads on the server, and is then deserialized on the client. This improves\n/// performance by beginning data loading on the server when the request is made, rather than\n/// beginning it on the client after WASM has been loaded.\n///\n/// You can access the value of the resource either synchronously using `.get()` or asynchronously\n/// using `.await`.\npub struct ArcResource<T, Ser = JsonSerdeCodec> {\n    ser: PhantomData<Ser>,\n    refetch: ArcRwSignal<usize>,\n    data: ArcAsyncDerived<T>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<T, Ser> Debug for ArcResource<T, Ser> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut d = f.debug_struct(\"ArcResource\");\n        d.field(\"ser\", &self.ser).field(\"data\", &self.data);\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        d.field(\"defined_at\", self.defined_at);\n        d.finish_non_exhaustive()\n    }\n}\n\nimpl<T, Ser> From<ArcResource<T, Ser>> for Resource<T, Ser>\nwhere\n    T: Send + Sync,\n{\n    #[track_caller]\n    fn from(arc_resource: ArcResource<T, Ser>) -> Self {\n        Resource {\n            ser: PhantomData,\n            data: arc_resource.data.into(),\n            refetch: arc_resource.refetch.into(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<T, Ser> From<Resource<T, Ser>> for ArcResource<T, Ser>\nwhere\n    T: Send + Sync,\n{\n    #[track_caller]\n    fn from(resource: Resource<T, Ser>) -> Self {\n        ArcResource {\n            ser: PhantomData,\n            data: resource.data.into(),\n            refetch: resource.refetch.into(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<T, Ser> DefinedAt for ArcResource<T, Ser> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T, Ser> Clone for ArcResource<T, Ser> {\n    fn clone(&self) -> Self {\n        Self {\n            ser: self.ser,\n            refetch: self.refetch.clone(),\n            data: self.data.clone(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n        }\n    }\n}\n\nimpl<T, Ser> Deref for ArcResource<T, Ser> {\n    type Target = ArcAsyncDerived<T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.data\n    }\n}\n\nimpl<T, Ser> Track for ArcResource<T, Ser>\nwhere\n    T: 'static,\n{\n    fn track(&self) {\n        self.data.track();\n    }\n}\n\nimpl<T, Ser> Notify for ArcResource<T, Ser>\nwhere\n    T: 'static,\n{\n    fn notify(&self) {\n        self.data.notify()\n    }\n}\n\nimpl<T, Ser> Write for ArcResource<T, Ser>\nwhere\n    T: 'static,\n{\n    type Value = Option<T>;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.data.try_write()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.data.try_write_untracked()\n    }\n}\n\n#[cfg(debug_assertions)]\nthread_local! {\n    static RESOURCE_SOURCE_SIGNAL_ACTIVE: AtomicBool = const { AtomicBool::new(false) };\n}\n\n#[cfg(debug_assertions)]\n/// Returns whether the current thread is currently running a resource source signal.\npub fn in_resource_source_signal() -> bool {\n    RESOURCE_SOURCE_SIGNAL_ACTIVE\n        .with(|scope| scope.load(std::sync::atomic::Ordering::Relaxed))\n}\n\n/// Set a static to true whilst running the given function.\n/// [`is_in_effect_scope`] will return true whilst the function is running.\nfn run_in_resource_source_signal<T>(fun: impl FnOnce() -> T) -> T {\n    #[cfg(debug_assertions)]\n    {\n        // For the theoretical nested case, set back to initial value rather than false:\n        let initial = RESOURCE_SOURCE_SIGNAL_ACTIVE.with(|scope| {\n            scope.swap(true, std::sync::atomic::Ordering::Relaxed)\n        });\n        let result = fun();\n        RESOURCE_SOURCE_SIGNAL_ACTIVE.with(|scope| {\n            scope.store(initial, std::sync::atomic::Ordering::Relaxed)\n        });\n        result\n    }\n    #[cfg(not(debug_assertions))]\n    {\n        fun()\n    }\n}\n\nimpl<T, Ser> ReadUntracked for ArcResource<T, Ser>\nwhere\n    T: 'static,\n{\n    type Value = <ArcAsyncDerived<T> as ReadUntracked>::Value;\n\n    #[track_caller]\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        #[cfg(all(feature = \"hydration\", debug_assertions))]\n        {\n            use reactive_graph::{\n                computed::suspense::SuspenseContext, effect::in_effect_scope,\n                owner::use_context,\n            };\n            if !in_effect_scope()\n                && !in_resource_source_signal()\n                && use_context::<SuspenseContext>().is_none()\n            {\n                let location = std::panic::Location::caller();\n                reactive_graph::log_warning(format_args!(\n                    \"At {location}, you are reading a resource in `hydrate` \\\n                     mode outside a <Suspense/> or <Transition/> or effect. \\\n                     This can cause hydration mismatch errors and loses out \\\n                     on a significant performance optimization. To fix this \\\n                     issue, you can either: \\n1. Wrap the place where you \\\n                     read the resource in a <Suspense/> or <Transition/> \\\n                     component, or \\n2. Switch to using \\\n                     ArcLocalResource::new(), which will wait to load the \\\n                     resource until the app is hydrated on the client side. \\\n                     (This will have worse performance in most cases.)\",\n                ));\n            }\n        }\n        self.data.try_read_untracked()\n    }\n}\n\nimpl<T, Ser> ArcResource<T, Ser>\nwhere\n    Ser: Encoder<T> + Decoder<T>,\n    <Ser as Encoder<T>>::Error: Debug,\n    <Ser as Decoder<T>>::Error: Debug,\n    <<Ser as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <Ser as Encoder<T>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding `Ser`.\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// If `blocking` is `true`, this is a blocking resource.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_with_options<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n        #[allow(unused)] // this is used with `feature = \"ssr\"`\n        blocking: bool,\n    ) -> ArcResource<T, Ser>\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        let shared_context = Owner::current_shared_context();\n        let id = shared_context\n            .as_ref()\n            .map(|sc| sc.next_id())\n            .unwrap_or_default();\n\n        let initial = initial_value::<T, Ser>(&id, shared_context.as_ref());\n        let is_ready = initial.is_some();\n\n        let refetch = ArcRwSignal::new(0);\n        let source = ArcMemo::new({\n            let refetch = refetch.clone();\n            move |_| (refetch.get(), run_in_resource_source_signal(&source))\n        });\n        let fun = {\n            let source = source.clone();\n            move || {\n                let (_, source) = source.get();\n                let fut = fetcher(source);\n                async move {\n                    if IS_SUPPRESSING_RESOURCE_LOAD.load(Ordering::Relaxed) {\n                        pending().await\n                    } else {\n                        fut.await\n                    }\n                }\n            }\n        };\n\n        let data = ArcAsyncDerived::new_with_manual_dependencies(\n            initial, fun, &source,\n        );\n        if is_ready {\n            source.with_untracked(|_| ());\n            source.add_subscriber(data.to_any_subscriber());\n        }\n\n        #[cfg(feature = \"ssr\")]\n        if let Some(shared_context) = shared_context {\n            let value = data.clone();\n            let ready_fut = data.ready();\n\n            if blocking {\n                shared_context.defer_stream(Box::pin(data.ready()));\n            }\n\n            if shared_context.get_is_hydrating() {\n                shared_context.write_async(\n                    id,\n                    Box::pin(async move {\n                        ready_fut.await;\n                        value.with_untracked(|data| match &data {\n                            // TODO handle serialization errors\n                            Some(val) => {\n                                Ser::encode(val).unwrap().into_encoded_string()\n                            }\n                            _ => unreachable!(),\n                        })\n                    }),\n                );\n            }\n        }\n\n        ArcResource {\n            ser: PhantomData,\n            data,\n            refetch,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Synchronously, reactively reads the current value of the resource and applies the function\n    /// `f` to its value if it is `Some(_)`.\n    #[track_caller]\n    pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>\n    where\n        T: Send + Sync + 'static,\n    {\n        self.data.try_with(|n| n.as_ref().map(f))?\n    }\n\n    /// Re-runs the async function with the current source data.\n    pub fn refetch(&self) {\n        *self.refetch.write() += 1;\n    }\n}\n\n#[inline(always)]\n#[allow(unused)]\npub(crate) fn initial_value<T, Ser>(\n    id: &SerializedDataId,\n    shared_context: Option<&Arc<dyn SharedContext + Send + Sync>>,\n) -> Option<T>\nwhere\n    Ser: Encoder<T> + Decoder<T>,\n    <Ser as Encoder<T>>::Error: Debug,\n    <Ser as Decoder<T>>::Error: Debug,\n    <<Ser as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <Ser as Encoder<T>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    #[cfg(feature = \"hydration\")]\n    {\n        use std::borrow::Borrow;\n\n        let shared_context = Owner::current_shared_context();\n        if let Some(shared_context) = shared_context {\n            let value = shared_context.read_data(id);\n            if let Some(value) = value {\n                let encoded =\n                    match <Ser as Decoder<T>>::Encoded::from_encoded_str(&value)\n                    {\n                        Ok(value) => value,\n                        Err(e) => {\n                            #[cfg(feature = \"tracing\")]\n                            tracing::error!(\"couldn't deserialize: {e:?}\");\n                            return None;\n                        }\n                    };\n                let encoded = encoded.borrow();\n                match Ser::decode(encoded) {\n                    Ok(value) => return Some(value),\n                    #[allow(unused)]\n                    Err(e) => {\n                        #[cfg(feature = \"tracing\")]\n                        tracing::error!(\"couldn't deserialize: {e:?}\");\n                    }\n                }\n            }\n        }\n    }\n    None\n}\n\nimpl<T, E, Ser> ArcResource<Result<T, E>, Ser>\nwhere\n    Ser: Encoder<Result<T, E>> + Decoder<Result<T, E>>,\n    <Ser as Encoder<Result<T, E>>>::Error: Debug,\n    <Ser as Decoder<Result<T, E>>>::Error: Debug,\n    <<Ser as Decoder<Result<T, E>>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <Ser as Encoder<Result<T, E>>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<Result<T, E>>>::Encoded: FromEncodedStr,\n    T: Send + Sync + 'static,\n    E: Send + Sync + Clone + 'static,\n{\n    /// Applies the given function when a resource that returns `Result<T, E>`\n    /// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`\n    /// calls over the `Option<Result<_, _>>` returned by the resource.\n    ///\n    /// This is useful when used with features like server functions, in conjunction\n    /// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are\n    /// left to handle the `None` and `Err(_)` states.\n    #[track_caller]\n    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {\n        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))\n    }\n}\n\nimpl<T> ArcResource<T, JsonSerdeCodec>\nwhere\n    JsonSerdeCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeCodec as Encoder<T>>::Error: Debug,\n    <JsonSerdeCodec as Decoder<T>>::Error: Debug,\n    <<JsonSerdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding [`JsonSerdeCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    #[track_caller]\n    pub fn new<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`JsonSerdeCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, true)\n    }\n}\n\nimpl<T> ArcResource<T, FromToStringCodec>\nwhere\n    FromToStringCodec: Encoder<T> + Decoder<T>,\n    <FromToStringCodec as Encoder<T>>::Error: Debug, <FromToStringCodec as Decoder<T>>::Error: Debug,\n    <<FromToStringCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding [`FromToStringCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    pub fn new_str<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`FromToStringCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    pub fn new_str_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, true)\n    }\n}\n\n#[cfg(feature = \"serde-wasm-bindgen\")]\nimpl<T> ArcResource<T, JsonSerdeWasmCodec>\nwhere\n    JsonSerdeWasmCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeWasmCodec as Encoder<T>>::Error: Debug, <JsonSerdeWasmCodec as Decoder<T>>::Error: Debug,\n    <<JsonSerdeWasmCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding [`JsonSerdeWasmCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    #[track_caller]\n    pub fn new_serde_wb<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`JsonSerdeWasmCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_serde_wb_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, true)\n    }\n}\n#[cfg(feature = \"miniserde\")]\nimpl<T> ArcResource<T, MiniserdeCodec>\nwhere\n    MiniserdeCodec: Encoder<T> + Decoder<T>,\n    <MiniserdeCodec as Encoder<T>>::Error: Debug,\n    <MiniserdeCodec as Decoder<T>>::Error: Debug,\n    <<MiniserdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding [`MiniserdeCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    #[track_caller]\n    pub fn new_miniserde<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`MiniserdeCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_miniserde_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, true)\n    }\n}\n\n#[cfg(feature = \"serde-lite\")]\nimpl<T> ArcResource<T, SerdeLite<JsonSerdeCodec>>\nwhere\n    SerdeLite<JsonSerdeCodec>: Encoder<T> + Decoder<T>,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Error: Debug, <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Error: Debug,\n    <<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,\n    <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding [`SerdeLite`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    #[track_caller]\n    pub fn new_serde_lite<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`SerdeLite`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_serde_lite_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, true)\n    }\n}\n\n#[cfg(feature = \"rkyv\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"rkyv\")))]\nimpl<T> ArcResource<T, RkyvCodec>\nwhere\n    RkyvCodec: Encoder<T> + Decoder<T>,\n    <RkyvCodec as Encoder<T>>::Error: Debug,\n    <RkyvCodec as Decoder<T>>::Error: Debug,\n    <<RkyvCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,\n{\n    /// Creates a new resource with the encoding [`RkyvCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    #[track_caller]\n    pub fn new_rkyv<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`RkyvCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_rkyv_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        ArcResource::new_with_options(source, fetcher, true)\n    }\n}\n\nimpl<T, Ser> IntoFuture for ArcResource<T, Ser>\nwhere\n    T: Clone + 'static,\n{\n    type Output = T;\n    type IntoFuture = AsyncDerivedFuture<T>;\n\n    fn into_future(self) -> Self::IntoFuture {\n        self.data.into_future()\n    }\n}\n\nimpl<T, Ser> ArcResource<T, Ser>\nwhere\n    T: 'static,\n{\n    /// Returns a new [`Future`] that is ready when the resource has loaded, and accesses its inner\n    /// value by reference.\n    pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {\n        self.data.by_ref()\n    }\n}\n\n/// An asynchronous resource.\n///\n/// Resources allow asynchronously loading data and serializing it from the server to the client,\n/// so that it loads on the server, and is then deserialized on the client. This improves\n/// performance by beginning data loading on the server when the request is made, rather than\n/// beginning it on the client after WASM has been loaded.\n///\n/// You can access the value of the resource either synchronously using `.get()` or asynchronously\n/// using `.await`.\npub struct Resource<T, Ser = JsonSerdeCodec>\nwhere\n    T: Send + Sync + 'static,\n{\n    ser: PhantomData<Ser>,\n    data: AsyncDerived<T>,\n    refetch: RwSignal<usize>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<T, Ser> Debug for Resource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut d = f.debug_struct(\"ArcResource\");\n        d.field(\"ser\", &self.ser).field(\"data\", &self.data);\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        d.field(\"defined_at\", self.defined_at);\n        d.finish_non_exhaustive()\n    }\n}\n\nimpl<T, Ser> DefinedAt for Resource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T: Send + Sync + 'static, Ser> Copy for Resource<T, Ser> {}\n\nimpl<T: Send + Sync + 'static, Ser> Clone for Resource<T, Ser> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, Ser> Deref for Resource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n{\n    type Target = AsyncDerived<T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.data\n    }\n}\n\nimpl<T, Ser> Track for Resource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n{\n    fn track(&self) {\n        self.data.track();\n    }\n}\n\nimpl<T, Ser> Notify for Resource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n{\n    fn notify(&self) {\n        self.data.notify()\n    }\n}\n\nimpl<T, Ser> Write for Resource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n{\n    type Value = Option<T>;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.data.try_write()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.data.try_write_untracked()\n    }\n}\n\nimpl<T, Ser> ReadUntracked for Resource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n{\n    type Value = <AsyncDerived<T> as ReadUntracked>::Value;\n\n    #[track_caller]\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        #[cfg(all(feature = \"hydration\", debug_assertions))]\n        {\n            use reactive_graph::{\n                computed::suspense::SuspenseContext, effect::in_effect_scope,\n                owner::use_context,\n            };\n            if !in_effect_scope()\n                && !in_resource_source_signal()\n                && use_context::<SuspenseContext>().is_none()\n            {\n                let location = std::panic::Location::caller();\n                reactive_graph::log_warning(format_args!(\n                    \"At {location}, you are reading a resource in `hydrate` \\\n                     mode outside a <Suspense/> or <Transition/> or effect. \\\n                     This can cause hydration mismatch errors and loses out \\\n                     on a significant performance optimization. To fix this \\\n                     issue, you can either: \\n1. Wrap the place where you \\\n                     read the resource in a <Suspense/> or <Transition/> \\\n                     component, or \\n2. Switch to using LocalResource::new(), \\\n                     which will wait to load the resource until the app is \\\n                     hydrated on the client side. (This will have worse \\\n                     performance in most cases.)\",\n                ));\n            }\n        }\n        self.data.try_read_untracked()\n    }\n}\n\nimpl<T> Resource<T, FromToStringCodec>\nwhere\n    FromToStringCodec: Encoder<T> + Decoder<T>,\n    <FromToStringCodec as Encoder<T>>::Error: Debug, <FromToStringCodec as Decoder<T>>::Error: Debug,\n    <<FromToStringCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    T: Send + Sync,\n{\n    /// Creates a new resource with the encoding [`FromToStringCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    #[track_caller]\n    pub fn new_str<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`FromToStringCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_str_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, true)\n    }\n}\n\nimpl<T> Resource<T, JsonSerdeCodec>\nwhere\n    JsonSerdeCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeCodec as Encoder<T>>::Error: Debug,\n    <JsonSerdeCodec as Decoder<T>>::Error: Debug,\n    <<JsonSerdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    T: Send + Sync,\n{\n    /// Creates a new resource with the encoding [`JsonSerdeCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    #[track_caller]\n    pub fn new<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`JsonSerdeCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, true)\n    }\n}\n\n#[cfg(feature = \"serde-wasm-bindgen\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"serde-wasm-bindgen\")))]\nimpl<T> Resource<T, JsonSerdeWasmCodec>\nwhere\n    JsonSerdeWasmCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeWasmCodec as Encoder<T>>::Error: Debug, <JsonSerdeWasmCodec as Decoder<T>>::Error: Debug,\n    <<JsonSerdeWasmCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    T: Send + Sync,\n{\n    /// Creates a new resource with the encoding [`JsonSerdeWasmCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    pub fn new_serde_wb<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`JsonSerdeWasmCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    pub fn new_serde_wb_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, true)\n    }\n}\n\n#[cfg(feature = \"miniserde\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"miniserde\")))]\nimpl<T> Resource<T, MiniserdeCodec>\nwhere\n    MiniserdeCodec: Encoder<T> + Decoder<T>,\n    <MiniserdeCodec as Encoder<T>>::Error: Debug,\n    <MiniserdeCodec as Decoder<T>>::Error: Debug,\n    <<MiniserdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    T: Send + Sync,\n{\n    /// Creates a new resource with the encoding [`MiniserdeCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    pub fn new_miniserde<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`MiniserdeCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    pub fn new_miniserde_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, true)\n    }\n}\n\n#[cfg(feature = \"serde-lite\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"serde-lite\")))]\nimpl<T> Resource<T, SerdeLite<JsonSerdeCodec>>\nwhere\n    SerdeLite<JsonSerdeCodec>: Encoder<T> + Decoder<T>,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Error: Debug, <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Error: Debug,\n    <<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,\n    <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,\n    T: Send + Sync,\n{\n    /// Creates a new resource with the encoding [`SerdeLite`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    pub fn new_serde_lite<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`SerdeLite`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    pub fn new_serde_lite_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, true)\n    }\n}\n\n#[cfg(feature = \"rkyv\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"rkyv\")))]\nimpl<T> Resource<T, RkyvCodec>\nwhere\n    RkyvCodec: Encoder<T> + Decoder<T>,\n    <RkyvCodec as Encoder<T>>::Error: Debug,\n    <RkyvCodec as Decoder<T>>::Error: Debug,\n    <<RkyvCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    T: Send + Sync,\n{\n    /// Creates a new resource with the encoding [`RkyvCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    pub fn new_rkyv<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, false)\n    }\n\n    /// Creates a new blocking resource with the encoding [`RkyvCodec`].\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    pub fn new_rkyv_blocking<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        S: PartialEq + Clone + Send + Sync + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Resource::new_with_options(source, fetcher, true)\n    }\n}\n\nimpl<T, Ser> Resource<T, Ser>\nwhere\n    Ser: Encoder<T> + Decoder<T>,\n    <Ser as Encoder<T>>::Error: Debug,\n    <Ser as Decoder<T>>::Error: Debug,\n    <<Ser as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,\n    <Ser as Encoder<T>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<T>>::Encoded: FromEncodedStr,\n    T: Send + Sync,\n{\n    /// Creates a new resource with the encoding `Ser`.\n    ///\n    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks\n    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to\n    /// generate a new [`Future`] to load data.\n    ///\n    /// On creation, if you are on the server, this will run the `fetcher` once to generate\n    /// a `Future` whose value will be serialized from the server to the client. If you are on\n    /// the client, the initial value will be deserialized without re-running that async task.\n    ///\n    /// If `blocking` is `true`, this is a blocking resource.\n    ///\n    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.\n    /// This is useful if you need their data to set HTML document metadata or information that\n    /// needs to appear in HTTP headers.\n    #[track_caller]\n    pub fn new_with_options<S, Fut>(\n        source: impl Fn() -> S + Send + Sync + 'static,\n        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,\n        blocking: bool,\n    ) -> Resource<T, Ser>\n    where\n        S: Send + Sync + Clone + PartialEq + 'static,\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        let ArcResource { data, refetch, .. }: ArcResource<T, Ser> =\n            ArcResource::new_with_options(source, fetcher, blocking);\n        Resource {\n            ser: PhantomData,\n            data: data.into(),\n            refetch: refetch.into(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Synchronously, reactively reads the current value of the resource and applies the function\n    /// `f` to its value if it is `Some(_)`.\n    pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U> {\n        self.data\n            .try_with(|n| n.as_ref().map(|n| Some(f(n))))?\n            .flatten()\n    }\n\n    /// Re-runs the async function with the current source data.\n    pub fn refetch(&self) {\n        self.refetch.try_update(|n| *n += 1);\n    }\n}\n\nimpl<T, E, Ser> Resource<Result<T, E>, Ser>\nwhere\n    Ser: Encoder<Result<T, E>> + Decoder<Result<T, E>>,\n    <Ser as Encoder<Result<T, E>>>::Error: Debug,\n    <Ser as Decoder<Result<T, E>>>::Error: Debug,\n    <<Ser as Decoder<Result<T, E>>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n    <Ser as Encoder<Result<T, E>>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<Result<T, E>>>::Encoded: FromEncodedStr,\n    T: Send + Sync,\n    E: Send + Sync + Clone,\n{\n    /// Applies the given function when a resource that returns `Result<T, E>`\n    /// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`\n    /// calls over the `Option<Result<_, _>>` returned by the resource.\n    ///\n    /// This is useful when used with features like server functions, in conjunction\n    /// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are\n    /// left to handle the `None` and `Err(_)` states.\n    #[track_caller]\n    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {\n        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))\n    }\n}\n\nimpl<T, Ser> IntoFuture for Resource<T, Ser>\nwhere\n    T: Clone + Send + Sync + 'static,\n{\n    type Output = T;\n    type IntoFuture = AsyncDerivedFuture<T>;\n\n    #[track_caller]\n    fn into_future(self) -> Self::IntoFuture {\n        self.data.into_future()\n    }\n}\n\nimpl<T, Ser> Resource<T, Ser>\nwhere\n    T: Send + Sync + 'static,\n{\n    /// Returns a new [`Future`] that is ready when the resource has loaded, and accesses its inner\n    /// value by reference.\n    pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {\n        self.data.by_ref()\n    }\n}\n"
  },
  {
    "path": "leptos_server/src/serializers.rs",
    "content": "use core::str::FromStr;\nuse serde::{de::DeserializeOwned, Serialize};\n\npub trait SerializableData<Ser: Serializer>: Sized {\n    type SerErr;\n    type DeErr;\n\n    fn ser(&self) -> Result<String, Self::SerErr>;\n\n    fn de(data: &str) -> Result<Self, Self::DeErr>;\n}\n\npub trait Serializer {}\n\n/// A [`Serializer`] that serializes using [`ToString`] and deserializes\n/// using [`FromStr`](core::str::FromStr).\npub struct Str;\n\nimpl Serializer for Str {}\n\nimpl<T> SerializableData<Str> for T\nwhere\n    T: ToString + FromStr,\n{\n    type SerErr = ();\n    type DeErr = <T as FromStr>::Err;\n\n    fn ser(&self) -> Result<String, Self::SerErr> {\n        Ok(self.to_string())\n    }\n\n    fn de(data: &str) -> Result<Self, Self::DeErr> {\n        T::from_str(data)\n    }\n}\n\n/// A [`Serializer`] that serializes using [`serde_json`].\npub struct SerdeJson;\n\nimpl Serializer for SerdeJson {}\n\nimpl<T> SerializableData<SerdeJson> for T\nwhere\n    T: DeserializeOwned + Serialize,\n{\n    type SerErr = serde_json::Error;\n    type DeErr = serde_json::Error;\n\n    fn ser(&self) -> Result<String, Self::SerErr> {\n        serde_json::to_string(&self)\n    }\n\n    fn de(data: &str) -> Result<Self, Self::DeErr> {\n        serde_json::from_str(data)\n    }\n}\n\n#[cfg(feature = \"miniserde\")]\nmod miniserde {\n    use super::{SerializableData, Serializer};\n    use miniserde::{json, Deserialize, Serialize};\n\n    /// A [`Serializer`] that serializes and deserializes using [`miniserde`].\n    pub struct Miniserde;\n\n    impl Serializer for Miniserde {}\n\n    impl<T> SerializableData<Miniserde> for T\n    where\n        T: Deserialize + Serialize,\n    {\n        type SerErr = ();\n        type DeErr = miniserde::Error;\n\n        fn ser(&self) -> Result<String, Self::SerErr> {\n            Ok(json::to_string(&self))\n        }\n\n        fn de(data: &str) -> Result<Self, Self::DeErr> {\n            json::from_str(data)\n        }\n    }\n}\n#[cfg(feature = \"miniserde\")]\npub use miniserde::*;\n\n#[cfg(feature = \"serde-lite\")]\nmod serde_lite {\n    use super::{SerializableData, Serializer};\n    use serde_lite::{Deserialize, Serialize};\n    use thiserror::Error;\n\n    #[derive(Error, Debug)]\n    pub enum SerdeLiteError {\n        #[error(\"serde_lite error {0:?}\")]\n        SerdeLite(serde_lite::Error),\n        #[error(\"serde_json error {0:?}\")]\n        SerdeJson(serde_json::Error),\n    }\n\n    impl From<serde_lite::Error> for SerdeLiteError {\n        fn from(value: serde_lite::Error) -> Self {\n            SerdeLiteError::SerdeLite(value)\n        }\n    }\n\n    impl From<serde_json::Error> for SerdeLiteError {\n        fn from(value: serde_json::Error) -> Self {\n            SerdeLiteError::SerdeJson(value)\n        }\n    }\n\n    /// A [`Serializer`] that serializes and deserializes using [`serde_lite`].\n    pub struct SerdeLite;\n\n    impl Serializer for SerdeLite {}\n\n    impl<T> SerializableData<SerdeLite> for T\n    where\n        T: Deserialize + Serialize,\n    {\n        type SerErr = SerdeLiteError;\n        type DeErr = SerdeLiteError;\n\n        fn ser(&self) -> Result<String, Self::SerErr> {\n            let intermediate = self.serialize()?;\n            Ok(serde_json::to_string(&intermediate)?)\n        }\n\n        fn de(data: &str) -> Result<Self, Self::DeErr> {\n            let intermediate = serde_json::from_str(data)?;\n            Ok(Self::deserialize(&intermediate)?)\n        }\n    }\n}\n#[cfg(feature = \"serde-lite\")]\npub use serde_lite::*;\n\n#[cfg(feature = \"rkyv\")]\nmod rkyv {\n    use super::{SerializableData, Serializer};\n    use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};\n    use rkyv::{\n        de::deserializers::SharedDeserializeMap,\n        ser::serializers::AllocSerializer,\n        validation::validators::DefaultValidator, Archive, CheckBytes,\n        Deserialize, Serialize,\n    };\n    use std::{error::Error, sync::Arc};\n    use thiserror::Error;\n\n    /// A [`Serializer`] that serializes and deserializes using [`rkyv`].\n    pub struct Rkyv;\n\n    impl Serializer for Rkyv {}\n\n    #[derive(Error, Debug)]\n    pub enum RkyvError {\n        #[error(\"rkyv error {0:?}\")]\n        Rkyv(Arc<dyn Error>),\n        #[error(\"base64 error {0:?}\")]\n        Base64Decode(base64::DecodeError),\n    }\n\n    impl From<Arc<dyn Error>> for RkyvError {\n        fn from(value: Arc<dyn Error>) -> Self {\n            RkyvError::Rkyv(value)\n        }\n    }\n\n    impl From<base64::DecodeError> for RkyvError {\n        fn from(value: base64::DecodeError) -> Self {\n            RkyvError::Base64Decode(value)\n        }\n    }\n\n    impl<T> SerializableData<Rkyv> for T\n    where\n        T: Serialize<AllocSerializer<1024>>,\n        T: Archive,\n        T::Archived: for<'b> CheckBytes<DefaultValidator<'b>>\n            + Deserialize<T, SharedDeserializeMap>,\n    {\n        type SerErr = RkyvError;\n        type DeErr = RkyvError;\n\n        fn ser(&self) -> Result<String, Self::SerErr> {\n            let bytes = rkyv::to_bytes::<T, 1024>(self)\n                .map_err(|e| Arc::new(e) as Arc<dyn Error>)?;\n            Ok(STANDARD_NO_PAD.encode(bytes))\n        }\n\n        fn de(data: &str) -> Result<Self, Self::DeErr> {\n            let bytes = STANDARD_NO_PAD.decode(data.as_bytes())?;\n            Ok(rkyv::from_bytes::<T>(&bytes)\n                .map_err(|e| Arc::new(e) as Arc<dyn Error>)?)\n        }\n    }\n}\n\n#[cfg(feature = \"rkyv\")]\npub use rkyv::*;\n\n#[cfg(feature = \"serde-wasm-bindgen\")]\nmod serde_wasm_bindgen {\n    use super::{SerializableData, Serializer};\n    use serde::{de::DeserializeOwned, Serialize};\n\n    /// A [`Serializer`] that serializes using [`serde_json`] and deserializes using\n    /// [`serde-wasm-bindgen`].\n    pub struct SerdeWasmBindgen;\n\n    impl Serializer for SerdeWasmBindgen {}\n\n    impl<T> SerializableData<SerdeWasmBindgen> for T\n    where\n        T: DeserializeOwned + Serialize,\n    {\n        type SerErr = serde_json::Error;\n        type DeErr = wasm_bindgen::JsValue;\n\n        fn ser(&self) -> Result<String, Self::SerErr> {\n            serde_json::to_string(&self)\n        }\n\n        fn de(data: &str) -> Result<Self, Self::DeErr> {\n            let json = js_sys::JSON::parse(data)?;\n            serde_wasm_bindgen::from_value(json).map_err(Into::into)\n        }\n    }\n}\n\n#[cfg(feature = \"serde-wasm-bindgen\")]\npub use serde_wasm_bindgen::*;\n"
  },
  {
    "path": "leptos_server/src/shared.rs",
    "content": "use crate::{FromEncodedStr, IntoEncodedString};\n#[cfg(feature = \"rkyv\")]\nuse codee::binary::RkyvCodec;\n#[cfg(feature = \"serde-wasm-bindgen\")]\nuse codee::string::JsonSerdeWasmCodec;\n#[cfg(feature = \"miniserde\")]\nuse codee::string::MiniserdeCodec;\n#[cfg(feature = \"serde-lite\")]\nuse codee::SerdeLite;\nuse codee::{\n    string::{FromToStringCodec, JsonSerdeCodec},\n    Decoder, Encoder,\n};\nuse std::{\n    fmt::{Debug, Display},\n    hash::Hash,\n    marker::PhantomData,\n    ops::{Deref, DerefMut},\n};\n\n/// A smart pointer that allows you to share identical, synchronously-loaded data between the\n/// server and the client.\n///\n/// If this constructed on the server, it serializes its value into the shared context. If it is\n/// constructed on the client during hydration, it reads its value from the shared context. If\n/// it it constructed on the client at any other time, it simply runs on the client.\n#[derive(Debug)]\npub struct SharedValue<T, Ser = JsonSerdeCodec> {\n    value: T,\n    ser: PhantomData<Ser>,\n}\n\nimpl<T, Ser> SharedValue<T, Ser> {\n    /// Returns the inner value.\n    pub fn into_inner(self) -> T {\n        self.value\n    }\n}\n\nimpl<T> SharedValue<T, JsonSerdeCodec>\nwhere\n    JsonSerdeCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeCodec as Encoder<T>>::Error: Debug,\n    <JsonSerdeCodec as Decoder<T>>::Error: Debug,\n    <JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    <<JsonSerdeCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n{\n    /// Wraps the initial value.\n    ///\n    /// If this is on the server, the function will be invoked and the value serialized. When it runs\n    /// on the client, it will be deserialized without running the function again.\n    ///\n    /// This uses the [`JsonSerdeCodec`] encoding.\n    pub fn new(initial: impl FnOnce() -> T) -> Self {\n        SharedValue::new_with_encoding(initial)\n    }\n}\n\nimpl<T> SharedValue<T, FromToStringCodec>\nwhere\n    FromToStringCodec: Encoder<T> + Decoder<T>,\n    <FromToStringCodec as Encoder<T>>::Error: Debug,\n    <FromToStringCodec as Decoder<T>>::Error: Debug,\n    <FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    <<FromToStringCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n{\n    /// Wraps the initial value.\n    ///\n    /// If this is on the server, the function will be invoked and the value serialized. When it runs\n    /// on the client, it will be deserialized without running the function again.\n    ///\n    /// This uses the [`FromToStringCodec`] encoding.\n    pub fn new_str(initial: impl FnOnce() -> T) -> Self {\n        SharedValue::new_with_encoding(initial)\n    }\n}\n\n#[cfg(feature = \"serde-lite\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"serde-lite\")))]\nimpl<T> SharedValue<T, SerdeLite<JsonSerdeCodec>>\nwhere\n    SerdeLite<JsonSerdeCodec>: Encoder<T> + Decoder<T>,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Error: Debug,\n    <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Error: Debug,\n    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,\n    <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,\n    <<SerdeLite<JsonSerdeCodec> as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n{\n    /// Wraps the initial value.\n    ///\n    /// If this is on the server, the function will be invoked and the value serialized. When it runs\n    /// on the client, it will be deserialized without running the function again.\n    ///\n    /// This uses the [`SerdeLite`] encoding.\n    pub fn new_serde_lite(initial: impl FnOnce() -> T) -> Self {\n        SharedValue::new_with_encoding(initial)\n    }\n}\n\n#[cfg(feature = \"serde-wasm-bindgen\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"serde-wasm-bindgen\")))]\nimpl<T> SharedValue<T, JsonSerdeWasmCodec>\nwhere\n    JsonSerdeWasmCodec: Encoder<T> + Decoder<T>,\n    <JsonSerdeWasmCodec as Encoder<T>>::Error: Debug,\n    <JsonSerdeWasmCodec as Decoder<T>>::Error: Debug,\n    <JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    <<JsonSerdeWasmCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n{\n    /// Wraps the initial value.\n    ///\n    /// If this is on the server, the function will be invoked and the value serialized. When it runs\n    /// on the client, it will be deserialized without running the function again.\n    ///\n    /// This uses the [`JsonSerdeWasmCodec`] encoding.\n    pub fn new_serde_wb(initial: impl FnOnce() -> T) -> Self {\n        SharedValue::new_with_encoding(initial)\n    }\n}\n\n#[cfg(feature = \"miniserde\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"miniserde\")))]\nimpl<T> SharedValue<T, MiniserdeCodec>\nwhere\n    MiniserdeCodec: Encoder<T> + Decoder<T>,\n    <MiniserdeCodec as Encoder<T>>::Error: Debug,\n    <MiniserdeCodec as Decoder<T>>::Error: Debug,\n    <MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    <<MiniserdeCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n{\n    /// Wraps the initial value.\n    ///\n    /// If this is on the server, the function will be invoked and the value serialized. When it runs\n    /// on the client, it will be deserialized without running the function again.\n    ///\n    /// This uses the [`MiniserdeCodec`] encoding.\n    pub fn new_miniserde(initial: impl FnOnce() -> T) -> Self {\n        SharedValue::new_with_encoding(initial)\n    }\n}\n\n#[cfg(feature = \"rkyv\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"rkyv\")))]\nimpl<T> SharedValue<T, RkyvCodec>\nwhere\n    RkyvCodec: Encoder<T> + Decoder<T>,\n    <RkyvCodec as Encoder<T>>::Error: Debug,\n    <RkyvCodec as Decoder<T>>::Error: Debug,\n    <RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,\n    <RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,\n    <<RkyvCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n{\n    /// Wraps the initial value.\n    ///\n    /// If this is on the server, the function will be invoked and the value serialized. When it runs\n    /// on the client, it will be deserialized without running the function again.\n    ///\n    /// This uses the [`RkyvCodec`] encoding.\n    pub fn new_rkyv(initial: impl FnOnce() -> T) -> Self {\n        SharedValue::new_with_encoding(initial)\n    }\n}\n\nimpl<T, Ser> SharedValue<T, Ser>\nwhere\n    Ser: Encoder<T> + Decoder<T>,\n    <Ser as Encoder<T>>::Error: Debug,\n    <Ser as Decoder<T>>::Error: Debug,\n    <Ser as Encoder<T>>::Encoded: IntoEncodedString,\n    <Ser as Decoder<T>>::Encoded: FromEncodedStr,\n    <<Ser as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:\n        Debug,\n{\n    /// Wraps the initial value.\n    ///\n    /// If this is on the server, the function will be invoked and the value serialized. When it runs\n    /// on the client, it will be deserialized without running the function again.\n    ///\n    /// This uses `Ser` as an encoding.\n    pub fn new_with_encoding(initial: impl FnOnce() -> T) -> Self {\n        let value: T;\n        #[cfg(feature = \"hydration\")]\n        {\n            use reactive_graph::owner::Owner;\n            use std::borrow::Borrow;\n\n            let sc = Owner::current_shared_context();\n            let id = sc.as_ref().map(|sc| sc.next_id()).unwrap_or_default();\n            let serialized = sc.as_ref().and_then(|sc| sc.read_data(&id));\n            let hydrating =\n                sc.as_ref().map(|sc| sc.during_hydration()).unwrap_or(false);\n            value = if hydrating {\n                let value = match serialized {\n                    None => {\n                        #[cfg(feature = \"tracing\")]\n                        tracing::error!(\"couldn't deserialize\");\n                        None\n                    }\n                    Some(data) => {\n                        match <Ser as Decoder<T>>::Encoded::from_encoded_str(\n                            &data,\n                        ) {\n                            #[allow(unused_variables)] // used in tracing\n                            Err(e) => {\n                                #[cfg(feature = \"tracing\")]\n                                tracing::error!(\n                                    \"couldn't deserialize from {data:?}: {e:?}\"\n                                );\n                                None\n                            }\n                            Ok(encoded) => {\n                                let decoded = Ser::decode(encoded.borrow());\n                                #[cfg(feature = \"tracing\")]\n                                let decoded = decoded\n                                    .inspect_err(|e| tracing::error!(\"{e:?}\"));\n                                decoded.ok()\n                            }\n                        }\n                    }\n                };\n                value.unwrap_or_else(initial)\n            } else {\n                let init = initial();\n                #[cfg(feature = \"ssr\")]\n                if let Some(sc) = sc {\n                    if sc.get_is_hydrating() {\n                        match Ser::encode(&init)\n                            .map(IntoEncodedString::into_encoded_string)\n                        {\n                            Ok(value) => sc.write_async(\n                                id,\n                                Box::pin(async move { value }),\n                            ),\n                            #[allow(unused_variables)] // used in tracing\n                            Err(e) => {\n                                #[cfg(feature = \"tracing\")]\n                                tracing::error!(\"couldn't serialize: {e:?}\");\n                            }\n                        }\n                    }\n                }\n                init\n            }\n        }\n        #[cfg(not(feature = \"hydration\"))]\n        {\n            value = initial();\n        }\n        Self {\n            value,\n            ser: PhantomData,\n        }\n    }\n}\n\nimpl<T, Ser> Deref for SharedValue<T, Ser> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        &self.value\n    }\n}\n\nimpl<T, Ser> DerefMut for SharedValue<T, Ser> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.value\n    }\n}\n\nimpl<T, Ser> PartialEq for SharedValue<T, Ser>\nwhere\n    T: PartialEq,\n{\n    fn eq(&self, other: &Self) -> bool {\n        self.value == other.value\n    }\n}\n\nimpl<T, Ser> Eq for SharedValue<T, Ser> where T: Eq {}\n\nimpl<T, Ser> Display for SharedValue<T, Ser>\nwhere\n    T: Display,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.value)\n    }\n}\n\nimpl<T, Ser> Hash for SharedValue<T, Ser>\nwhere\n    T: Hash,\n{\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.value.hash(state);\n    }\n}\n\nimpl<T, Ser> PartialOrd for SharedValue<T, Ser>\nwhere\n    T: PartialOrd,\n{\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        self.value.partial_cmp(&other.value)\n    }\n}\n\nimpl<T, Ser> Ord for SharedValue<T, Ser>\nwhere\n    T: Ord,\n{\n    fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n        self.value.cmp(&other.value)\n    }\n}\n"
  },
  {
    "path": "meta/Cargo.toml",
    "content": "[package]\nname = \"leptos_meta\"\nversion = \"0.8.6\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Tools to set HTML metadata in the Leptos web framework.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nleptos = { workspace = true }\nor_poisoned = { workspace = true }\nindexmap = { workspace = true, default-features = true }\nsend_wrapper = { workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\nwasm-bindgen = { workspace = true, default-features = true }\nfutures = { workspace = true, default-features = true }\n\n[dependencies.web-sys]\nfeatures = [\"HtmlLinkElement\", \"HtmlMetaElement\", \"HtmlTitleElement\"]\nworkspace = true\ndefault-features = true\n\n[features]\ndefault = []\nssr = []\ntracing = [\"dep:tracing\"]\nnonce = [\"leptos/nonce\"]\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\"]\nmax_combination_size = 2\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(leptos_debuginfo)'] }\n"
  },
  {
    "path": "meta/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "meta/src/body.rs",
    "content": "use crate::ServerMetaContext;\nuse leptos::{\n    attr::{any_attribute::AnyAttribute, NextAttribute},\n    component, html,\n    reactive::owner::use_context,\n    tachys::{\n        dom::document,\n        html::attribute::Attribute,\n        hydration::Cursor,\n        view::{\n            add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n            RenderHtml,\n        },\n    },\n    IntoView,\n};\n\n/// A component to set metadata on the document’s `<body>` element from\n/// within the application.\n///\n/// This component takes no props, but can take any number of spread attributes\n/// following the `{..}` operator.\n///\n/// ```\n/// use leptos::prelude::*;\n/// use leptos_meta::*;\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     provide_meta_context();\n///     let (prefers_dark, set_prefers_dark) = signal(false);\n///     let body_class = move || {\n///         if prefers_dark.get() {\n///             \"dark\".to_string()\n///         } else {\n///             \"light\".to_string()\n///         }\n///     };\n///\n///     view! {\n///       <main>\n///         <Body {..} class=body_class id=\"body\"/>\n///       </main>\n///     }\n/// }\n/// ```\n#[component]\npub fn Body() -> impl IntoView {\n    BodyView { attributes: () }\n}\n\nstruct BodyView<At> {\n    attributes: At,\n}\n\nstruct BodyViewState<At>\nwhere\n    At: Attribute,\n{\n    attributes: At::State,\n}\n\nimpl<At> Render for BodyView<At>\nwhere\n    At: Attribute,\n{\n    type State = BodyViewState<At>;\n\n    fn build(self) -> Self::State {\n        let el = document().body().expect(\"there to be a <body> element\");\n        let attributes = self.attributes.build(&el);\n\n        BodyViewState { attributes }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.attributes.rebuild(&mut state.attributes);\n    }\n}\n\nimpl<At> AddAnyAttr for BodyView<At>\nwhere\n    At: Attribute,\n{\n    type Output<SomeNewAttr: Attribute> =\n        BodyView<<At as NextAttribute>::Output<SomeNewAttr>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        BodyView {\n            attributes: self.attributes.add_any_attr(attr),\n        }\n    }\n}\n\nimpl<At> RenderHtml for BodyView<At>\nwhere\n    At: Attribute,\n{\n    type AsyncOutput = BodyView<At::AsyncOutput>;\n    type Owned = BodyView<At::CloneableOwned>;\n\n    const MIN_LENGTH: usize = At::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {\n        self.attributes.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        BodyView {\n            attributes: self.attributes.resolve().await,\n        }\n    }\n\n    fn to_html_with_buf(\n        self,\n        _buf: &mut String,\n        _position: &mut Position,\n        _escape: bool,\n        _mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        if let Some(meta) = use_context::<ServerMetaContext>() {\n            let mut buf = String::new();\n            _ = html::attributes_to_html(\n                (self.attributes, extra_attrs),\n                &mut buf,\n            );\n            if !buf.is_empty() {\n                _ = meta.body.send(buf);\n            }\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _cursor: &Cursor,\n        _position: &PositionState,\n    ) -> Self::State {\n        let el = document().body().expect(\"there to be a <body> element\");\n        let attributes = self.attributes.hydrate::<FROM_SERVER>(&el);\n\n        BodyViewState { attributes }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        BodyView {\n            attributes: self.attributes.into_cloneable_owned(),\n        }\n    }\n}\n\nimpl<At> Mountable for BodyViewState<At>\nwhere\n    At: Attribute,\n{\n    fn unmount(&mut self) {}\n\n    fn mount(\n        &mut self,\n        _parent: &leptos::tachys::renderer::types::Element,\n        _marker: Option<&leptos::tachys::renderer::types::Node>,\n    ) {\n    }\n\n    fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {\n        false\n    }\n\n    fn elements(&self) -> Vec<leptos::tachys::renderer::types::Element> {\n        vec![document()\n            .body()\n            .expect(\"there to be a <body> element\")\n            .into()]\n    }\n}\n"
  },
  {
    "path": "meta/src/html.rs",
    "content": "use crate::ServerMetaContext;\nuse leptos::{\n    attr::{any_attribute::AnyAttribute, NextAttribute},\n    component, html,\n    reactive::owner::use_context,\n    tachys::{\n        dom::document,\n        html::attribute::Attribute,\n        hydration::Cursor,\n        view::{\n            add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n            RenderHtml,\n        },\n    },\n    IntoView,\n};\n\n/// A component to set metadata on the document’s `<html>` element from\n/// within the application.\n///\n/// This component takes no props, but can take any number of spread attributes\n/// following the `{..}` operator.\n///\n/// ```\n/// use leptos::prelude::*;\n/// use leptos_meta::*;\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     provide_meta_context();\n///\n///     view! {\n///       <main>\n///         <Html\n///           {..}\n///           lang=\"he\"\n///           dir=\"rtl\"\n///           data-theme=\"dark\"\n///         />\n///       </main>\n///     }\n/// }\n/// ```\n#[component]\npub fn Html() -> impl IntoView {\n    HtmlView { attributes: () }\n}\n\nstruct HtmlView<At> {\n    attributes: At,\n}\n\nstruct HtmlViewState<At>\nwhere\n    At: Attribute,\n{\n    attributes: At::State,\n}\n\nimpl<At> Render for HtmlView<At>\nwhere\n    At: Attribute,\n{\n    type State = HtmlViewState<At>;\n\n    fn build(self) -> Self::State {\n        let el = document()\n            .document_element()\n            .expect(\"there to be a <html> element\");\n\n        let attributes = self.attributes.build(&el);\n\n        HtmlViewState { attributes }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.attributes.rebuild(&mut state.attributes);\n    }\n}\n\nimpl<At> AddAnyAttr for HtmlView<At>\nwhere\n    At: Attribute,\n{\n    type Output<SomeNewAttr: Attribute> =\n        HtmlView<<At as NextAttribute>::Output<SomeNewAttr>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        HtmlView {\n            attributes: self.attributes.add_any_attr(attr),\n        }\n    }\n}\n\nimpl<At> RenderHtml for HtmlView<At>\nwhere\n    At: Attribute,\n{\n    type AsyncOutput = HtmlView<At::AsyncOutput>;\n    type Owned = HtmlView<At::CloneableOwned>;\n\n    const MIN_LENGTH: usize = At::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {\n        self.attributes.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        HtmlView {\n            attributes: self.attributes.resolve().await,\n        }\n    }\n\n    fn to_html_with_buf(\n        self,\n        _buf: &mut String,\n        _position: &mut Position,\n        _escape: bool,\n        _mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        if let Some(meta) = use_context::<ServerMetaContext>() {\n            let mut buf = String::new();\n            _ = html::attributes_to_html(\n                (self.attributes, extra_attrs),\n                &mut buf,\n            );\n            if !buf.is_empty() {\n                _ = meta.html.send(buf);\n            }\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _cursor: &Cursor,\n        _position: &PositionState,\n    ) -> Self::State {\n        let el = document()\n            .document_element()\n            .expect(\"there to be a <html> element\");\n\n        let attributes = self.attributes.hydrate::<FROM_SERVER>(&el);\n\n        HtmlViewState { attributes }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        HtmlView {\n            attributes: self.attributes.into_cloneable_owned(),\n        }\n    }\n}\n\nimpl<At> Mountable for HtmlViewState<At>\nwhere\n    At: Attribute,\n{\n    fn unmount(&mut self) {}\n\n    fn mount(\n        &mut self,\n        _parent: &leptos::tachys::renderer::types::Element,\n        _marker: Option<&leptos::tachys::renderer::types::Node>,\n    ) {\n        // <Html> only sets attributes\n        // the <html> tag doesn't need to be mounted anywhere, of course\n    }\n\n    fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {\n        false\n    }\n\n    fn elements(&self) -> Vec<leptos::tachys::renderer::types::Element> {\n        vec![document()\n            .document_element()\n            .expect(\"there to be a <html> element\")]\n    }\n}\n"
  },
  {
    "path": "meta/src/lib.rs",
    "content": "#![deny(missing_docs)]\n#![forbid(unsafe_code)]\n\n//! # Leptos Meta\n//!\n//! Leptos Meta allows you to modify content in a document’s `<head>` from within components\n//! using the [`Leptos`](https://github.com/leptos-rs/leptos) web framework.\n//!\n//! Document metadata is updated automatically when running in the browser. For server-side\n//! rendering, after the component tree is rendered to HTML, [`ServerMetaContextOutput::inject_meta_context`] will inject meta tags into a stream of HTML inside the `<head>`.\n//!\n//! ```\n//! use leptos::prelude::*;\n//! use leptos_meta::*;\n//!\n//! #[component]\n//! fn MyApp() -> impl IntoView {\n//!     // Provides a [`MetaContext`], if there is not already one provided.\n//!     provide_meta_context();\n//!\n//!     let (name, set_name) = create_signal(\"Alice\".to_string());\n//!\n//!     view! {\n//!       <Title\n//!         // reactively sets document.title when `name` changes\n//!         text=move || name.get()\n//!         // applies the `formatter` function to the `text` value\n//!         formatter=|text| format!(\"“{text}” is your name\")\n//!       />\n//!       <main>\n//!         <input\n//!           prop:value=move || name.get()\n//!           on:input=move |ev| set_name.set(event_target_value(&ev))\n//!         />\n//!       </main>\n//!     }\n//! }\n//! ```\n//! # Feature Flags\n//! - `ssr` Server-side rendering: Generate an HTML string (typically on the server)\n//! - `tracing` Adds integration with the `tracing` crate.\n//!\n//! **Important Note:** If you’re using server-side rendering, you should enable `ssr`.\n\nuse futures::{Stream, StreamExt};\nuse leptos::{\n    attr::{any_attribute::AnyAttribute, NextAttribute},\n    component,\n    logging::debug_warn,\n    oco::Oco,\n    reactive::owner::{provide_context, use_context},\n    tachys::{\n        dom::document,\n        html::{\n            attribute::Attribute,\n            element::{ElementType, HtmlElement},\n        },\n        hydration::Cursor,\n        view::{\n            add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n            RenderHtml,\n        },\n    },\n    IntoView,\n};\nuse send_wrapper::SendWrapper;\nuse std::{\n    fmt::Debug,\n    sync::{\n        mpsc::{channel, Receiver, Sender},\n        Arc, LazyLock,\n    },\n};\nuse wasm_bindgen::JsCast;\nuse web_sys::HtmlHeadElement;\n\nmod body;\nmod html;\nmod link;\nmod meta_tags;\nmod script;\nmod style;\nmod stylesheet;\nmod title;\npub use body::*;\npub use html::*;\npub use link::*;\npub use meta_tags::*;\npub use script::*;\npub use style::*;\npub use stylesheet::*;\npub use title::*;\n\n/// Contains the current state of meta tags. To access it, you can use [`use_head`].\n///\n/// This should generally by provided somewhere in the root of your application using\n/// [`provide_meta_context`].\n#[derive(Clone, Debug)]\npub struct MetaContext {\n    /// Metadata associated with the `<title>` element.\n    pub(crate) title: TitleContext,\n    /// The hydration cursor for the location in the `<head>` for arbitrary tags will be rendered.\n    pub(crate) cursor: Arc<LazyLock<SendWrapper<Cursor>>>,\n}\n\nimpl MetaContext {\n    /// Creates an empty [`MetaContext`].\n    pub fn new() -> Self {\n        Default::default()\n    }\n}\n\npub(crate) const HEAD_MARKER_COMMENT: &str = \"HEAD\";\n/// Return value of [`Node::node_type`] for a comment.\n/// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#node.comment_node\nconst COMMENT_NODE: u16 = 8;\n\nimpl Default for MetaContext {\n    fn default() -> Self {\n        let build_cursor: fn() -> SendWrapper<Cursor> = || {\n            let head = document().head().expect(\"missing <head> element\");\n            let mut cursor = None;\n            let mut child = head.first_child();\n            while let Some(this_child) = child {\n                if this_child.node_type() == COMMENT_NODE\n                    && this_child.text_content().as_deref()\n                        == Some(HEAD_MARKER_COMMENT)\n                {\n                    cursor = Some(this_child);\n                    break;\n                }\n                child = this_child.next_sibling();\n            }\n            SendWrapper::new(Cursor::new(\n                cursor\n                    .expect(\n                        \"no leptos_meta HEAD marker comment found. Did you \\\n                         include the <MetaTags/> component in the <head> of \\\n                         your server-rendered app?\",\n                    )\n                    .unchecked_into(),\n            ))\n        };\n\n        let cursor = Arc::new(LazyLock::new(build_cursor));\n        Self {\n            title: Default::default(),\n            cursor,\n        }\n    }\n}\n\n/// Allows you to add `<head>` content from components located in the `<body>` of the application,\n/// which can be accessed during server rendering via [`ServerMetaContextOutput`].\n///\n/// This should be provided as context during server rendering.\n///\n/// No content added after the first chunk of the stream has been sent will be included in the\n/// initial `<head>`. Data that needs to be included in the `<head>` during SSR should be\n/// synchronous or loaded as a blocking resource.\n#[derive(Clone, Debug)]\npub struct ServerMetaContext {\n    /// Metadata associated with the `<title>` element.\n    pub(crate) title: TitleContext,\n    /// Attributes for the `<html>` element.\n    pub(crate) html: Sender<String>,\n    /// Attributes for the `<body>` element.\n    pub(crate) body: Sender<String>,\n    /// Arbitrary elements to be added to the `<head>` as HTML.\n    #[allow(unused)] // used in SSR\n    pub(crate) elements: Sender<String>,\n}\n\n/// Allows you to access `<head>` content that was inserted via [`ServerMetaContext`].\n#[must_use = \"If you do not use the output, adding meta tags will have no \\\n              effect.\"]\n#[derive(Debug)]\npub struct ServerMetaContextOutput {\n    pub(crate) title: TitleContext,\n    html: Receiver<String>,\n    body: Receiver<String>,\n    elements: Receiver<String>,\n}\n\nimpl ServerMetaContext {\n    /// Creates an empty [`ServerMetaContext`].\n    pub fn new() -> (ServerMetaContext, ServerMetaContextOutput) {\n        let title = TitleContext::default();\n        let (html_tx, html_rx) = channel();\n        let (body_tx, body_rx) = channel();\n        let (elements_tx, elements_rx) = channel();\n        let tx = ServerMetaContext {\n            title: title.clone(),\n            html: html_tx,\n            body: body_tx,\n            elements: elements_tx,\n        };\n        let rx = ServerMetaContextOutput {\n            title,\n            html: html_rx,\n            body: body_rx,\n            elements: elements_rx,\n        };\n        (tx, rx)\n    }\n}\n\nimpl ServerMetaContextOutput {\n    /// Consumes the metadata, injecting it into the the first chunk of an HTML stream in the\n    /// appropriate place.\n    ///\n    /// This means that only meta tags rendered during the first chunk of the stream will be\n    /// included.\n    pub async fn inject_meta_context(\n        self,\n        mut stream: impl Stream<Item = String> + Send + Unpin,\n    ) -> impl Stream<Item = String> + Send {\n        // if the first chunk consists of a synchronously-available Suspend,\n        // inject_meta_context can accidentally run a tick before it, but the Suspend\n        // when both are available. waiting a tick before awaiting the first chunk\n        // in the Stream ensures that this always runs after that first chunk\n        // see https://github.com/leptos-rs/leptos/issues/3976 for the original issue\n        leptos::task::tick().await;\n\n        // wait for the first chunk of the stream, to ensure our components hve run\n        let mut first_chunk = stream.next().await.unwrap_or_default();\n\n        // create <title> tag\n        let title = self.title.as_string();\n        let title_len = title\n            .as_ref()\n            .map(|n| \"<title>\".len() + n.len() + \"</title>\".len())\n            .unwrap_or(0);\n\n        // collect all registered meta tags\n        let meta_buf = self.elements.try_iter().collect::<String>();\n\n        // get HTML strings for `<html>` and `<body>`\n        let html_attrs = self.html.try_iter().collect::<String>();\n        let body_attrs = self.body.try_iter().collect::<String>();\n\n        let mut modified_chunk = if title_len == 0 && meta_buf.is_empty() {\n            first_chunk\n        } else {\n            let mut buf = String::with_capacity(\n                first_chunk.len() + title_len + meta_buf.len(),\n            );\n            let head_loc = first_chunk\n                .find(\"</head>\")\n                .expect(\"you are using leptos_meta without a </head> tag\");\n            let marker_loc = first_chunk\n                .find(\"<!--HEAD-->\")\n                .map(|pos| pos + \"<!--HEAD-->\".len())\n                .unwrap_or_else(|| {\n                    first_chunk.find(\"</head>\").unwrap_or(head_loc)\n                });\n            let (before_marker, after_marker) =\n                first_chunk.split_at_mut(marker_loc);\n            buf.push_str(before_marker);\n            buf.push_str(&meta_buf);\n            if let Some(title) = title {\n                buf.push_str(\"<title>\");\n                buf.push_str(&title);\n                buf.push_str(\"</title>\");\n            }\n            buf.push_str(after_marker);\n            buf\n        };\n\n        if !html_attrs.is_empty() {\n            if let Some(index) = modified_chunk.find(\"<html\") {\n                // Calculate the position where the new string should be inserted\n                let insert_pos = index + \"<html\".len();\n                modified_chunk.insert_str(insert_pos, &html_attrs);\n            }\n        }\n\n        if !body_attrs.is_empty() {\n            if let Some(index) = modified_chunk.find(\"<body\") {\n                // Calculate the position where the new string should be inserted\n                let insert_pos = index + \"<body\".len();\n                modified_chunk.insert_str(insert_pos, &body_attrs);\n            }\n        }\n\n        futures::stream::once(async move { modified_chunk }).chain(stream)\n    }\n}\n\n/// Provides a [`MetaContext`], if there is not already one provided. This ensures that you can provide it\n/// at the highest possible level, without overwriting a [`MetaContext`] that has already been provided\n/// (for example, by a server-rendering integration.)\npub fn provide_meta_context() {\n    if use_context::<MetaContext>().is_none() {\n        provide_context(MetaContext::new());\n    }\n}\n\n/// Returns the current [`MetaContext`].\n///\n/// If there is no [`MetaContext`] in this or any parent scope, this will\n/// create a new [`MetaContext`] and provide it to the current scope.\n///\n/// Note that this may cause confusing behavior, e.g., if multiple nested routes independently\n/// call `use_head()` but a single [`MetaContext`] has not been provided at the application root.\n/// The best practice is always to call [`provide_meta_context`] early in the application.\npub fn use_head() -> MetaContext {\n    match use_context::<MetaContext>() {\n        None => {\n            debug_warn!(\n                \"use_head() is being called without a MetaContext being \\\n                 provided. We'll automatically create and provide one, but if \\\n                 this is being called in a child route it may cause bugs. To \\\n                 be safe, you should provide_meta_context() somewhere in the \\\n                 root of the app.\"\n            );\n            let meta = MetaContext::new();\n            provide_context(meta.clone());\n            meta\n        }\n        Some(ctx) => ctx,\n    }\n}\n\npub(crate) fn register<E, At, Ch>(\n    el: HtmlElement<E, At, Ch>,\n) -> RegisteredMetaTag<E, At, Ch>\nwhere\n    HtmlElement<E, At, Ch>: RenderHtml,\n{\n    RegisteredMetaTag { el }\n}\n\nstruct RegisteredMetaTag<E, At, Ch> {\n    // this is `None` if we've already taken it out to render to HTML on the server\n    // we don't render it in place in RenderHtml, so it's fine\n    el: HtmlElement<E, At, Ch>,\n}\n\nstruct RegisteredMetaTagState<E, At, Ch>\nwhere\n    HtmlElement<E, At, Ch>: Render,\n{\n    state: <HtmlElement<E, At, Ch> as Render>::State,\n}\n\nimpl<E, At, Ch> Drop for RegisteredMetaTagState<E, At, Ch>\nwhere\n    HtmlElement<E, At, Ch>: Render,\n{\n    fn drop(&mut self) {\n        self.state.unmount();\n    }\n}\n\nfn document_head() -> HtmlHeadElement {\n    let document = document();\n    document.head().unwrap_or_else(|| {\n        let el = document.create_element(\"head\").unwrap();\n        let document = document.document_element().unwrap();\n        _ = document.append_child(&el);\n        el.unchecked_into()\n    })\n}\n\nimpl<E, At, Ch> Render for RegisteredMetaTag<E, At, Ch>\nwhere\n    E: ElementType,\n    At: Attribute,\n    Ch: Render,\n{\n    type State = RegisteredMetaTagState<E, At, Ch>;\n\n    fn build(self) -> Self::State {\n        let state = self.el.build();\n        RegisteredMetaTagState { state }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.el.rebuild(&mut state.state);\n    }\n}\n\nimpl<E, At, Ch> AddAnyAttr for RegisteredMetaTag<E, At, Ch>\nwhere\n    E: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n{\n    type Output<SomeNewAttr: Attribute> =\n        RegisteredMetaTag<E, <At as NextAttribute>::Output<SomeNewAttr>, Ch>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        RegisteredMetaTag {\n            el: self.el.add_any_attr(attr),\n        }\n    }\n}\n\nimpl<E, At, Ch> RenderHtml for RegisteredMetaTag<E, At, Ch>\nwhere\n    E: ElementType,\n    At: Attribute,\n    Ch: RenderHtml + Send,\n{\n    type AsyncOutput = Self;\n    type Owned = RegisteredMetaTag<E, At::CloneableOwned, Ch::Owned>;\n\n    const MIN_LENGTH: usize = 0;\n    const EXISTS: bool = false;\n\n    fn dry_resolve(&mut self) {\n        self.el.dry_resolve()\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self // TODO?\n    }\n\n    fn to_html_with_buf(\n        self,\n        _buf: &mut String,\n        _position: &mut Position,\n        _escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // meta tags are rendered into the buffer stored into the context\n        // the value has already been taken out, when we're on the server\n        #[cfg(feature = \"ssr\")]\n        if let Some(cx) = use_context::<ServerMetaContext>() {\n            let mut buf = String::new();\n            self.el.to_html_with_buf(\n                &mut buf,\n                &mut Position::NextChild,\n                false,\n                false,\n                vec![],\n            );\n            _ = cx.elements.send(buf); // fails only if the receiver is already dropped\n        } else {\n            let msg = \"tried to use a leptos_meta component without \\\n                       `ServerMetaContext` provided\";\n\n            #[cfg(feature = \"tracing\")]\n            tracing::warn!(\"{}\", msg);\n\n            #[cfg(not(feature = \"tracing\"))]\n            eprintln!(\"{msg}\");\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _cursor: &Cursor,\n        _position: &PositionState,\n    ) -> Self::State {\n        let cursor = use_context::<MetaContext>()\n            .expect(\n                \"attempting to hydrate `leptos_meta` components without a \\\n                 MetaContext provided\",\n            )\n            .cursor;\n        let state = self.el.hydrate::<FROM_SERVER>(\n            &cursor,\n            &PositionState::new(Position::NextChild),\n        );\n        RegisteredMetaTagState { state }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        RegisteredMetaTag {\n            el: self.el.into_owned(),\n        }\n    }\n}\n\nimpl<E, At, Ch> Mountable for RegisteredMetaTagState<E, At, Ch>\nwhere\n    E: ElementType,\n    At: Attribute,\n    Ch: Render,\n{\n    fn unmount(&mut self) {\n        self.state.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        _parent: &leptos::tachys::renderer::types::Element,\n        _marker: Option<&leptos::tachys::renderer::types::Node>,\n    ) {\n        // we always mount this to the <head>, which is the whole point\n        // but this shouldn't warn about the parent being a regular element or being unused\n        // because it will call \"mount\" with the parent where it is located in the component tree,\n        // but actually be mounted to the <head>\n        self.state.mount(&document_head(), None);\n    }\n\n    fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {\n        // Registered meta tags will be mounted in the <head>, but *seem* to be mounted somewhere\n        // else in the DOM. We should never tell the renderer that we have successfully mounted\n        // something before this, because if e.g., a <Meta/> is the first item in an Either, then\n        // the alternate view will end up being mounted in the <head> -- which is not at all what\n        // we intended!\n        false\n    }\n\n    fn elements(&self) -> Vec<leptos::tachys::renderer::types::Element> {\n        self.state.elements()\n    }\n}\n\n/// During server rendering, inserts the meta tags that have been generated by the other components\n/// in this crate into the DOM. This should be placed somewhere inside the `<head>` element that is\n/// being used during server rendering.\n#[component]\npub fn MetaTags() -> impl IntoView {\n    MetaTagsView\n}\n\n#[derive(Debug)]\nstruct MetaTagsView;\n\n// this implementation doesn't do anything during client-side rendering, it's just for server-side\n// rendering HTML for all the tags that will be injected into the `<head>`\n//\n// client-side rendering is handled by the individual components\nimpl Render for MetaTagsView {\n    type State = ();\n\n    fn build(self) -> Self::State {}\n\n    fn rebuild(self, _state: &mut Self::State) {}\n}\n\nimpl AddAnyAttr for MetaTagsView {\n    type Output<SomeNewAttr: Attribute> = MetaTagsView;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        self\n    }\n}\n\nimpl RenderHtml for MetaTagsView {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        _position: &mut Position,\n        _escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        buf.push_str(\"<!--HEAD-->\");\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _cursor: &Cursor,\n        _position: &PositionState,\n    ) -> Self::State {\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\npub(crate) trait OrDefaultNonce {\n    fn or_default_nonce(self) -> Option<Oco<'static, str>>;\n}\n\nimpl OrDefaultNonce for Option<Oco<'static, str>> {\n    fn or_default_nonce(self) -> Option<Oco<'static, str>> {\n        #[cfg(feature = \"nonce\")]\n        {\n            use leptos::nonce::use_nonce;\n\n            match self {\n                Some(nonce) => Some(nonce),\n                None => use_nonce().map(|n| Arc::clone(n.as_inner()).into()),\n            }\n        }\n        #[cfg(not(feature = \"nonce\"))]\n        {\n            self\n        }\n    }\n}\n"
  },
  {
    "path": "meta/src/link.rs",
    "content": "use crate::register;\nuse leptos::{\n    component, oco::Oco, prelude::GlobalAttributes,\n    tachys::html::element::link, IntoView,\n};\n\n/// Injects an [`HTMLLinkElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement) into the document\n/// head, accepting any of the valid attributes for that tag.\n///\n/// ```\n/// use leptos::prelude::*;\n/// use leptos_meta::*;\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     provide_meta_context();\n///\n///     view! {\n///       <main>\n///         <Link rel=\"preload\"\n///           href=\"myFont.woff2\"\n///           as_=\"font\"\n///           type_=\"font/woff2\"\n///           crossorigin=\"anonymous\"\n///         />\n///       </main>\n///     }\n/// }\n/// ```\n#[component]\npub fn Link(\n    /// The [`id`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-id) attribute.\n    #[prop(optional, into)]\n    id: Option<Oco<'static, str>>,\n    /// The [`as`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as) attribute.\n    #[prop(optional, into)]\n    as_: Option<Oco<'static, str>>,\n    /// The [`crossorigin`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-crossorigin) attribute.\n    #[prop(optional, into)]\n    crossorigin: Option<Oco<'static, str>>,\n    /// The [`fetchpriority`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-fetchpriority) attribute.\n    #[prop(optional, into)]\n    fetchpriority: Option<Oco<'static, str>>,\n    /// The [`href`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-href) attribute.\n    #[prop(optional, into)]\n    href: Option<Oco<'static, str>>,\n    /// The [`hreflang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-hreflang) attribute.\n    #[prop(optional, into)]\n    hreflang: Option<Oco<'static, str>>,\n    /// The [`imagesizes`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesizes) attribute.\n    #[prop(optional, into)]\n    imagesizes: Option<Oco<'static, str>>,\n    /// The [`imagesrcset`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesrcset) attribute.\n    #[prop(optional, into)]\n    imagesrcset: Option<Oco<'static, str>>,\n    /// The [`integrity`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-integrity) attribute.\n    #[prop(optional, into)]\n    integrity: Option<Oco<'static, str>>,\n    /// The [`media`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-media) attribute.\n    #[prop(optional, into)]\n    media: Option<Oco<'static, str>>,\n    /// The [`referrerpolicy`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-referrerpolicy) attribute.\n    #[prop(optional, into)]\n    referrerpolicy: Option<Oco<'static, str>>,\n    /// The [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel) attribute.\n    #[prop(optional, into)]\n    rel: Option<Oco<'static, str>>,\n    /// The [`sizes`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-sizes) attribute.\n    #[prop(optional, into)]\n    sizes: Option<Oco<'static, str>>,\n    /// The [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-title) attribute.\n    #[prop(optional, into)]\n    title: Option<Oco<'static, str>>,\n    /// The [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-type) attribute.\n    #[prop(optional, into)]\n    type_: Option<Oco<'static, str>>,\n    /// The [`blocking`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-blocking) attribute.\n    #[prop(optional, into)]\n    blocking: Option<Oco<'static, str>>,\n) -> impl IntoView {\n    // TODO additional attributes\n    register(\n        link()\n            .id(id)\n            .r#as(as_)\n            .crossorigin(crossorigin)\n            .fetchpriority(fetchpriority)\n            .href(href)\n            .hreflang(hreflang)\n            .imagesizes(imagesizes)\n            .imagesrcset(imagesrcset)\n            .integrity(integrity)\n            .media(media)\n            .referrerpolicy(referrerpolicy)\n            .rel(rel)\n            .sizes(sizes)\n            .title(title)\n            .r#type(type_)\n            .blocking(blocking),\n    )\n}\n"
  },
  {
    "path": "meta/src/meta_tags.rs",
    "content": "use crate::register;\nuse leptos::{\n    component,\n    prelude::{CustomAttribute, GlobalAttributes},\n    tachys::html::element::meta,\n    text_prop::TextProp,\n    IntoView,\n};\n\n/// Injects an [`HTMLMetaElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMetaElement) into the document\n/// head to set metadata\n///\n/// ```\n/// use leptos::prelude::*;\n/// use leptos_meta::*;\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///   provide_meta_context();\n///\n///   view! {\n///     <main>\n///       <Meta charset=\"utf-8\"/>\n///       <Meta name=\"description\" content=\"A Leptos fan site.\"/>\n///       <Meta http_equiv=\"refresh\" content=\"3;url=https://github.com/leptos-rs/leptos\"/>\n///     </main>\n///   }\n/// }\n/// ```\n#[component]\npub fn Meta(\n    /// The [`charset`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-charset) attribute.\n    #[prop(optional, into)]\n    charset: Option<TextProp>,\n    /// The [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-name) attribute.\n    #[prop(optional, into)]\n    name: Option<TextProp>,\n    /// The [`property`](https://ogp.me/) attribute.\n    #[prop(optional, into)]\n    property: Option<TextProp>,\n    /// The [`http-equiv`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-http-equiv) attribute.\n    #[prop(optional, into)]\n    http_equiv: Option<TextProp>,\n    /// The [`itemprop`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-itemprop) attribute.\n    #[prop(optional, into)]\n    itemprop: Option<TextProp>,\n    /// The [`content`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-content) attribute.\n    #[prop(optional, into)]\n    content: Option<TextProp>,\n) -> impl IntoView {\n    register(\n        meta()\n            .charset(charset.map(|v| move || v.get()))\n            .name(name.map(|v| move || v.get()))\n            .attr(\"property\", property.map(|v| move || v.get()))\n            .http_equiv(http_equiv.map(|v| move || v.get()))\n            .itemprop(itemprop.map(|v| move || v.get()))\n            .content(content.map(|v| move || v.get())),\n    )\n}\n"
  },
  {
    "path": "meta/src/script.rs",
    "content": "use crate::{register, OrDefaultNonce};\nuse leptos::{\n    component, oco::Oco, prelude::*, tachys::html::element::script, IntoView,\n};\n\n/// Injects an [`HTMLScriptElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement) into the document\n/// head, accepting any of the valid attributes for that tag.\n///\n/// ```\n/// use leptos::prelude::*;\n/// use leptos_meta::*;\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     provide_meta_context();\n///\n///     view! {\n///       <main>\n///         <Script>\n///           \"console.log('Hello, world!');\"\n///         </Script>\n///       </main>\n///     }\n/// }\n/// ```\n#[component]\npub fn Script(\n    /// An ID for the `<script>` tag.\n    #[prop(optional, into)]\n    id: Option<Oco<'static, str>>,\n    /// The [`async`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-async) attribute.\n    #[prop(optional, into)]\n    async_: Option<Oco<'static, str>>,\n    /// The [`crossorigin`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-crossorigin) attribute.\n    #[prop(optional, into)]\n    crossorigin: Option<Oco<'static, str>>,\n    /// The [`defer`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer) attribute.\n    #[prop(optional, into)]\n    defer: Option<Oco<'static, str>>,\n    /// The [`fetchpriority `](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-fetchpriority ) attribute.\n    #[prop(optional, into)]\n    fetchpriority: Option<Oco<'static, str>>,\n    /// The [`integrity`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-integrity) attribute.\n    #[prop(optional, into)]\n    integrity: Option<Oco<'static, str>>,\n    /// The [`nomodule`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nomodule) attribute.\n    #[prop(optional, into)]\n    nomodule: Option<Oco<'static, str>>,\n    /// The [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nonce) attribute.\n    #[prop(optional, into)]\n    nonce: Option<Oco<'static, str>>,\n    /// The [`referrerpolicy`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-referrerpolicy) attribute.\n    #[prop(optional, into)]\n    referrerpolicy: Option<Oco<'static, str>>,\n    /// The [`src`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-src) attribute.\n    #[prop(optional, into)]\n    src: Option<Oco<'static, str>>,\n    /// The [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type) attribute.\n    #[prop(optional, into)]\n    type_: Option<Oco<'static, str>>,\n    /// The [`blocking`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-blocking) attribute.\n    #[prop(optional, into)]\n    blocking: Option<Oco<'static, str>>,\n    /// The content of the `<script>` tag.\n    #[prop(optional)]\n    children: Option<Children>,\n) -> impl IntoView {\n    register(\n        script()\n            .id(id)\n            .r#async(async_)\n            .crossorigin(crossorigin)\n            .defer(defer)\n            .fetchpriority(fetchpriority)\n            .integrity(integrity)\n            .nomodule(nomodule)\n            .nonce(nonce.or_default_nonce())\n            .referrerpolicy(referrerpolicy)\n            .src(src)\n            .r#type(type_)\n            .blocking(blocking)\n            .child(children.map(|c| c())),\n    )\n}\n"
  },
  {
    "path": "meta/src/style.rs",
    "content": "use crate::{register, OrDefaultNonce};\nuse leptos::{\n    component, oco::Oco, prelude::*, tachys::html::element::style, IntoView,\n};\n\n/// Injects an [`HTMLStyleElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLStyleElement) into the document\n/// head, accepting any of the valid attributes for that tag.\n///\n/// ```\n/// use leptos::prelude::*;\n/// use leptos_meta::*;\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     provide_meta_context();\n///\n///     view! {\n///       <main>\n///         <Style>\n///           \"body { font-weight: bold; }\"\n///         </Style>\n///       </main>\n///     }\n/// }\n/// ```\n#[component]\npub fn Style(\n    /// An ID for the `<script>` tag.\n    #[prop(optional, into)]\n    id: Option<Oco<'static, str>>,\n    /// The [`media`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-media) attribute.\n    #[prop(optional, into)]\n    media: Option<Oco<'static, str>>,\n    /// The [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-nonce) attribute.\n    #[prop(optional, into)]\n    nonce: Option<Oco<'static, str>>,\n    /// The [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-title) attribute.\n    #[prop(optional, into)]\n    title: Option<Oco<'static, str>>,\n    /// The [`blocking`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-blocking) attribute.\n    #[prop(optional, into)]\n    blocking: Option<Oco<'static, str>>,\n    /// The content of the `<style>` tag.\n    #[prop(optional)]\n    children: Option<Children>,\n) -> impl IntoView {\n    register(\n        style()\n            .id(id)\n            .media(media)\n            .nonce(nonce.or_default_nonce())\n            .title(title)\n            .blocking(blocking)\n            .child(children.map(|c| c())),\n    )\n}\n"
  },
  {
    "path": "meta/src/stylesheet.rs",
    "content": "use crate::register;\nuse leptos::{\n    attr::global::GlobalAttributes, component, prelude::LeptosOptions,\n    tachys::html::element::link, IntoView,\n};\n\n/// Injects an [`HTMLLinkElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement) into the document\n/// head that loads a stylesheet from the URL given by the `href` property.\n///\n/// Note that this does *not* work with the `cargo-leptos` `hash-files` feature: if you are using file\n/// hashing, you should use [`HashedStylesheet`](crate::HashedStylesheet).\n///\n/// ```\n/// use leptos::prelude::*;\n/// use leptos_meta::*;\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     provide_meta_context();\n///\n///     view! {\n///       <main>\n///         <Stylesheet href=\"/style.css\"/>\n///       </main>\n///     }\n/// }\n/// ```\n#[component]\npub fn Stylesheet(\n    /// The URL at which the stylesheet is located.\n    #[prop(into)]\n    href: String,\n    /// An ID for the stylesheet.\n    #[prop(optional, into)]\n    id: Option<String>,\n) -> impl IntoView {\n    // TODO additional attributes\n    register(link().id(id).rel(\"stylesheet\").href(href))\n}\n\n/// Injects an [`HTMLLinkElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement) into the document head that loads a `cargo-leptos`-hashed stylesheet.\n///\n/// This should only be used in the application’s server-side `shell` function, as\n/// [`LeptosOptions`] is not available in the browser. Unlike other `leptos_meta` components, it\n/// will render the `<link>` it creates exactly where it is called.\n#[component]\npub fn HashedStylesheet(\n    /// Leptos options\n    options: LeptosOptions,\n    /// An ID for the stylesheet.\n    #[prop(optional, into)]\n    id: Option<String>,\n    /// A base url, not including a trailing slash\n    #[prop(optional, into)]\n    root: Option<String>,\n) -> impl IntoView {\n    let mut css_file_name = options.output_name.to_string();\n    if options.hash_files {\n        let hash_path = std::env::current_exe()\n            .map(|path| {\n                path.parent().map(|p| p.to_path_buf()).unwrap_or_default()\n            })\n            .unwrap_or_default()\n            .join(options.hash_file.as_ref());\n        if hash_path.exists() {\n            let hashes = std::fs::read_to_string(&hash_path)\n                .expect(\"failed to read hash file\");\n            for line in hashes.lines() {\n                let line = line.trim();\n                if !line.is_empty() {\n                    if let Some((file, hash)) = line.split_once(':') {\n                        if file == \"css\" {\n                            css_file_name\n                                .push_str(&format!(\".{}\", hash.trim()));\n                        }\n                    }\n                }\n            }\n        }\n    }\n    css_file_name.push_str(\".css\");\n    let pkg_path = &options.site_pkg_dir;\n    let root = root.unwrap_or_default();\n\n    link()\n        .id(id)\n        .rel(\"stylesheet\")\n        .href(format!(\"{root}/{pkg_path}/{css_file_name}\"))\n}\n"
  },
  {
    "path": "meta/src/title.rs",
    "content": "use crate::{use_head, MetaContext, ServerMetaContext};\nuse leptos::{\n    attr::{any_attribute::AnyAttribute, Attribute},\n    component,\n    oco::Oco,\n    prelude::{ArcTrigger, Notify, Track},\n    reactive::{effect::RenderEffect, owner::use_context},\n    tachys::{\n        dom::document,\n        hydration::Cursor,\n        view::{\n            add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n            RenderHtml,\n        },\n    },\n    text_prop::TextProp,\n    IntoView,\n};\nuse or_poisoned::OrPoisoned;\nuse std::sync::{\n    atomic::{AtomicU32, Ordering},\n    Arc, Mutex, RwLock,\n};\n\n/// Contains the current state of the document's `<title>`.\n#[derive(Clone, Default)]\npub struct TitleContext {\n    id: Arc<AtomicU32>,\n    formatter_stack: Arc<RwLock<Vec<(TitleId, Formatter)>>>,\n    text_stack: Arc<RwLock<Vec<(TitleId, TextProp)>>>,\n    revalidate: ArcTrigger,\n    #[allow(clippy::type_complexity)]\n    effect: Arc<Mutex<Option<RenderEffect<Option<Oco<'static, str>>>>>>,\n}\n\nimpl core::fmt::Debug for TitleContext {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_tuple(\"TitleContext\").finish()\n    }\n}\n\ntype TitleId = u32;\n\nimpl TitleContext {\n    fn next_id(&self) -> TitleId {\n        self.id.fetch_add(1, Ordering::Relaxed)\n    }\n\n    fn invalidate(&self) {\n        self.revalidate.notify();\n    }\n\n    fn spawn_effect(&self) {\n        let this = self.clone();\n        let revalidate = self.revalidate.clone();\n\n        let mut effect_lock = self.effect.lock().or_poisoned();\n        if effect_lock.is_none() {\n            *effect_lock = Some(RenderEffect::new({\n                move |_| {\n                    revalidate.track();\n                    let text = this.as_string();\n                    document().set_title(text.as_deref().unwrap_or_default());\n                    text\n                }\n            }));\n        }\n    }\n\n    fn push_text_and_formatter(\n        &self,\n        id: TitleId,\n        text: Option<TextProp>,\n        formatter: Option<Formatter>,\n    ) {\n        if let Some(text) = text {\n            self.text_stack.write().or_poisoned().push((id, text));\n        }\n        if let Some(formatter) = formatter {\n            self.formatter_stack\n                .write()\n                .or_poisoned()\n                .push((id, formatter));\n        }\n        self.invalidate();\n    }\n\n    fn update_text_and_formatter(\n        &self,\n        id: TitleId,\n        text: Option<TextProp>,\n        formatter: Option<Formatter>,\n    ) {\n        let mut text_stack = self.text_stack.write().or_poisoned();\n        let mut formatter_stack = self.formatter_stack.write().or_poisoned();\n        let text_pos =\n            text_stack.iter().position(|(item_id, _)| *item_id == id);\n        let formatter_pos = formatter_stack\n            .iter()\n            .position(|(item_id, _)| *item_id == id);\n\n        match (text_pos, text) {\n            (None, None) => {}\n            (Some(old), Some(new)) => {\n                text_stack[old].1 = new;\n                self.invalidate();\n            }\n            (Some(old), None) => {\n                text_stack.remove(old);\n                self.invalidate();\n            }\n            (None, Some(new)) => {\n                text_stack.push((id, new));\n                self.invalidate();\n            }\n        }\n        match (formatter_pos, formatter) {\n            (None, None) => {}\n            (Some(old), Some(new)) => {\n                formatter_stack[old].1 = new;\n                self.invalidate();\n            }\n            (Some(old), None) => {\n                formatter_stack.remove(old);\n                self.invalidate();\n            }\n            (None, Some(new)) => {\n                formatter_stack.push((id, new));\n                self.invalidate();\n            }\n        }\n    }\n\n    fn remove_id(&self, id: TitleId) -> (Option<TextProp>, Option<Formatter>) {\n        let mut text_stack = self.text_stack.write().or_poisoned();\n        let text = text_stack\n            .iter()\n            .position(|(item_id, _)| *item_id == id)\n            .map(|pos| text_stack.remove(pos).1);\n\n        let mut formatter_stack = self.formatter_stack.write().or_poisoned();\n        let formatter = formatter_stack\n            .iter()\n            .position(|(item_id, _)| *item_id == id)\n            .map(|pos| formatter_stack.remove(pos).1);\n\n        self.invalidate();\n\n        (text, formatter)\n    }\n\n    /// Converts the title into a string that can be used as the text content of a `<title>` tag.\n    pub fn as_string(&self) -> Option<Oco<'static, str>> {\n        let title = self\n            .text_stack\n            .read()\n            .or_poisoned()\n            .last()\n            .map(|n| n.1.get());\n\n        title.map(|title| {\n            if let Some(formatter) =\n                self.formatter_stack.read().or_poisoned().last()\n            {\n                (formatter.1 .0)(title.into_owned()).into()\n            } else {\n                title\n            }\n        })\n    }\n}\n\n/// A function that is applied to the text value before setting `document.title`.\n#[repr(transparent)]\npub struct Formatter(Box<dyn Fn(String) -> String + Send + Sync>);\n\nimpl<F> From<F> for Formatter\nwhere\n    F: Fn(String) -> String + Send + Sync + 'static,\n{\n    #[inline(always)]\n    fn from(f: F) -> Formatter {\n        Formatter(Box::new(f))\n    }\n}\n\n/// A component to set the document’s title by creating an [`HTMLTitleElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTitleElement).\n///\n/// The `title` and `formatter` can be set independently of one another. For example, you can create a root-level\n/// `<Title formatter=.../>` that will wrap each of the text values of `<Title/>` components created lower in the tree.\n///\n/// ```\n/// use leptos::prelude::*;\n/// use leptos_meta::*;\n///\n/// #[component]\n/// fn MyApp() -> impl IntoView {\n///     provide_meta_context();\n///     let formatter = |text| format!(\"{text} — Leptos Online\");\n///\n///     view! {\n///       <main>\n///         <Title formatter/>\n///         // ... routing logic here\n///       </main>\n///     }\n/// }\n///\n/// #[component]\n/// fn PageA() -> impl IntoView {\n///     view! {\n///       <main>\n///         <Title text=\"Page A\"/> // sets title to \"Page A — Leptos Online\"\n///       </main>\n///     }\n/// }\n///\n/// #[component]\n/// fn PageB() -> impl IntoView {\n///     view! {\n///       <main>\n///         <Title text=\"Page B\"/> // sets title to \"Page B — Leptos Online\"\n///       </main>\n///     }\n/// }\n/// ```\n#[component]\npub fn Title(\n    /// A function that will be applied to any text value before it’s set as the title.\n    #[prop(optional, into)]\n    mut formatter: Option<Formatter>,\n    /// Sets the current `document.title`.\n    #[prop(optional, into)]\n    mut text: Option<TextProp>,\n) -> impl IntoView {\n    let meta = use_head();\n    let server_ctx = use_context::<ServerMetaContext>();\n    let id = meta.title.next_id();\n    if let Some(cx) = server_ctx {\n        // if we are server rendering, we will not actually use these values via RenderHtml\n        // instead, they'll be handled separately by the server integration\n        // so it's safe to take them out of the props here\n        cx.title\n            .push_text_and_formatter(id, text.take(), formatter.take());\n    };\n\n    TitleView {\n        id,\n        meta,\n        formatter,\n        text,\n    }\n}\n\nstruct TitleView {\n    id: u32,\n    meta: MetaContext,\n    formatter: Option<Formatter>,\n    text: Option<TextProp>,\n}\n\nstruct TitleViewState {\n    id: TitleId,\n    meta: MetaContext,\n    // these are only Some(_) after being unmounted, and hold these values until dropped or remounted\n    formatter: Option<Formatter>,\n    text: Option<TextProp>,\n}\n\nimpl Drop for TitleViewState {\n    fn drop(&mut self) {\n        // when TitleViewState is dropped, it should remove its ID from the text and formatter stacks\n        // so that they no longer appear. it will also revalidate the whole title in case this one was active\n        self.meta.title.remove_id(self.id);\n    }\n}\n\nimpl Render for TitleView {\n    type State = TitleViewState;\n\n    fn build(self) -> Self::State {\n        let TitleView {\n            id,\n            meta,\n            formatter,\n            text,\n        } = self;\n        meta.title.spawn_effect();\n        TitleViewState {\n            id,\n            meta,\n            text,\n            formatter,\n        }\n    }\n\n    fn rebuild(self, _state: &mut Self::State) {\n        self.meta.title.update_text_and_formatter(\n            self.id,\n            self.text,\n            self.formatter,\n        );\n    }\n}\n\nimpl AddAnyAttr for TitleView {\n    type Output<SomeNewAttr: Attribute> = TitleView;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        self\n    }\n}\n\nimpl RenderHtml for TitleView {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0;\n    const EXISTS: bool = false;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        _buf: &mut String,\n        _position: &mut Position,\n        _escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // meta tags are rendered into the buffer stored into the context\n        // the value has already been taken out, when we're on the server\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _cursor: &Cursor,\n        _position: &PositionState,\n    ) -> Self::State {\n        let TitleView {\n            id,\n            meta,\n            formatter,\n            text,\n        } = self;\n        meta.title.spawn_effect();\n        // these need to be pushed here, rather than on mount, because mount() is not called when hydrating\n        meta.title.push_text_and_formatter(id, text, formatter);\n        TitleViewState {\n            id,\n            meta,\n            text: None,\n            formatter: None,\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl Mountable for TitleViewState {\n    fn unmount(&mut self) {\n        let (text, formatter) = self.meta.title.remove_id(self.id);\n        if text.is_some() {\n            self.text = text;\n        }\n        if formatter.is_some() {\n            self.formatter = formatter;\n        }\n    }\n\n    fn mount(\n        &mut self,\n        _parent: &leptos::tachys::renderer::types::Element,\n        _marker: Option<&leptos::tachys::renderer::types::Node>,\n    ) {\n        // TitleView::el() guarantees that there is a <title> in the <head>\n        // so there is no element to be mounted\n        //\n        // \"mounting\" in this case means that we actually want this title to be in active use\n        // as a result, we will push it into the title stack and revalidate\n        self.meta.title.push_text_and_formatter(\n            self.id,\n            self.text.take(),\n            self.formatter.take(),\n        );\n    }\n\n    fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {\n        false\n    }\n\n    fn elements(&self) -> Vec<leptos::tachys::renderer::types::Element> {\n        vec![]\n    }\n}\n"
  },
  {
    "path": "next_tuple/Cargo.toml",
    "content": "[package]\nname = \"next_tuple\"\nversion = \"0.1.0\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"A trait to build and extend tuples.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\n"
  },
  {
    "path": "next_tuple/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "next_tuple/README.md",
    "content": "Allows extending a tuple, or creating a new tuple, by adding the next value.\n"
  },
  {
    "path": "next_tuple/src/lib.rs",
    "content": "//! Defines a trait that allows you to extend a tuple, by returning\n//! a new tuple with an element of an arbitrary type added.\n\n#![no_std]\n#![allow(non_snake_case)]\n#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\n/// Allows extending a tuple, or creating a new tuple, by adding the next value.\npub trait NextTuple {\n    /// The type that will be returned by adding another value of type `Next` to the end of the current type.\n    type Output<Next>;\n\n    /// Adds the next value and returns the result.\n    fn next_tuple<Next>(self, next: Next) -> Self::Output<Next>;\n}\n\nmacro_rules! impl_tuple_builder {\n    ($($ty:ident),*) => {\n\t\timpl<$($ty),*> NextTuple for ($($ty,)*) {\n\t\t\ttype Output<Next> = ($($ty,)* Next);\n\n\t\t\tfn next_tuple<Next>(self, next: Next) -> Self::Output<Next> {\n\t\t\t\tlet ($($ty,)*) = self;\n\t\t\t\t($($ty,)* next)\n\t\t\t}\n\t\t}\n    };\n}\n\nimpl NextTuple for () {\n    type Output<Next> = (Next,);\n\n    fn next_tuple<Next>(self, next: Next) -> Self::Output<Next> {\n        (next,)\n    }\n}\n\nimpl_tuple_builder!(A);\nimpl_tuple_builder!(A, B);\nimpl_tuple_builder!(A, B, C);\nimpl_tuple_builder!(A, B, C, D);\nimpl_tuple_builder!(A, B, C, D, E);\nimpl_tuple_builder!(A, B, C, D, E, F);\nimpl_tuple_builder!(A, B, C, D, E, F, G);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);\nimpl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);\nimpl_tuple_builder!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U\n);\nimpl_tuple_builder!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V\n);\nimpl_tuple_builder!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W\n);\nimpl_tuple_builder!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X\n);\nimpl_tuple_builder!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y\n);\nimpl_tuple_builder!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,\n    Z\n);\n"
  },
  {
    "path": "oco/Cargo.toml",
    "content": "[package]\nname = \"oco_ref\"\nversion = \"0.2.1\"\nauthors = [\"Danik Vitek\", \"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"A smart pointer for storing immutable values with relatively-cheap cloning. (Like a `Cow` meets an `Rc`!)\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nserde = { workspace = true, default-features = true }\nthiserror = { workspace = true, default-features = true }\n\n[dev-dependencies]\nserde_json = { workspace = true, default-features = true }\n"
  },
  {
    "path": "oco/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "oco/README.md",
    "content": "This module contains the `Oco` (Owned Clones Once) smart pointer,\nwhich is used to store immutable references to values.\nThis is useful for storing, for example, strings.\n\nImagine this as an alternative to [`Cow`] with an additional, reference-counted\nbranch.\n\n```rust\nuse oco_ref::Oco;\nuse std::sync::Arc;\n\nlet static_str = \"foo\";\nlet arc_str: Arc<str> = \"bar\".into();\nlet owned_str: String = \"baz\".into();\n\nfn uses_oco(value: impl Into<Oco<'static, str>>) {\n    let mut value = value.into();\n\n    // ensures that the value is either a reference, or reference-counted\n    // O(n) at worst\n    let clone1 = value.clone_inplace();\n\n    // these subsequent clones are O(1)\n    let clone2 = value.clone();\n    let clone3 = value.clone();\n}\n\nuses_oco(static_str);\nuses_oco(arc_str);\nuses_oco(owned_str);\n```\n"
  },
  {
    "path": "oco/src/lib.rs",
    "content": "//! which is used to store immutable references to values.\n//! This is useful for storing, for example, strings.\n//!\n//! Imagine this as an alternative to [`Cow`] with an additional, reference-counted\n//! branch.\n//!\n//! ```rust\n//! use oco_ref::Oco;\n//! use std::sync::Arc;\n//!\n//! let static_str = \"foo\";\n//! let rc_str: Arc<str> = \"bar\".into();\n//! let owned_str: String = \"baz\".into();\n//!\n//! fn uses_oco(value: impl Into<Oco<'static, str>>) {\n//!     let mut value = value.into();\n//!\n//!     // ensures that the value is either a reference, or reference-counted\n//!     // O(n) at worst\n//!     let clone1 = value.clone_inplace();\n//!\n//!     // these subsequent clones are O(1)\n//!     let clone2 = value.clone();\n//!     let clone3 = value.clone();\n//! }\n//!\n//! uses_oco(static_str);\n//! uses_oco(rc_str);\n//! uses_oco(owned_str);\n//! ```\n\n#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\nuse serde::{de::DeserializeOwned, Deserialize, Serialize};\nuse std::{\n    borrow::{Borrow, Cow},\n    ffi::{CStr, OsStr},\n    fmt,\n    hash::Hash,\n    ops::{Add, Deref},\n    path::Path,\n    sync::Arc,\n};\n\n/// \"Owned Clones Once\": a smart pointer that can be either a reference,\n/// an owned value, or a reference-counted pointer. This is useful for\n/// storing immutable values, such as strings, in a way that is cheap to\n/// clone and pass around.\n///\n/// The cost of the `Clone` implementation depends on the branch.  Cloning the [`Oco::Borrowed`]\n/// variant simply copies the references (`O(1)`). Cloning the [`Oco::Counted`]\n/// variant increments a reference count (`O(1)`). Cloning the [`Oco::Owned`]\n/// variant requires an `O(n)` clone of the data.\n///\n/// For an amortized `O(1)` clone, you can use [`Oco::clone_inplace()`]. Using this method,\n/// [`Oco::Borrowed`] and [`Oco::Counted`] are still `O(1)`. [`Oco::Owned`] does a single `O(n)`\n/// clone, but converts the object to the [`Oco::Counted`] branch, which means future clones will\n/// be `O(1)`.\n///\n/// In general, you'll either want to call `clone_inplace()` once, before sharing the `Oco` with\n/// other parts of your application (so that all future clones are `O(1)`), or simply use this as\n/// if it is a [`Cow`] with an additional branch for reference-counted values.\npub enum Oco<'a, T: ?Sized + ToOwned + 'a> {\n    /// A static reference to a value.\n    Borrowed(&'a T),\n    /// A reference counted pointer to a value.\n    Counted(Arc<T>),\n    /// An owned value.\n    Owned(<T as ToOwned>::Owned),\n}\n\nimpl<T: ?Sized + ToOwned> Oco<'_, T> {\n    /// Converts the value into an owned value.\n    pub fn into_owned(self) -> <T as ToOwned>::Owned {\n        match self {\n            Oco::Borrowed(v) => v.to_owned(),\n            Oco::Counted(v) => v.as_ref().to_owned(),\n            Oco::Owned(v) => v,\n        }\n    }\n\n    /// Checks if the value is [`Oco::Borrowed`].\n    /// # Examples\n    /// ```\n    /// # use std::sync::Arc;\n    /// # use oco_ref::Oco;\n    /// assert!(Oco::<str>::Borrowed(\"Hello\").is_borrowed());\n    /// assert!(!Oco::<str>::Counted(Arc::from(\"Hello\")).is_borrowed());\n    /// assert!(!Oco::<str>::Owned(\"Hello\".to_string()).is_borrowed());\n    /// ```\n    pub const fn is_borrowed(&self) -> bool {\n        matches!(self, Oco::Borrowed(_))\n    }\n\n    /// Checks if the value is [`Oco::Counted`].\n    /// # Examples\n    /// ```\n    /// # use std::sync::Arc;\n    /// # use oco_ref::Oco;\n    /// assert!(Oco::<str>::Counted(Arc::from(\"Hello\")).is_counted());\n    /// assert!(!Oco::<str>::Borrowed(\"Hello\").is_counted());\n    /// assert!(!Oco::<str>::Owned(\"Hello\".to_string()).is_counted());\n    /// ```\n    pub const fn is_counted(&self) -> bool {\n        matches!(self, Oco::Counted(_))\n    }\n\n    /// Checks if the value is [`Oco::Owned`].\n    /// # Examples\n    /// ```\n    /// # use std::sync::Arc;\n    /// # use oco_ref::Oco;\n    /// assert!(Oco::<str>::Owned(\"Hello\".to_string()).is_owned());\n    /// assert!(!Oco::<str>::Borrowed(\"Hello\").is_owned());\n    /// assert!(!Oco::<str>::Counted(Arc::from(\"Hello\")).is_owned());\n    /// ```\n    pub const fn is_owned(&self) -> bool {\n        matches!(self, Oco::Owned(_))\n    }\n}\n\nimpl<T: ?Sized + ToOwned> Deref for Oco<'_, T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        match self {\n            Oco::Borrowed(v) => v,\n            Oco::Owned(v) => v.borrow(),\n            Oco::Counted(v) => v,\n        }\n    }\n}\n\nimpl<T: ?Sized + ToOwned> Borrow<T> for Oco<'_, T> {\n    #[inline(always)]\n    fn borrow(&self) -> &T {\n        self.deref()\n    }\n}\n\nimpl<T: ?Sized + ToOwned> AsRef<T> for Oco<'_, T> {\n    #[inline(always)]\n    fn as_ref(&self) -> &T {\n        self.deref()\n    }\n}\n\nimpl AsRef<Path> for Oco<'_, str> {\n    #[inline(always)]\n    fn as_ref(&self) -> &Path {\n        self.as_str().as_ref()\n    }\n}\n\nimpl AsRef<Path> for Oco<'_, OsStr> {\n    #[inline(always)]\n    fn as_ref(&self) -> &Path {\n        self.as_os_str().as_ref()\n    }\n}\n\n// --------------------------------------\n// pub fn as_{slice}(&self) -> &{slice}\n// --------------------------------------\n\nimpl Oco<'_, str> {\n    /// Returns a `&str` slice of this [`Oco`].\n    /// # Examples\n    /// ```\n    /// # use oco_ref::Oco;\n    /// let oco = Oco::<str>::Borrowed(\"Hello\");\n    /// let s: &str = oco.as_str();\n    /// assert_eq!(s, \"Hello\");\n    /// ```\n    #[inline(always)]\n    pub fn as_str(&self) -> &str {\n        self\n    }\n}\n\nimpl Oco<'_, CStr> {\n    /// Returns a `&CStr` slice of this [`Oco`].\n    /// # Examples\n    /// ```\n    /// # use oco_ref::Oco;\n    /// use std::ffi::CStr;\n    ///\n    /// let oco =\n    ///     Oco::<CStr>::Borrowed(CStr::from_bytes_with_nul(b\"Hello\\0\").unwrap());\n    /// let s: &CStr = oco.as_c_str();\n    /// assert_eq!(s, CStr::from_bytes_with_nul(b\"Hello\\0\").unwrap());\n    /// ```\n    #[inline(always)]\n    pub fn as_c_str(&self) -> &CStr {\n        self\n    }\n}\n\nimpl Oco<'_, OsStr> {\n    /// Returns a `&OsStr` slice of this [`Oco`].\n    /// # Examples\n    /// ```\n    /// # use oco_ref::Oco;\n    /// use std::ffi::OsStr;\n    ///\n    /// let oco = Oco::<OsStr>::Borrowed(OsStr::new(\"Hello\"));\n    /// let s: &OsStr = oco.as_os_str();\n    /// assert_eq!(s, OsStr::new(\"Hello\"));\n    /// ```\n    #[inline(always)]\n    pub fn as_os_str(&self) -> &OsStr {\n        self\n    }\n}\n\nimpl Oco<'_, Path> {\n    /// Returns a `&Path` slice of this [`Oco`].\n    /// # Examples\n    /// ```\n    /// # use oco_ref::Oco;\n    /// use std::path::Path;\n    ///\n    /// let oco = Oco::<Path>::Borrowed(Path::new(\"Hello\"));\n    /// let s: &Path = oco.as_path();\n    /// assert_eq!(s, Path::new(\"Hello\"));\n    /// ```\n    #[inline(always)]\n    pub fn as_path(&self) -> &Path {\n        self\n    }\n}\n\nimpl<T> Oco<'_, [T]>\nwhere\n    [T]: ToOwned,\n{\n    /// Returns a `&[T]` slice of this [`Oco`].\n    /// # Examples\n    /// ```\n    /// # use oco_ref::Oco;\n    /// let oco = Oco::<[u8]>::Borrowed(b\"Hello\");\n    /// let s: &[u8] = oco.as_slice();\n    /// assert_eq!(s, b\"Hello\");\n    /// ```\n    #[inline(always)]\n    pub fn as_slice(&self) -> &[T] {\n        self\n    }\n}\n\nimpl<'a, T> Clone for Oco<'a, T>\nwhere\n    T: ?Sized + ToOwned + 'a,\n    for<'b> Arc<T>: From<&'b T>,\n{\n    /// Returns a new [`Oco`] with the same value as this one.\n    /// If the value is [`Oco::Owned`], this will convert it into\n    /// [`Oco::Counted`], so that the next clone will be O(1).\n    /// # Examples\n    /// [`String`] :\n    /// ```\n    /// # use oco_ref::Oco;\n    /// let oco = Oco::<str>::Owned(\"Hello\".to_string());\n    /// let oco2 = oco.clone();\n    /// assert_eq!(oco, oco2);\n    /// assert!(oco2.is_counted());\n    /// ```\n    /// [`Vec`] :\n    /// ```\n    /// # use oco_ref::Oco;\n    /// let oco = Oco::<[u8]>::Owned(b\"Hello\".to_vec());\n    /// let oco2 = oco.clone();\n    /// assert_eq!(oco, oco2);\n    /// assert!(oco2.is_counted());\n    /// ```\n    fn clone(&self) -> Self {\n        match self {\n            Self::Borrowed(v) => Self::Borrowed(v),\n            Self::Counted(v) => Self::Counted(Arc::clone(v)),\n            Self::Owned(v) => Self::Counted(Arc::from(v.borrow())),\n        }\n    }\n}\n\nimpl<'a, T> Oco<'a, T>\nwhere\n    T: ?Sized + ToOwned + 'a,\n    for<'b> Arc<T>: From<&'b T>,\n{\n    /// Upgrades the value in place, by converting into [`Oco::Counted`] if it\n    /// was previously [`Oco::Owned`].\n    /// # Examples\n    /// ```\n    /// # use oco_ref::Oco;\n    /// let mut oco1 = Oco::<str>::Owned(\"Hello\".to_string());\n    /// assert!(oco1.is_owned());\n    /// oco1.upgrade_inplace();\n    /// assert!(oco1.is_counted());\n    /// ```\n    pub fn upgrade_inplace(&mut self) {\n        if let Self::Owned(v) = &*self {\n            let rc = Arc::from(v.borrow());\n            *self = Self::Counted(rc);\n        }\n    }\n\n    /// Clones the value with inplace conversion into [`Oco::Counted`] if it\n    /// was previously [`Oco::Owned`].\n    /// # Examples\n    /// ```\n    /// # use oco_ref::Oco;\n    /// let mut oco1 = Oco::<str>::Owned(\"Hello\".to_string());\n    /// let oco2 = oco1.clone_inplace();\n    /// assert_eq!(oco1, oco2);\n    /// assert!(oco1.is_counted());\n    /// assert!(oco2.is_counted());\n    /// ```\n    pub fn clone_inplace(&mut self) -> Self {\n        match &*self {\n            Self::Borrowed(v) => Self::Borrowed(v),\n            Self::Counted(v) => Self::Counted(Arc::clone(v)),\n            Self::Owned(v) => {\n                let rc = Arc::from(v.borrow());\n                *self = Self::Counted(rc.clone());\n                Self::Counted(rc)\n            }\n        }\n    }\n}\n\nimpl<T: ?Sized> Default for Oco<'_, T>\nwhere\n    T: ToOwned,\n    T::Owned: Default,\n{\n    fn default() -> Self {\n        Oco::Owned(T::Owned::default())\n    }\n}\n\nimpl<'b, A: ?Sized, B: ?Sized> PartialEq<Oco<'b, B>> for Oco<'_, A>\nwhere\n    A: PartialEq<B>,\n    A: ToOwned,\n    B: ToOwned,\n{\n    fn eq(&self, other: &Oco<'b, B>) -> bool {\n        **self == **other\n    }\n}\n\nimpl<T: ?Sized + ToOwned + Eq> Eq for Oco<'_, T> {}\n\nimpl<'b, A: ?Sized, B: ?Sized> PartialOrd<Oco<'b, B>> for Oco<'_, A>\nwhere\n    A: PartialOrd<B>,\n    A: ToOwned,\n    B: ToOwned,\n{\n    fn partial_cmp(&self, other: &Oco<'b, B>) -> Option<std::cmp::Ordering> {\n        (**self).partial_cmp(&**other)\n    }\n}\n\nimpl<T: ?Sized + Ord> Ord for Oco<'_, T>\nwhere\n    T: ToOwned,\n{\n    fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n        (**self).cmp(&**other)\n    }\n}\n\nimpl<T: ?Sized + Hash> Hash for Oco<'_, T>\nwhere\n    T: ToOwned,\n{\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        (**self).hash(state)\n    }\n}\n\nimpl<T: ?Sized + fmt::Debug> fmt::Debug for Oco<'_, T>\nwhere\n    T: ToOwned,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        (**self).fmt(f)\n    }\n}\n\nimpl<T: ?Sized + fmt::Display> fmt::Display for Oco<'_, T>\nwhere\n    T: ToOwned,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        (**self).fmt(f)\n    }\n}\n\nimpl<'a, T: ?Sized> From<&'a T> for Oco<'a, T>\nwhere\n    T: ToOwned,\n{\n    fn from(v: &'a T) -> Self {\n        Oco::Borrowed(v)\n    }\n}\n\nimpl<'a, T: ?Sized> From<Cow<'a, T>> for Oco<'a, T>\nwhere\n    T: ToOwned,\n{\n    fn from(v: Cow<'a, T>) -> Self {\n        match v {\n            Cow::Borrowed(v) => Oco::Borrowed(v),\n            Cow::Owned(v) => Oco::Owned(v),\n        }\n    }\n}\n\nimpl<'a, T: ?Sized> From<Oco<'a, T>> for Cow<'a, T>\nwhere\n    T: ToOwned,\n{\n    fn from(value: Oco<'a, T>) -> Self {\n        match value {\n            Oco::Borrowed(v) => Cow::Borrowed(v),\n            Oco::Owned(v) => Cow::Owned(v),\n            Oco::Counted(v) => Cow::Owned(v.as_ref().to_owned()),\n        }\n    }\n}\n\nimpl<T: ?Sized> From<Arc<T>> for Oco<'_, T>\nwhere\n    T: ToOwned,\n{\n    fn from(v: Arc<T>) -> Self {\n        Oco::Counted(v)\n    }\n}\n\nimpl<T: ?Sized> From<Box<T>> for Oco<'_, T>\nwhere\n    T: ToOwned,\n{\n    fn from(v: Box<T>) -> Self {\n        Oco::Counted(v.into())\n    }\n}\n\nimpl From<String> for Oco<'_, str> {\n    fn from(v: String) -> Self {\n        Oco::Owned(v)\n    }\n}\n\nimpl From<Oco<'_, str>> for String {\n    fn from(v: Oco<'_, str>) -> Self {\n        match v {\n            Oco::Borrowed(v) => v.to_owned(),\n            Oco::Counted(v) => v.as_ref().to_owned(),\n            Oco::Owned(v) => v,\n        }\n    }\n}\n\nimpl<T> From<Vec<T>> for Oco<'_, [T]>\nwhere\n    [T]: ToOwned<Owned = Vec<T>>,\n{\n    fn from(v: Vec<T>) -> Self {\n        Oco::Owned(v)\n    }\n}\n\nimpl<'a, T, const N: usize> From<&'a [T; N]> for Oco<'a, [T]>\nwhere\n    [T]: ToOwned,\n{\n    fn from(v: &'a [T; N]) -> Self {\n        Oco::Borrowed(v)\n    }\n}\n\nimpl<'a> From<Oco<'a, str>> for Oco<'a, [u8]> {\n    fn from(v: Oco<'a, str>) -> Self {\n        match v {\n            Oco::Borrowed(v) => Oco::Borrowed(v.as_bytes()),\n            Oco::Owned(v) => Oco::Owned(v.into_bytes()),\n            Oco::Counted(v) => Oco::Counted(v.into()),\n        }\n    }\n}\n\n/// Error returned from `Oco::try_from` for unsuccessful\n/// conversion from `Oco<'_, [u8]>` to `Oco<'_, str>`.\n#[derive(Debug, Clone, thiserror::Error)]\n#[error(\"invalid utf-8 sequence: {_0}\")]\npub enum FromUtf8Error {\n    /// Error for conversion of [`Oco::Borrowed`] and [`Oco::Counted`] variants\n    /// (`&[u8]` to `&str`).\n    #[error(\"{_0}\")]\n    StrFromBytes(\n        #[source]\n        #[from]\n        std::str::Utf8Error,\n    ),\n    /// Error for conversion of [`Oco::Owned`] variant (`Vec<u8>` to `String`).\n    #[error(\"{_0}\")]\n    StringFromBytes(\n        #[source]\n        #[from]\n        std::string::FromUtf8Error,\n    ),\n}\n\nmacro_rules! impl_slice_eq {\n    ([$($g:tt)*] $((where $($w:tt)+))?, $lhs:ty, $rhs: ty) => {\n        impl<$($g)*> PartialEq<$rhs> for $lhs\n        $(where\n            $($w)*)?\n        {\n            #[inline]\n            fn eq(&self, other: &$rhs) -> bool {\n                PartialEq::eq(&self[..], &other[..])\n            }\n        }\n\n        impl<$($g)*> PartialEq<$lhs> for $rhs\n        $(where\n            $($w)*)?\n        {\n            #[inline]\n            fn eq(&self, other: &$lhs) -> bool {\n                PartialEq::eq(&self[..], &other[..])\n            }\n        }\n    };\n}\n\nimpl_slice_eq!([], Oco<'_, str>, str);\nimpl_slice_eq!(['a, 'b], Oco<'a, str>, &'b str);\nimpl_slice_eq!([], Oco<'_, str>, String);\nimpl_slice_eq!(['a, 'b], Oco<'a, str>, Cow<'b, str>);\n\nimpl_slice_eq!([T: PartialEq] (where [T]: ToOwned), Oco<'_, [T]>, [T]);\nimpl_slice_eq!(['a, 'b, T: PartialEq] (where [T]: ToOwned), Oco<'a, [T]>, &'b [T]);\nimpl_slice_eq!([T: PartialEq] (where [T]: ToOwned), Oco<'_, [T]>, Vec<T>);\nimpl_slice_eq!(['a, 'b, T: PartialEq] (where [T]: ToOwned), Oco<'a, [T]>, Cow<'b, [T]>);\n\nimpl<'b> Add<&'b str> for Oco<'_, str> {\n    type Output = Oco<'static, str>;\n\n    fn add(self, rhs: &'b str) -> Self::Output {\n        Oco::Owned(String::from(self) + rhs)\n    }\n}\n\nimpl<'b> Add<Cow<'b, str>> for Oco<'_, str> {\n    type Output = Oco<'static, str>;\n\n    fn add(self, rhs: Cow<'b, str>) -> Self::Output {\n        Oco::Owned(String::from(self) + rhs.as_ref())\n    }\n}\n\nimpl<'b> Add<Oco<'b, str>> for Oco<'_, str> {\n    type Output = Oco<'static, str>;\n\n    fn add(self, rhs: Oco<'b, str>) -> Self::Output {\n        Oco::Owned(String::from(self) + rhs.as_ref())\n    }\n}\n\nimpl<'a> FromIterator<Oco<'a, str>> for String {\n    fn from_iter<T: IntoIterator<Item = Oco<'a, str>>>(iter: T) -> Self {\n        iter.into_iter().fold(String::new(), |mut acc, item| {\n            acc.push_str(item.as_ref());\n            acc\n        })\n    }\n}\n\nimpl<'a, T> Deserialize<'a> for Oco<'static, T>\nwhere\n    T: ?Sized + ToOwned + 'a,\n    T::Owned: DeserializeOwned,\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'a>,\n    {\n        <T::Owned>::deserialize(deserializer).map(Oco::Owned)\n    }\n}\n\nimpl<'a, T> Serialize for Oco<'a, T>\nwhere\n    T: ?Sized + ToOwned + 'a,\n    for<'b> &'b T: Serialize,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.as_ref().serialize(serializer)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn debug_fmt_should_display_quotes_for_strings() {\n        let s: Oco<str> = Oco::Borrowed(\"hello\");\n        assert_eq!(format!(\"{s:?}\"), \"\\\"hello\\\"\");\n        let s: Oco<str> = Oco::Counted(Arc::from(\"hello\"));\n        assert_eq!(format!(\"{s:?}\"), \"\\\"hello\\\"\");\n    }\n\n    #[test]\n    fn partial_eq_should_compare_str_to_str() {\n        let s: Oco<str> = Oco::Borrowed(\"hello\");\n        assert_eq!(s, \"hello\");\n        assert_eq!(\"hello\", s);\n        assert_eq!(s, String::from(\"hello\"));\n        assert_eq!(String::from(\"hello\"), s);\n        assert_eq!(s, Cow::from(\"hello\"));\n        assert_eq!(Cow::from(\"hello\"), s);\n    }\n\n    #[test]\n    fn partial_eq_should_compare_slice_to_slice() {\n        let s: Oco<[i32]> = Oco::Borrowed([1, 2, 3].as_slice());\n        assert_eq!(s, [1, 2, 3].as_slice());\n        assert_eq!([1, 2, 3].as_slice(), s);\n        assert_eq!(s, vec![1, 2, 3]);\n        assert_eq!(vec![1, 2, 3], s);\n        assert_eq!(s, Cow::<'_, [i32]>::Borrowed(&[1, 2, 3]));\n        assert_eq!(Cow::<'_, [i32]>::Borrowed(&[1, 2, 3]), s);\n    }\n\n    #[test]\n    fn add_should_concatenate_strings() {\n        let s: Oco<str> = Oco::Borrowed(\"hello\");\n        assert_eq!(s.clone() + \" world\", \"hello world\");\n        assert_eq!(s.clone() + Cow::from(\" world\"), \"hello world\");\n        assert_eq!(s + Oco::from(\" world\"), \"hello world\");\n    }\n\n    #[test]\n    fn as_str_should_return_a_str() {\n        let s: Oco<str> = Oco::Borrowed(\"hello\");\n        assert_eq!(s.as_str(), \"hello\");\n        let s: Oco<str> = Oco::Counted(Arc::from(\"hello\"));\n        assert_eq!(s.as_str(), \"hello\");\n    }\n\n    #[test]\n    fn as_slice_should_return_a_slice() {\n        let s: Oco<[i32]> = Oco::Borrowed([1, 2, 3].as_slice());\n        assert_eq!(s.as_slice(), [1, 2, 3].as_slice());\n        let s: Oco<[i32]> = Oco::Counted(Arc::from([1, 2, 3]));\n        assert_eq!(s.as_slice(), [1, 2, 3].as_slice());\n    }\n\n    #[test]\n    fn default_for_str_should_return_an_empty_string() {\n        let s: Oco<str> = Default::default();\n        assert!(s.is_empty());\n    }\n\n    #[test]\n    fn default_for_slice_should_return_an_empty_slice() {\n        let s: Oco<[i32]> = Default::default();\n        assert!(s.is_empty());\n    }\n\n    #[test]\n    fn default_for_any_option_should_return_none() {\n        let s: Oco<Option<i32>> = Default::default();\n        assert!(s.is_none());\n    }\n\n    #[test]\n    fn cloned_owned_string_should_make_counted_str() {\n        let s: Oco<str> = Oco::Owned(String::from(\"hello\"));\n        assert!(s.clone().is_counted());\n    }\n\n    #[test]\n    fn cloned_borrowed_str_should_make_borrowed_str() {\n        let s: Oco<str> = Oco::Borrowed(\"hello\");\n        assert!(s.clone().is_borrowed());\n    }\n\n    #[test]\n    fn cloned_counted_str_should_make_counted_str() {\n        let s: Oco<str> = Oco::Counted(Arc::from(\"hello\"));\n        assert!(s.clone().is_counted());\n    }\n\n    #[test]\n    fn cloned_inplace_owned_string_should_make_counted_str_and_become_counted()\n    {\n        let mut s: Oco<str> = Oco::Owned(String::from(\"hello\"));\n        assert!(s.clone_inplace().is_counted());\n        assert!(s.is_counted());\n    }\n\n    #[test]\n    fn cloned_inplace_borrowed_str_should_make_borrowed_str_and_remain_borrowed(\n    ) {\n        let mut s: Oco<str> = Oco::Borrowed(\"hello\");\n        assert!(s.clone_inplace().is_borrowed());\n        assert!(s.is_borrowed());\n    }\n\n    #[test]\n    fn cloned_inplace_counted_str_should_make_counted_str_and_remain_counted() {\n        let mut s: Oco<str> = Oco::Counted(Arc::from(\"hello\"));\n        assert!(s.clone_inplace().is_counted());\n        assert!(s.is_counted());\n    }\n\n    #[test]\n    fn serialization_works() {\n        let s = serde_json::to_string(&Oco::Borrowed(\"foo\"))\n            .expect(\"should serialize string\");\n        assert_eq!(s, \"\\\"foo\\\"\");\n    }\n\n    #[test]\n    fn deserialization_works() {\n        let s: Oco<str> = serde_json::from_str(\"\\\"bar\\\"\")\n            .expect(\"should deserialize from string\");\n        assert_eq!(s, Oco::from(String::from(\"bar\")));\n    }\n}\n"
  },
  {
    "path": "or_poisoned/Cargo.toml",
    "content": "[package]\nname = \"or_poisoned\"\nversion = \"0.1.0\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Unwrap std lock guards in a semantic way.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\n"
  },
  {
    "path": "or_poisoned/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "or_poisoned/README.md",
    "content": "Provides a simple trait that unwraps the locks provide by [`std::sync::RwLock`].\n\nIn every case, this is the same as calling `.expect(\"lock poisoned\")`. However, it\ndoes not use `.unwrap()` or `.expect()`, which makes it easier to distinguish from\nother forms of unwrapping when reading code.\n\n```rust\nuse or_poisoned::OrPoisoned;\nuse std::sync::RwLock;\n\nlet lock = RwLock::new(String::from(\"Hello!\"));\n\nlet read = lock.read().or_poisoned();\n// this is identical to\nlet read = lock.read().unwrap();\n```\n"
  },
  {
    "path": "or_poisoned/src/lib.rs",
    "content": "//! Provides a simple trait that unwraps the locks provide by [`std::sync::RwLock`].\n//!\n//! In every case, this is the same as calling `.expect(\"lock poisoned\")`. However, it\n//! does not use `.unwrap()` or `.expect()`, which makes it easier to distinguish from\n//! other forms of unwrapping when reading code.\n//!\n//! ```rust\n//! use or_poisoned::OrPoisoned;\n//! use std::sync::RwLock;\n//!\n//! let lock = RwLock::new(String::from(\"Hello!\"));\n//!\n//! let read = lock.read().or_poisoned();\n//! // this is identical to\n//! let read = lock.read().unwrap();\n//! ```\n\n#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\nuse std::sync::{\n    LockResult, MutexGuard, PoisonError, RwLockReadGuard, RwLockWriteGuard,\n};\n\n/// Unwraps a lock.\npub trait OrPoisoned {\n    /// The inner guard type.\n    type Inner;\n\n    /// Unwraps the lock.\n    ///\n    /// ## Panics\n    ///\n    /// Will panic if the lock is poisoned.\n    fn or_poisoned(self) -> Self::Inner;\n}\n\nimpl<'a, T: ?Sized> OrPoisoned\n    for Result<RwLockReadGuard<'a, T>, PoisonError<RwLockReadGuard<'a, T>>>\n{\n    type Inner = RwLockReadGuard<'a, T>;\n\n    fn or_poisoned(self) -> Self::Inner {\n        self.expect(\"lock poisoned\")\n    }\n}\n\nimpl<'a, T: ?Sized> OrPoisoned\n    for Result<RwLockWriteGuard<'a, T>, PoisonError<RwLockWriteGuard<'a, T>>>\n{\n    type Inner = RwLockWriteGuard<'a, T>;\n\n    fn or_poisoned(self) -> Self::Inner {\n        self.expect(\"lock poisoned\")\n    }\n}\n\nimpl<'a, T: ?Sized> OrPoisoned for LockResult<MutexGuard<'a, T>> {\n    type Inner = MutexGuard<'a, T>;\n\n    fn or_poisoned(self) -> Self::Inner {\n        self.expect(\"lock poisoned\")\n    }\n}\n"
  },
  {
    "path": "projects/README.md",
    "content": "# `projects` README\n\nThe `projects` directory is intended as a collective of medium-to-large-scale examples: a place to show a variety of use cases and integrations between Leptos and other libraries. Over time, our hope is that this allows us to showcase a wider variety of user examples, without the main `examples` directory becoming too overwhelming to be useful.\n\nThe `examples` directory is included in our CI, and examples are regularly linted and tested. The barrier to entry for the `projects` directory is intended to be lower: Example projects will generally be built against a particular version, and not regularly linted or updated. Hopefully this distinction allows us to accept more examples without worrying about the maintenance burden of constant updates.\n\nFeel free to submit projects to this directory via PR!\n\n\n## Index\n\n### meilisearch-searchbar \n[Meilisearch](https://www.meilisearch.com/) is a search engine built in Rust that you can self-host. This example shows how to run it alongside a leptos server and present a search bar with autocomplete to the user.\n\n### nginx-mpmc \n[Nginx](https://nginx.org/) Multiple Producer Multi Consumer, this example shows how you can use Nginx to provide different clients to the user while running multiple Leptos servers that provide server functions to any of the clients.\n\n### ory-kratos \n[Ory](https://www.ory.sh/docs/welcome) is a combination of different authorization services. Ory Kratos is their Identification service, which provides password storage, emailing, login and registration functionality, etc. This example shows running Ory Kratos alongside a leptos server and making use of their UI Node data types in leptos. TODO: This example needs a bit more work to show off SSO passwordless etc \n\n### tauri-from-scratch\nThis example walks you through in explicit detail how to use [Tauri](https://tauri.app/) to render your Leptos App on non web targets using [WebView](https://en.wikipedia.org/wiki/WebView) while communicating with your leptos server and servering an SSR supported web experience. TODO: It could be simplified since part of the readme includes copying and pasting boilerplate.\n\n### counter_dwarf_debug\nThis example shows how to add breakpoints within the browser or visual studio code for debugging.\n\n### bevy3d_ui\nThis example uses the bevy 3d game engine with leptos within webassembly.\n"
  },
  {
    "path": "projects/bevy3d_ui/Cargo.toml",
    "content": "[package]\nname = \"bevy3d_ui\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { version = \"0.7.8\", features = [\"csr\"] }\nleptos_meta = { version = \"0.7.8\" }\nleptos_router = { version = \"0.7.8\" }\nconsole_log = \"1.0\"\nlog = \"0.4.22\"\nconsole_error_panic_hook = \"0.1.7\"\nbevy = \"0.15.2\"\ncrossbeam-channel = \"0.5.13\"\n\n[dev-dependencies]\nwasm-bindgen = \"0.2.100\"\nwasm-bindgen-test = \"0.3.50\"\nweb-sys = \"0.3.77\"\n\n[workspace]\n# The empty workspace here is to keep rust-analyzer satisfied\n"
  },
  {
    "path": "projects/bevy3d_ui/README.md",
    "content": "# Bevy 3D UI Example\n\nThis example combines a leptos UI with a bevy 3D view.  \nBevy is a 3D game engine written in rust that can be compiled to web assembly by using the wgpu library.  \nThe wgpu library in turn can target the newer webgpu standard or the older webgl for web browsers.\n\nIn the case of a desktop application, if you wanted to use a styled ui via leptos and a 3d view via bevy\nyou could also combine this with tauri.  \n\n## Quick Start\n\n  * Run `trunk serve to run the example.\n  * Browse to http://127.0.0.1:8080/\n\nIt's best to use a web browser with webgpu capability for best results such as Chrome or Opera.\n"
  },
  {
    "path": "projects/bevy3d_ui/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "projects/bevy3d_ui/src/demos/bevydemo1/eventqueue/events.rs",
    "content": "use bevy::prelude::*;\n\n/// Event Processor\n#[derive(Resource)]\npub struct EventProcessor<TSender, TReceiver> {\n    pub sender: crossbeam_channel::Sender<TSender>,\n    pub receiver: crossbeam_channel::Receiver<TReceiver>,\n}\n\nimpl<TSender, TReceiver> Clone for EventProcessor<TSender, TReceiver> {\n    fn clone(&self) -> Self {\n        Self {\n            sender: self.sender.clone(),\n            receiver: self.receiver.clone(),\n        }\n    }\n}\n\n/// Events sent from the client to bevy\n#[derive(Debug)]\npub enum ClientInEvents {\n    /// Update the 3d model position from the client\n    CounterEvt(CounterEvtData),\n}\n\n/// Events sent out from bevy to the client\n#[derive(Debug)]\npub enum PluginOutEvents {\n    /// TODO Feed back to the client an event from bevy\n    Click,\n}\n\n/// Input event to update the bevy view from the client\n#[derive(Clone, Debug, Event)]\npub struct CounterEvtData {\n    /// Amount to move on the Y Axis\n    pub value: f32,\n}\n"
  },
  {
    "path": "projects/bevy3d_ui/src/demos/bevydemo1/eventqueue/mod.rs",
    "content": "pub mod events;\npub mod plugin;\n"
  },
  {
    "path": "projects/bevy3d_ui/src/demos/bevydemo1/eventqueue/plugin.rs",
    "content": "use super::events::*;\nuse bevy::prelude::*;\n\n/// Events plugin for bevy\n#[derive(Clone)]\npub struct DuplexEventsPlugin {\n    /// Client processor for sending ClientInEvents, receiving PluginOutEvents\n    client_processor: EventProcessor<ClientInEvents, PluginOutEvents>,\n    /// Internal processor for sending PluginOutEvents, receiving ClientInEvents\n    plugin_processor: EventProcessor<PluginOutEvents, ClientInEvents>,\n}\n\nimpl DuplexEventsPlugin {\n    /// Create a new instance\n    pub fn new() -> DuplexEventsPlugin {\n        // For sending messages from bevy to the client\n        let (bevy_sender, client_receiver) = crossbeam_channel::bounded(50);\n        // For sending message from the client to bevy\n        let (client_sender, bevy_receiver) = crossbeam_channel::bounded(50);\n        DuplexEventsPlugin {\n            client_processor: EventProcessor {\n                sender: client_sender,\n                receiver: client_receiver,\n            },\n            plugin_processor: EventProcessor {\n                sender: bevy_sender,\n                receiver: bevy_receiver,\n            },\n        }\n    }\n\n    /// Get the client event processor\n    pub fn get_processor(\n        &self,\n    ) -> EventProcessor<ClientInEvents, PluginOutEvents> {\n        self.client_processor.clone()\n    }\n}\n\n/// Build the bevy plugin and attach\nimpl Plugin for DuplexEventsPlugin {\n    fn build(&self, app: &mut App) {\n        app.insert_resource(self.plugin_processor.clone())\n            .init_resource::<Events<CounterEvtData>>()\n            .add_systems(PreUpdate, input_events_system);\n    }\n}\n\n/// Send the event to bevy using EventWriter\nfn input_events_system(\n    int_processor: Res<EventProcessor<PluginOutEvents, ClientInEvents>>,\n    mut counter_event_writer: EventWriter<CounterEvtData>,\n) {\n    for input_event in int_processor.receiver.try_iter() {\n        match input_event {\n            ClientInEvents::CounterEvt(event) => {\n                // Send event through Bevy's event system\n                counter_event_writer.send(event);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "projects/bevy3d_ui/src/demos/bevydemo1/mod.rs",
    "content": "pub mod eventqueue;\npub mod scene;\npub mod state;\n"
  },
  {
    "path": "projects/bevy3d_ui/src/demos/bevydemo1/scene.rs",
    "content": "use super::eventqueue::events::{\n    ClientInEvents, CounterEvtData, EventProcessor, PluginOutEvents,\n};\nuse super::eventqueue::plugin::DuplexEventsPlugin;\nuse super::state::{Shared, SharedResource, SharedState};\nuse bevy::prelude::*;\n\n/// Represents the Cube in the scene\n#[derive(Component, Copy, Clone)]\npub struct Cube;\n\n/// Represents the 3D Scene\n#[derive(Clone)]\npub struct Scene {\n    is_setup: bool,\n    canvas_id: String,\n    evt_plugin: DuplexEventsPlugin,\n    shared_state: Shared<SharedState>,\n    processor: EventProcessor<ClientInEvents, PluginOutEvents>,\n}\n\nimpl Scene {\n    /// Create a new instance\n    pub fn new(canvas_id: String) -> Scene {\n        let plugin = DuplexEventsPlugin::new();\n        Scene {\n            is_setup: false,\n            canvas_id,\n            evt_plugin: plugin.clone(),\n            shared_state: SharedState::new(),\n            processor: plugin.get_processor(),\n        }\n    }\n\n    /// Get the shared state\n    pub fn get_state(&self) -> Shared<SharedState> {\n        self.shared_state.clone()\n    }\n\n    /// Get the event processor\n    pub fn get_processor(\n        &self,\n    ) -> EventProcessor<ClientInEvents, PluginOutEvents> {\n        self.processor.clone()\n    }\n\n    /// Setup and attach the bevy instance to the html canvas element\n    pub fn setup(&mut self) {\n        if self.is_setup {\n            return;\n        };\n        App::new()\n            .add_plugins(DefaultPlugins.set(WindowPlugin {\n                primary_window: Some(Window {\n                    canvas: Some(self.canvas_id.clone()),\n                    ..default()\n                }),\n                ..default()\n            }))\n            .add_plugins(self.evt_plugin.clone())\n            .insert_resource(SharedResource(self.shared_state.clone()))\n            .add_systems(Startup, setup_scene)\n            .add_systems(Update, handle_bevy_event)\n            .run();\n        self.is_setup = true;\n    }\n}\n\n/// Setup the scene\nfn setup_scene(\n    mut commands: Commands,\n    mut meshes: ResMut<Assets<Mesh>>,\n    mut materials: ResMut<Assets<StandardMaterial>>,\n    resource: Res<SharedResource>,\n) {\n    let name = resource.0.lock().unwrap().name.clone();\n    // circular base\n    commands.spawn((\n        Mesh3d(meshes.add(Circle::new(4.0))),\n        MeshMaterial3d(materials.add(Color::WHITE)),\n        Transform::from_rotation(Quat::from_rotation_x(\n            -std::f32::consts::FRAC_PI_2,\n        )),\n    ));\n\n    // cube\n    commands.spawn((\n        Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),\n        MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),\n        Transform::from_xyz(0.0, 0.5, 0.0),\n        Cube,\n    ));\n\n    // light\n    commands.spawn((\n        PointLight {\n            shadows_enabled: true,\n            ..default()\n        },\n        Transform::from_xyz(4.0, 8.0, 4.0),\n    ));\n\n    // camera\n    commands.spawn((\n        Camera3d::default(),\n        Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),\n    ));\n    commands.spawn((Text::new(name), TextFont::default()));\n}\n\n/// Move the Cube on event\nfn handle_bevy_event(\n    mut counter_event_reader: EventReader<CounterEvtData>,\n    mut cube_query: Query<&mut Transform, With<Cube>>,\n) {\n    let mut cube_transform = cube_query.get_single_mut().expect(\"no cube :(\");\n    for _ev in counter_event_reader.read() {\n        cube_transform.translation += Vec3::new(0.0, _ev.value, 0.0);\n    }\n}\n"
  },
  {
    "path": "projects/bevy3d_ui/src/demos/bevydemo1/state.rs",
    "content": "use bevy::ecs::system::Resource;\nuse std::sync::{Arc, Mutex};\n\npub type Shared<T> = Arc<Mutex<T>>;\n\n/// Shared Resource used for Bevy\n#[derive(Resource)]\npub struct SharedResource(pub Shared<SharedState>);\n\n/// Shared State\npub struct SharedState {\n    pub name: String,\n}\n\nimpl SharedState {\n    /// Get a new shared state\n    pub fn new() -> Arc<Mutex<SharedState>> {\n        let state = SharedState {\n            name: \"This can be used for shared state\".to_string(),\n        };\n        let shared = Arc::new(Mutex::new(state));\n        shared\n    }\n}\n"
  },
  {
    "path": "projects/bevy3d_ui/src/demos/mod.rs",
    "content": "pub mod bevydemo1;\n"
  },
  {
    "path": "projects/bevy3d_ui/src/main.rs",
    "content": "mod demos;\nmod routes;\nuse leptos::prelude::*;\nuse routes::RootPage;\n\npub fn main() {\n    // Bevy will output a lot of debug info to the console when this is enabled.\n    //_ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(|| view! { <RootPage/> })\n}\n"
  },
  {
    "path": "projects/bevy3d_ui/src/routes/demo1.rs",
    "content": "use crate::demos::bevydemo1::eventqueue::events::{\n    ClientInEvents, CounterEvtData,\n};\nuse crate::demos::bevydemo1::scene::Scene;\nuse leptos::prelude::*;\n\n/// 3d view component\n#[component]\npub fn Demo1() -> impl IntoView {\n    // Setup a Counter\n    let initial_value: i32 = 0;\n    let step: i32 = 1;\n    let (value, set_value) = signal(initial_value);\n\n    // Setup a bevy 3d scene\n    let scene = Scene::new(\"#bevy\".to_string());\n    let sender = scene.get_processor().sender;\n    let (sender_sig, _set_sender_sig) = signal(sender);\n    let (scene_sig, _set_scene_sig) = signal(scene);\n\n    // We need to add the 3D view onto the canvas post render.\n    Effect::new(move |_| {\n        request_animation_frame(move || {\n            scene_sig.get_untracked().setup();\n        });\n    });\n\n    view! {\n        <div>\n            <button on:click=move |_| set_value.set(0)>\"Clear\"</button>\n            <button on:click=move |_| {\n                set_value.update(|value| *value -= step);\n                let newpos = (step as f32) / 10.0;\n                sender_sig\n                    .get()\n                    .send(ClientInEvents::CounterEvt(CounterEvtData { value: -newpos }))\n                    .expect(\"could not send event\");\n            }>\"-1\"</button>\n            <span>\"Value: \" {value} \"!\"</span>\n            <button on:click=move |_| {\n                set_value.update(|value| *value += step);\n                let newpos = step as f32 / 10.0;\n                sender_sig\n                    .get()\n                    .send(ClientInEvents::CounterEvt(CounterEvtData { value: newpos }))\n                    .expect(\"could not send event\");\n            }>\"+1\"</button>\n        </div>\n\n        <canvas id=\"bevy\" width=\"800\" height=\"600\"></canvas>\n    }\n}\n"
  },
  {
    "path": "projects/bevy3d_ui/src/routes/mod.rs",
    "content": "pub mod demo1;\nuse demo1::Demo1;\nuse leptos::prelude::*;\nuse leptos_meta::Meta;\nuse leptos_meta::Title;\nuse leptos_meta::{provide_meta_context, MetaTags, Stylesheet};\nuse leptos_router::components::*;\nuse leptos_router::StaticSegment;\n#[component]\npub fn RootPage() -> impl IntoView {\n    provide_meta_context();\n\n    view! {\n        <Meta name=\"charset\" content=\"UTF-8\"/>\n        <Meta name=\"description\" content=\"Leptonic CSR template\"/>\n        <Meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n        <Meta name=\"theme-color\" content=\"#e66956\"/>\n        <Title text=\"Leptos Bevy3D Example\"/>\n        <Stylesheet href=\"https://fonts.googleapis.com/css?family=Roboto&display=swap\"/>\n        <MetaTags/>\n        <Router>\n            <Routes fallback=move || \"Not found.\">\n                <Route path=StaticSegment(\"\") view=Demo1 />\n            </Routes>\n        </Router>\n    }\n}\n"
  },
  {
    "path": "projects/counter_dwarf_debug/.gitignore",
    "content": "# For this example we want to include the vscode files\n!.vscode\n"
  },
  {
    "path": "projects/counter_dwarf_debug/.vscode/launch.json",
    "content": "{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Launch Browser Chrome\",\n            \"request\": \"launch\",\n            \"type\": \"chrome\",\n            \"url\": \"http://localhost:4001\",\n            \"webRoot\": \"${workspaceFolder}/dist\",\n            // Needed to keep the dwarf extension in the browser\n            \"userDataDir\": false,\n            \"preLaunchTask\": \"trunk: serve\",\n            \"postDebugTask\": \"postdebugKill\"\n        },\n    ]\n}\n"
  },
  {
    "path": "projects/counter_dwarf_debug/.vscode/tasks.json",
    "content": "{\n\t\"version\": \"2.0.0\",\n\t\"tasks\": [\n\n\t\t// Task to build the sources\n\t\t{\n\t\t\t\"label\": \"trunk: build\",\n\t\t\t\"type\": \"shell\",\n\t\t\t\"command\": \"trunk\",\n\t\t\t\"args\": [\"build\"],\n\t\t\t\"problemMatcher\": [\n\t\t\t\t\"$rustc\"\n\t\t\t],\n\t\t\t\"group\": \"build\",\n\t\t},\n\n\t\t// Task to launch trunk serve for debugging\n\t\t{\n\t\t\t\"label\": \"trunk: serve\",\n\t\t\t\"type\": \"shell\",\n\t\t\t\"command\": \"trunk\",\n\t\t\t\"args\": [\"serve\"],\n\t\t\t\"isBackground\": true,\n\t\t\t\"problemMatcher\": {\n\t\t\t\t\"pattern\": {\n\t\t\t\t\t\"regexp\": \".\",\n\t\t\t\t\t\"file\": 1,\"line\": 1,\n\t\t\t\t\t\"column\": 1,\"message\": 1\n\t\t\t\t},\n\t\t\t\t\"background\": {\n\t\t\t\t\t\"activeOnStart\": true,\n\t\t\t\t\t\"beginsPattern\": \".\",\n\t\t\t\t\t\"endsPattern\": \".\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t// Terminate the trunk serve task\n\t\t{\n\t\t\t\"label\": \"postdebugKill\",\n\t\t\t\"type\": \"shell\",\n\t\t\t\"command\": \"echo ${input:terminate}\",\n\t\t},\n\t],\n\t\"inputs\": [\n\t\t{\n\t\t\t\"id\": \"terminate\",\n\t\t\t\"type\": \"command\",\n\t\t\t\"command\": \"workbench.action.tasks.terminate\",\n\t\t\t\"args\": \"terminateAll\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "projects/counter_dwarf_debug/Cargo.toml",
    "content": "[workspace]\n# The empty workspace here is to keep rust-analyzer satisfied\n\n[package]\nname = \"counter_dwarf_debug\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[dependencies]\nleptos = { path = \"../../leptos\", features = [\"csr\"] }\nconsole_log = \"1.0\"\nlog = \"0.4.22\"\nconsole_error_panic_hook = \"0.1.7\"\n\n[dev-dependencies]\nwasm-bindgen = \"0.2.92\"\nwasm-bindgen-test = \"0.3.42\"\nweb-sys = \"0.3.69\"\n"
  },
  {
    "path": "projects/counter_dwarf_debug/README.md",
    "content": "# Debugging Leptos Counter Example in Browser and VSCode\n\nThis example builds on the simple counter by adding breakpoints and single stepping the source code for debugging.  \nBoth within the browser and VSCode.  \nThis uses a new feature of wasm called Dwarf which is a form of source code mapping.\n\nNote variable inspection during the breakpoints doesn't seem to work at this stage.\n\n## Quick Start\n\n  * Install the requirements below\n  * Open this directory within visual studio code\n  * Add a breakpoint to the code\n  * Launch the example using the visual studio code debug launcher\n\n## How This Works\n\n### Html Changes\n\nFirst we need to make a change to the index.html file\n\nFrom this\n```html\n<link data-trunk rel=\"rust\" data-wasm-opt=\"z\"/>\n```\n\nTo this\n```html\n<link data-trunk rel=\"rust\" data-keep-debug=\"true\" data-wasm-opt=\"z\"/>\n```\n\nThis instructs the rust `trunk` utility to pass a long an option to `wasm-bindgen` called `--keep-debug`  \nThis option bundles in a type of sourcemap into the built wasm file.  \nBe aware that this will make the wasm file much larger.\n\n### Browser Changes\n\nNext we need to allow the browser to read the DWARF data from the wasm file.\nFor Chrome / Opera there's an extension here that needs to be installed.\n\n  * https://chromewebstore.google.com/detail/cc++-devtools-support-dwa/pdcpmagijalfljmkmjngeonclgbbannb?pli=1\n\n## Debugging within the Browser\n\nWithin the browser's dev console it should now be possible to view the rust source code and add breakpoints.\n\n![Chrome Debug Image](./img/breakpoint1.png)\n\n## Debugging within VSCode\n\nNote this is still experimental, although I have managed to get breakpoints working under VSCode.  \nSo far I've only tried this within a windows environment.\n\nIn order to have the breakpoints land at the correct position.  \nWe need to install the following VSCode extension.\n\n  * [WebAssembly DWARF Debugging](https://marketplace.visualstudio.com/items?itemName=ms-vscode.wasm-dwarf-debugging)\n\nWithin the browser launch section under `launch.json` we need to set userDataDir to false in order for the DWARF browser extension to be loaded.\n```json\n  {\n    \"name\": \"Launch Browser Chrome\",\n    \"request\": \"launch\",\n    \"type\": \"chrome\",\n    \"url\": \"http://localhost:8080\",\n    \"webRoot\": \"${workspaceFolder}/dist\",\n    // Needed to keep the dwarf extension in the browser\n    \"userDataDir\": false,\n  },\n```\n\nNow we should be able to add breakpoints within visual studio code while debugging the rust wasm.\n\n![Chrome Debug Image](./img/breakpoint2.png)\n"
  },
  {
    "path": "projects/counter_dwarf_debug/Trunk.toml",
    "content": "[serve]\naddress = \"127.0.0.1\"\nport = 4001\nopen = false\n"
  },
  {
    "path": "projects/counter_dwarf_debug/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-keep-debug=\"true\" data-wasm-opt=\"z\"/>\n\t\t<link data-trunk rel=\"icon\" type=\"image/ico\" href=\"/public/favicon.ico\"/>\n\t</head>\n\t<body></body>\n</html>\n"
  },
  {
    "path": "projects/counter_dwarf_debug/rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"stable\" # test change\n"
  },
  {
    "path": "projects/counter_dwarf_debug/src/lib.rs",
    "content": "use leptos::*;\n\n/// A simple counter component.\n///\n/// You can use doc comments like this to document your component.\n#[component]\npub fn SimpleCounter(\n    /// The starting value for the counter\n    initial_value: i32,\n    /// The change that should be applied each time the button is clicked.\n    step: i32,\n) -> impl IntoView {\n    let (value, set_value) = create_signal(initial_value);\n\n    view! {\n        <div>\n            <button on:click=move |_| set_value.set(0)>\"Clear\"</button>\n            <button on:click=move |_| set_value.update(|value| *value -= step)>\"-1\"</button>\n            <span>\"Value: \" {value} \"!\"</span>\n            <button on:click=move |_| {\n                // Test Panic\n                //panic!(\"Test Panic\");\n                // In order to breakpoint the below, the code needs to be on it's own line\n                set_value.update(|value| *value += step)\n            }\n            >\"+1\"</button>\n        </div>\n    }\n}\n"
  },
  {
    "path": "projects/counter_dwarf_debug/src/main.rs",
    "content": "use counter_dwarf_debug::SimpleCounter;\nuse leptos::*;\n\npub fn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(|| {\n        view! {\n            <SimpleCounter\n                initial_value=0\n                step=1\n            />\n        }\n    })\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n"
  },
  {
    "path": "projects/hexagonal-architecture/Cargo.toml",
    "content": "[package]\nname = \"leptos-hexagonal-design\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nleptos = { version = \"0.7.0\" }\nleptos_router = { version = \"0.7.0\" }\naxum = { version = \"0.7\", optional = true }\nconsole_error_panic_hook = \"0.1\"\nleptos_axum = { version = \"0.7.0\", optional = true }\nleptos_meta = { version = \"0.7.0\" }\ntokio = { version = \"1\", features = [\"rt-multi-thread\"], optional = true }\ntower = { version = \"0.4\", optional = true }\ntower-http = { version = \"0.5\", features = [\"fs\"], optional = true }\nwasm-bindgen = \"=0.2.99\"\nthiserror = \"1\"\ntracing = { version = \"0.1\", optional = true }\nhttp = \"1\"\nmockall = \"0.13.1\"\ncfg-if = \"1.0.0\"\nserde = \"1.0.215\"\npin-project-lite = \"0.2.15\"\n\n[features]\nconfig_1 = []\nhydrate = [\"leptos/hydrate\"]\nssr = [\n    \"dep:axum\",\n    \"dep:tokio\",\n    \"dep:tower\",\n    \"dep:tower-http\",\n    \"dep:leptos_axum\",\n    \"leptos/ssr\",\n    \"leptos_meta/ssr\",\n    \"leptos_router/ssr\",\n    \"dep:tracing\",\n]\n\n# Defines a size-optimized profile for the WASM bundle in release mode\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"leptos-hexagonal-design\"\n\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"public\"\n\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n\n# The port to use for automatic reload monitoring\nreload-port = 3001\n\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\n# The profile to use for the lib target when compiling for release\n#\n# Optional. Defaults to \"release\".\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "projects/hexagonal-architecture/LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  },
  {
    "path": "projects/hexagonal-architecture/README.md",
    "content": "### Leptos Hexagonal Design\n\nThis Blog Post / Github Repository is about applying principles of hexagonal design\n    - Isolating Business Logic from Sub Domains\n    - Decoupling design to improve flexibility and testablity\n    - Applying the principles hierachically so that sub domains which talk to external services also implement also implement hexagonal architecture\n\n\nThere are specific constraints that guide our design decisions\n\n- Server Functions Can't be Generic\n- Boxed Traits Objects Have overhead, so we only want to use as much generic code as possible avoid Trait Objects\n\nThe way this works is we define the functionality of our program in the main domain (i.e the business problem and processes our app is trying to solve / proceduralize). We then create sub domains and external services, although they are represented the same. External services are usually the end nodes of your app's architectural graph. Our main application builds it's service layout using configuration flags.\n\n```rust\npub fn config() -> MainAppHandlerAlias {\n    cfg_if::cfg_if! {\n                if #[cfg(feature=\"open_ai_wrapper\")] {\n                    fn server_handler_config_1() -> MainAppHandler<\n                        AuthService<PostgresDb, Redis>,\n                        AiMessageGen<PostgresDb,OpenAiWrapper>,\n                        > {\n                        MainAppHandler::new_with_postgres_and_redis_open_ai()\n                    }\n                    server_handler_config_1()\n                } else {\n                    fn server_handler_config_2() -> MainAppHandler<\n                        AuthService<MySql, MemCache>,\n                        OtherAiMessageGen<MySql,HuggingFaceWrapper>,\n                        > {\n                        MainAppHandler::new_with_my_sql_memcache_hugging_face()\n                    }           \n                    server_handler_config_2()\n                }\n            }\n}\n\n```\n\nAnd we pass in our handler which implements a trait\n\n```rust\npub trait HandlerServerFn {\n    pub fn server_fn_1_inner(&self);\n}\nimpl<S,S2> HandlerServerFn for MainAppHandler<S:SubDomain1Trait,S2:SubDomain2Trait> {\n    pub fn server_fn_1_inner(&self) {\n        // do thing\n    }\n}\n```\n\nin our main fn we produce our applications service graph and pass it to our leptos router.\n\n```rust\nmain () {\n   let leptos_options = conf.leptos_options;\n    let routes = generate_route_list(crate::app::App);\n    // our feature flag based config function.\n    let handler = config();\n    let handler_c = handler.clone();\n    // we implement FromRef<ServerState> for LeptosOptions\n    let server_state = ServerState {\n        handler,\n        leptos_options: leptos_options.clone(),\n    };\n    let app = Router::new()\n        .leptos_routes_with_context(\n            &server_state,\n            routes,\n            // We pass in the MainAppHandler struct as context so we can fetch it anywhere context is available on the server.\n            // This includes in middleware we define on server functions (see middleware.rs)\n            move || provide_context(handler_c.clone()),\n            {\n                let leptos_options = leptos_options.clone();\n                move || shell(leptos_options.clone())\n            },\n        )\n        .fallback(leptos_axum::file_and_error_handler::<\n            ServerState<HandlerStructAlias>,\n            _,\n        >(shell))\n        .with_state(server_state);\n}\n```\n\nand then in our server functions \n\n```rust\n#[server]\npub async fn server_fn_1() -> Result<(),ServerFnError> {\n    // we type alias every variation of our services we plan on configuring. The alternative is using Box<dyn Trait> which isn't bad - just slower.\n    Ok(expect_context::<MainAppHandlerAlias>().server_fn_1_inner())\n}\n```\n\nAnd then we can mock and service trait in any combination like so\n\n```rust\n    #[tokio::test]\n    pub async fn test_subdomain_1_with_mocks() -> Result<(), Box<dyn Error>> {\n        let mut mock_external_service_1 = MockExternalServiceTrait1::new();\n        mock_external_service_1\n            .expect_external_service_1_method()\n            .returning(|| {\n                println!(\"Mock external service 1\");\n                Ok(ExternalService1Data)\n            });\n        let mut mock_external_service_2 = MockExternalServiceTrait2::new();\n        mock_external_service_2\n            .expect_external_service_2_method()\n            .returning(|| {\n                println!(\"Mock external service 2\");\n                Ok(ExternalService2Data)\n            });\n        let real_subdomain_1_with_mock_externals = SubDomainStruct1 {\n            external_service_1: mock_external_service_1,\n            external_service_2: mock_external_service_2,\n        };\n        let data = real_subdomain_1_with_mock_externals\n            .sub_domain_1_method()\n            .await?;\n        assert_eq!(data, SubDomain1Data);\n        Ok(())\n    }\n```\n\n\nCheck out the code in the repository for a working example.\n\nRun the tests with \n\n` cargo test --features ssr `\nand otherwise run\n` cargo leptos serve `\nand navigate to `127.0.0.1:3000`\n\nhere's a picture\n\n\n![alt text](leptos_hexagonal_architecture.png)"
  },
  {
    "path": "projects/hexagonal-architecture/end2end/package.json",
    "content": "{\n  \"name\": \"end2end\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {},\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.44.1\",\n    \"@types/node\": \"^20.12.12\",\n    \"typescript\": \"^5.4.5\"\n  }\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/end2end/playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from \"@playwright/test\";\nimport { devices, defineConfig } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://github.com/motdotla/dotenv\n */\n// require('dotenv').config();\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nexport default defineConfig({\n  testDir: \"./tests\",\n  /* Maximum time one test can run for. */\n  timeout: 30 * 1000,\n  expect: {\n    /**\n     * Maximum time expect() should wait for the condition to be met.\n     * For example in `await expect(locator).toHaveText();`\n     */\n    timeout: 5000,\n  },\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !!process.env.CI,\n  /* Retry on CI only */\n  retries: process.env.CI ? 2 : 0,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.CI ? 1 : undefined,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: \"html\",\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */\n    actionTimeout: 0,\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    // baseURL: 'http://localhost:3000',\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: \"on-first-retry\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"chromium\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n      },\n    },\n\n    {\n      name: \"firefox\",\n      use: {\n        ...devices[\"Desktop Firefox\"],\n      },\n    },\n\n    {\n      name: \"webkit\",\n      use: {\n        ...devices[\"Desktop Safari\"],\n      },\n    },\n\n    /* Test against mobile viewports. */\n    // {\n    //   name: 'Mobile Chrome',\n    //   use: {\n    //     ...devices['Pixel 5'],\n    //   },\n    // },\n    // {\n    //   name: 'Mobile Safari',\n    //   use: {\n    //     ...devices['iPhone 12'],\n    //   },\n    // },\n\n    /* Test against branded browsers. */\n    // {\n    //   name: 'Microsoft Edge',\n    //   use: {\n    //     channel: 'msedge',\n    //   },\n    // },\n    // {\n    //   name: 'Google Chrome',\n    //   use: {\n    //     channel: 'chrome',\n    //   },\n    // },\n  ],\n\n  /* Folder for test artifacts such as screenshots, videos, traces, etc. */\n  // outputDir: 'test-results/',\n\n  /* Run your local dev server before starting the tests */\n  // webServer: {\n  //   command: 'npm run start',\n  //   port: 3000,\n  // },\n});\n"
  },
  {
    "path": "projects/hexagonal-architecture/end2end/tests/example.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest(\"homepage has title and heading text\", async ({ page }) => {\n  await page.goto(\"http://localhost:3000/\");\n\n  await expect(page).toHaveTitle(\"Welcome to Leptos\");\n\n  await expect(page.locator(\"h1\")).toHaveText(\"Welcome to Leptos!\");\n});\n"
  },
  {
    "path": "projects/hexagonal-architecture/end2end/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig to read more about this file */\n\n    /* Projects */\n    // \"incremental\": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */\n    // \"composite\": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */\n    // \"tsBuildInfoFile\": \"./.tsbuildinfo\",              /* Specify the path to .tsbuildinfo incremental compilation file. */\n    // \"disableSourceOfProjectReferenceRedirect\": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */\n    // \"disableSolutionSearching\": true,                 /* Opt a project out of multi-project reference checking when editing. */\n    // \"disableReferencedProjectLoad\": true,             /* Reduce the number of projects loaded automatically by TypeScript. */\n\n    /* Language and Environment */\n    \"target\": \"es2016\",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */\n    // \"lib\": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */\n    // \"jsx\": \"preserve\",                                /* Specify what JSX code is generated. */\n    // \"experimentalDecorators\": true,                   /* Enable experimental support for legacy experimental decorators. */\n    // \"emitDecoratorMetadata\": true,                    /* Emit design-type metadata for decorated declarations in source files. */\n    // \"jsxFactory\": \"\",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */\n    // \"jsxFragmentFactory\": \"\",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */\n    // \"jsxImportSource\": \"\",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */\n    // \"reactNamespace\": \"\",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */\n    // \"noLib\": true,                                    /* Disable including any library files, including the default lib.d.ts. */\n    // \"useDefineForClassFields\": true,                  /* Emit ECMAScript-standard-compliant class fields. */\n    // \"moduleDetection\": \"auto\",                        /* Control what method is used to detect module-format JS files. */\n\n    /* Modules */\n    \"module\": \"commonjs\",                                /* Specify what module code is generated. */\n    // \"rootDir\": \"./\",                                  /* Specify the root folder within your source files. */\n    // \"moduleResolution\": \"node10\",                     /* Specify how TypeScript looks up a file from a given module specifier. */\n    // \"baseUrl\": \"./\",                                  /* Specify the base directory to resolve non-relative module names. */\n    // \"paths\": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */\n    // \"rootDirs\": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */\n    // \"typeRoots\": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */\n    // \"types\": [],                                      /* Specify type package names to be included without being referenced in a source file. */\n    // \"allowUmdGlobalAccess\": true,                     /* Allow accessing UMD globals from modules. */\n    // \"moduleSuffixes\": [],                             /* List of file name suffixes to search when resolving a module. */\n    // \"allowImportingTsExtensions\": true,               /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */\n    // \"resolvePackageJsonExports\": true,                /* Use the package.json 'exports' field when resolving package imports. */\n    // \"resolvePackageJsonImports\": true,                /* Use the package.json 'imports' field when resolving imports. */\n    // \"customConditions\": [],                           /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */\n    // \"resolveJsonModule\": true,                        /* Enable importing .json files. */\n    // \"allowArbitraryExtensions\": true,                 /* Enable importing files with any extension, provided a declaration file is present. */\n    // \"noResolve\": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */\n\n    /* JavaScript Support */\n    // \"allowJs\": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */\n    // \"checkJs\": true,                                  /* Enable error reporting in type-checked JavaScript files. */\n    // \"maxNodeModuleJsDepth\": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */\n\n    /* Emit */\n    // \"declaration\": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */\n    // \"declarationMap\": true,                           /* Create sourcemaps for d.ts files. */\n    // \"emitDeclarationOnly\": true,                      /* Only output d.ts files and not JavaScript files. */\n    // \"sourceMap\": true,                                /* Create source map files for emitted JavaScript files. */\n    // \"inlineSourceMap\": true,                          /* Include sourcemap files inside the emitted JavaScript. */\n    // \"outFile\": \"./\",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */\n    // \"outDir\": \"./\",                                   /* Specify an output folder for all emitted files. */\n    // \"removeComments\": true,                           /* Disable emitting comments. */\n    // \"noEmit\": true,                                   /* Disable emitting files from a compilation. */\n    // \"importHelpers\": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */\n    // \"importsNotUsedAsValues\": \"remove\",               /* Specify emit/checking behavior for imports that are only used for types. */\n    // \"downlevelIteration\": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */\n    // \"sourceRoot\": \"\",                                 /* Specify the root path for debuggers to find the reference source code. */\n    // \"mapRoot\": \"\",                                    /* Specify the location where debugger should locate map files instead of generated locations. */\n    // \"inlineSources\": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */\n    // \"emitBOM\": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */\n    // \"newLine\": \"crlf\",                                /* Set the newline character for emitting files. */\n    // \"stripInternal\": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */\n    // \"noEmitHelpers\": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */\n    // \"noEmitOnError\": true,                            /* Disable emitting files if any type checking errors are reported. */\n    // \"preserveConstEnums\": true,                       /* Disable erasing 'const enum' declarations in generated code. */\n    // \"declarationDir\": \"./\",                           /* Specify the output directory for generated declaration files. */\n    // \"preserveValueImports\": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */\n\n    /* Interop Constraints */\n    // \"isolatedModules\": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */\n    // \"verbatimModuleSyntax\": true,                     /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */\n    // \"allowSyntheticDefaultImports\": true,             /* Allow 'import x from y' when a module doesn't have a default export. */\n    \"esModuleInterop\": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */\n    // \"preserveSymlinks\": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */\n    \"forceConsistentCasingInFileNames\": true,            /* Ensure that casing is correct in imports. */\n\n    /* Type Checking */\n    \"strict\": true,                                      /* Enable all strict type-checking options. */\n    // \"noImplicitAny\": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */\n    // \"strictNullChecks\": true,                         /* When type checking, take into account 'null' and 'undefined'. */\n    // \"strictFunctionTypes\": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */\n    // \"strictBindCallApply\": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */\n    // \"strictPropertyInitialization\": true,             /* Check for class properties that are declared but not set in the constructor. */\n    // \"noImplicitThis\": true,                           /* Enable error reporting when 'this' is given the type 'any'. */\n    // \"useUnknownInCatchVariables\": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */\n    // \"alwaysStrict\": true,                             /* Ensure 'use strict' is always emitted. */\n    // \"noUnusedLocals\": true,                           /* Enable error reporting when local variables aren't read. */\n    // \"noUnusedParameters\": true,                       /* Raise an error when a function parameter isn't read. */\n    // \"exactOptionalPropertyTypes\": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */\n    // \"noImplicitReturns\": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */\n    // \"noFallthroughCasesInSwitch\": true,               /* Enable error reporting for fallthrough cases in switch statements. */\n    // \"noUncheckedIndexedAccess\": true,                 /* Add 'undefined' to a type when accessed using an index. */\n    // \"noImplicitOverride\": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */\n    // \"noPropertyAccessFromIndexSignature\": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */\n    // \"allowUnusedLabels\": true,                        /* Disable error reporting for unused labels. */\n    // \"allowUnreachableCode\": true,                     /* Disable error reporting for unreachable code. */\n\n    /* Completeness */\n    // \"skipDefaultLibCheck\": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */\n    \"skipLibCheck\": true                                 /* Skip type checking all .d.ts files. */\n  }\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/app.rs",
    "content": "use leptos::prelude::*;\nuse leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};\nuse leptos_router::{\n    components::{Route, Router, Routes},\n    StaticSegment,\n};\n\n#[cfg(feature = \"ssr\")]\nuse super::{server_types::HandlerStructAlias, traits::HandlerTrait};\nuse crate::ui_types::*;\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n\n    view! {\n        // injects a stylesheet into the document <head>\n        // id=leptos means cargo-leptos will hot-reload this stylesheet\n        <Stylesheet id=\"leptos\" href=\"/pkg/leptos-hexagonal-design.css\"/>\n\n        // sets the document title\n        <Title text=\"Welcome to Leptos\"/>\n\n        // content for this welcome page\n        <Router>\n            <main>\n                <Routes fallback=|| \"Page not found.\".into_view()>\n                    <Route path=StaticSegment(\"\") view=HomePage/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n/// Renders the home page of your application.\n#[component]\nfn HomePage() -> impl IntoView {\n    let server_fn_1 = ServerAction::<ServerFunction1>::new();\n    let server_fn_2 = ServerAction::<ServerFunction2>::new();\n    let server_fn_3 = ServerAction::<ServerFunction3>::new();\n    Effect::new(move |_| {\n        server_fn_1.dispatch(ServerFunction1 {});\n        server_fn_2.dispatch(ServerFunction2 {});\n        server_fn_3.dispatch(ServerFunction3 {});\n    });\n}\n\n#[server]\n#[middleware(crate::middleware::SubDomain1Layer)]\npub async fn server_function_1() -> Result<UiMappingFromDomainData, ServerFnError> {\n    Ok(expect_context::<HandlerStructAlias>()\n        .server_fn_1()\n        .await?\n        .into())\n}\n#[server]\npub async fn server_function_2() -> Result<UiMappingFromDomainData2, ServerFnError> {\n    Ok(expect_context::<HandlerStructAlias>()\n        .server_fn_2()\n        .await?\n        .into())\n}\n#[server]\npub async fn server_function_3() -> Result<UiMappingFromDomainData3, ServerFnError> {\n    Ok(expect_context::<HandlerStructAlias>()\n        .server_fn_3()\n        .await?\n        .into())\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/config.rs",
    "content": "use super::server_types::*;\n\npub fn config() -> HandlerStructAlias {\n    cfg_if::cfg_if! {\n                if #[cfg(feature=\"config_1\")] {\n                    fn server_handler_config_1() -> HandlerStruct<\n            SubDomainStruct1<ExternalService1_1, ExternalService2_1>,\n            SubDomainStruct2<ExternalService1_1>,\n        > {\n            HandlerStruct::default()\n        }\n                    server_handler_config_1()\n                } else {\n                    fn server_handler_config_2() -> HandlerStruct<\n        SubDomainStruct1<ExternalService1_2, ExternalService2_2>,\n        SubDomainStruct2<ExternalService1_2>,\n    > {\n        HandlerStruct::new()\n    }\n                    server_handler_config_2()\n                }\n            }\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/lib.rs",
    "content": "pub mod app;\n\npub mod ui_types;\n\n#[cfg(feature = \"ssr\")]\npub mod config;\n#[cfg(feature = \"ssr\")]\npub mod middleware;\n#[cfg(feature = \"ssr\")]\npub mod server_types;\n#[cfg(feature = \"ssr\")]\npub mod trait_impl;\n#[cfg(feature = \"ssr\")]\npub mod traits;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(App);\n}\n\n#[cfg(test)]\npub mod tests {\n    use super::server_types::*;\n    use super::traits::*;\n    use std::error::Error;\n\n    #[tokio::test]\n    pub async fn test_subdomain_1_with_mocks() -> Result<(), Box<dyn Error>> {\n        let mut mock_external_service_1 = MockExternalServiceTrait1::new();\n        mock_external_service_1\n            .expect_external_service_1_method()\n            .returning(|| {\n                println!(\"Mock external service 1\");\n                Ok(ExternalService1Data)\n            });\n        let mut mock_external_service_2 = MockExternalServiceTrait2::new();\n        mock_external_service_2\n            .expect_external_service_2_method()\n            .returning(|| {\n                println!(\"Mock external service 2\");\n                Ok(ExternalService2Data)\n            });\n        let real_subdomain_1_with_mock_externals = SubDomainStruct1 {\n            external_service_1: mock_external_service_1,\n            external_service_2: mock_external_service_2,\n        };\n        let data = real_subdomain_1_with_mock_externals\n            .sub_domain_1_method()\n            .await?;\n        assert_eq!(data, SubDomain1Data);\n        Ok(())\n    }\n\n    #[tokio::test]\n    pub async fn test_subdomain_2_with_mocks() -> Result<(), Box<dyn Error>> {\n        let mut mock_external_service_1 = MockExternalServiceTrait1::new();\n        mock_external_service_1\n            .expect_external_service_1_method()\n            .returning(|| {\n                println!(\"Mock external service 1 AGAIN\");\n                Ok(ExternalService1Data)\n            });\n        let real_subdomain_2_with_mock_externals = SubDomainStruct2 {\n            external_service_1: mock_external_service_1,\n        };\n        let data = real_subdomain_2_with_mock_externals\n            .sub_domain_2_method()\n            .await?;\n        assert_eq!(data, SubDomain2Data);\n        Ok(())\n    }\n\n    #[tokio::test]\n    pub async fn test_handler_with_mocks() -> Result<(), Box<dyn Error>> {\n        let mut mock_subdomain_1_trait = MockSubDomainTrait1::new();\n        mock_subdomain_1_trait\n            .expect_sub_domain_1_method()\n            .returning(|| {\n                println!(\"Mock Subdomain 1\");\n                Ok(SubDomain1Data)\n            });\n        let mut mock_subdomain_2_trait = MockSubDomainTrait2::new();\n        mock_subdomain_2_trait\n            .expect_sub_domain_2_method()\n            .returning(|| {\n                println!(\"Mock Subdomain 2\");\n                Ok(SubDomain2Data)\n            });\n        let real_handler_with_mock_subdomains = HandlerStruct {\n            sub_domain_1: mock_subdomain_1_trait,\n            sub_domain_2: mock_subdomain_2_trait,\n        };\n        let data = real_handler_with_mock_subdomains.server_fn_1().await?;\n        assert_eq!(data, DomainData);\n        let data = real_handler_with_mock_subdomains.server_fn_2().await?;\n        assert_eq!(data, DomainData2);\n        let data = real_handler_with_mock_subdomains.server_fn_3().await?;\n        assert_eq!(data, DomainData3);\n        Ok(())\n    }\n\n    fn mock_subdomain_1() -> SubDomainStruct1<MockExternalServiceTrait1, MockExternalServiceTrait2>\n    {\n        let mut mock_external_service_1 = MockExternalServiceTrait1::new();\n        mock_external_service_1\n            .expect_external_service_1_method()\n            .returning(|| {\n                println!(\"Mock external service 1\");\n                Ok(ExternalService1Data)\n            });\n        let mut mock_external_service_2 = MockExternalServiceTrait2::new();\n        mock_external_service_2\n            .expect_external_service_2_method()\n            .returning(|| {\n                println!(\"Mock external service 2\");\n                Ok(ExternalService2Data)\n            });\n        let real_subdomain_1_with_mock_externals = SubDomainStruct1 {\n            external_service_1: mock_external_service_1,\n            external_service_2: mock_external_service_2,\n        };\n        real_subdomain_1_with_mock_externals\n    }\n\n    #[tokio::test]\n    pub async fn test_handler_with_mock_and_real_mix() -> Result<(), Box<dyn Error>> {\n        let sub_domain_1 = mock_subdomain_1();\n        let mut mock_subdomain_2_trait = MockSubDomainTrait2::new();\n        mock_subdomain_2_trait\n            .expect_sub_domain_2_method()\n            .returning(|| {\n                println!(\"Mock Subdomain 2\");\n                Ok(SubDomain2Data)\n            });\n        let real_handler = HandlerStruct {\n            sub_domain_1,\n            sub_domain_2: mock_subdomain_2_trait,\n        };\n        let data = real_handler.server_fn_1().await?;\n        assert_eq!(data, DomainData);\n        let data = real_handler.server_fn_2().await?;\n        assert_eq!(data, DomainData2);\n        let data = real_handler.server_fn_3().await?;\n        assert_eq!(data, DomainData3);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use leptos::logging::log;\n    use leptos::prelude::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use leptos_hexagonal_design::{\n        app::*,\n        config::config,\n        server_types::{HandlerStructAlias, ServerState},\n    };\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let leptos_options = conf.leptos_options;\n    let routes = generate_route_list(App);\n    let handler = config();\n    let handler_c = handler.clone();\n    let server_state = ServerState {\n        handler,\n        leptos_options: leptos_options.clone(),\n    };\n    let app = Router::new()\n        .leptos_routes_with_context(\n            &server_state,\n            routes,\n            move || provide_context(handler_c.clone()),\n            {\n                let leptos_options = leptos_options.clone();\n                move || shell(leptos_options.clone())\n            },\n        )\n        .fallback(leptos_axum::file_and_error_handler::<\n            ServerState<HandlerStructAlias>,\n            _,\n        >(shell))\n        .with_state(server_state);\n\n    log!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for pure client-side testing\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/middleware.rs",
    "content": "use axum::{\n    body::Body,\n    http::{Request, Response},\n};\nuse leptos::prelude::expect_context;\nuse std::{\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\nuse tower::{Layer, Service};\n\nuse crate::{\n    server_types::{HandlerStructAlias, ServerState},\n    traits::SubDomainTrait1,\n};\nuse pin_project_lite::pin_project;\n\n#[derive(Clone)]\npub struct SubDomain1Layer;\n\nimpl<S> Layer<S> for SubDomain1Layer {\n    type Service = SubDomain1MiddleWare<S>;\n\n    fn layer(&self, inner: S) -> Self::Service {\n        SubDomain1MiddleWare { inner }\n    }\n}\n\npub struct SubDomain1MiddleWare<S> {\n    inner: S,\n}\n\nimpl<S, ReqBody> Service<Request<ReqBody>> for SubDomain1MiddleWare<S>\nwhere\n    S: Service<Request<ReqBody>, Response = Response<Body>>,\n    S::Error: std::fmt::Debug,\n    S::Future: Send + 'static,\n{\n    type Response = S::Response;\n    type Error = S::Error;\n    type Future = SubDomain1Future<S::Future>;\n\n    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.inner.poll_ready(cx)\n    }\n\n    fn call(&mut self, req: Request<ReqBody>) -> Self::Future {\n        let req_fut = self.inner.call(req);\n        SubDomain1Future { req_fut }\n    }\n}\npin_project! {\n    pub struct SubDomain1Future<F> {\n        #[pin]\n        req_fut: F,\n    }\n}\n\nimpl<F, Err> Future for SubDomain1Future<F>\nwhere\n    F: Future<Output = Result<Response<Body>, Err>>,\n{\n    type Output = Result<Response<Body>, Err>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let subdomain_1 = expect_context::<ServerState<HandlerStructAlias>>()\n            .handler\n            .sub_domain_1;\n        let mut subdomain_1_fut = subdomain_1.sub_domain_1_method();\n        match Pin::as_mut(&mut subdomain_1_fut).poll(cx) {\n            Poll::Ready(Ok(_)) => {\n                println!(\"Middleware for Subdomain 1 Passed, calling request...\");\n                this.req_fut.poll(cx)\n            }\n            Poll::Ready(Err(_)) => Poll::Ready(Ok(Response::builder()\n                .status(http::StatusCode::FORBIDDEN)\n                .body(Body::from(\"Access denied\"))\n                .unwrap())),\n            Poll::Pending => Poll::Pending,\n        }\n    }\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/server_types.rs",
    "content": "use super::traits::*;\nuse leptos::config::LeptosOptions;\nuse thiserror::Error;\n\n#[derive(Clone)]\npub struct ServerState<Handler: HandlerTrait> {\n    pub handler: Handler,\n    pub leptos_options: LeptosOptions,\n}\n\n#[cfg(feature = \"config_1\")]\npub type HandlerStructAlias = HandlerStruct<\n    SubDomainStruct1<ExternalService1_1, ExternalService2_1>,\n    SubDomainStruct2<ExternalService1_1>,\n>;\n#[cfg(not(feature = \"config_1\"))]\npub type HandlerStructAlias = HandlerStruct<\n    SubDomainStruct1<ExternalService1_2, ExternalService2_2>,\n    SubDomainStruct2<ExternalService1_2>,\n>;\n\n#[derive(Clone, Default)]\npub struct HandlerStruct<SubDomain1: SubDomainTrait1, SubDomain2: SubDomainTrait2> {\n    pub sub_domain_1: SubDomain1,\n    pub sub_domain_2: SubDomain2,\n}\n#[derive(Clone, Default)]\npub struct SubDomainStruct1<\n    ExternalService1: ExternalServiceTrait1,\n    ExternalService2: ExternalServiceTrait2,\n> {\n    pub external_service_1: ExternalService1,\n    pub external_service_2: ExternalService2,\n}\n\n#[derive(Clone, Default)]\npub struct SubDomainStruct2<ExternalService1: ExternalServiceTrait1> {\n    pub external_service_1: ExternalService1,\n}\n\n#[derive(Clone, Default)]\npub struct ExternalService1_1;\n#[derive(Clone, Default)]\npub struct ExternalService1_2;\n#[derive(Clone, Default)]\npub struct ExternalService2_1;\n#[derive(Clone, Default)]\npub struct ExternalService2_2;\n#[derive(Clone, Default)]\npub struct ExternalService1;\n\n#[derive(Clone, PartialEq, Debug)]\npub struct DomainData;\n#[derive(Clone, PartialEq, Debug)]\npub struct DomainData2;\n#[derive(Clone, PartialEq, Debug)]\npub struct DomainData3;\n#[derive(Clone, PartialEq, Debug)]\npub struct SubDomain1Data;\n#[derive(Clone, PartialEq, Debug)]\npub struct SubDomain2Data;\n#[derive(Clone)]\npub struct ExternalService1Data;\n#[derive(Clone)]\npub struct ExternalService2Data;\n\n#[derive(Clone, Error, Debug)]\npub enum DomainError {\n    #[error(\"Underlying Subdomain 1 Error\")]\n    SubDomain1Error(#[from] SubDomain1Error),\n    #[error(\"Underlying Subdomain 2 Error\")]\n    SubDomain2Error(#[from] SubDomain2Error),\n}\n\n#[derive(Clone, Error, Debug)]\npub enum SubDomain1Error {\n    #[error(\"Sub Domain 1 Error\")]\n    SubDomain1Error,\n    #[error(\"Underlying Service 1\")]\n    ExternalService1Error(#[from] ExternalService1Error),\n    #[error(\"Underlying Service 2\")]\n    ExternalService2Error(#[from] ExternalService2Error),\n}\n#[derive(Clone, Error, Debug)]\npub enum SubDomain2Error {\n    #[error(\"Sub Domain 2 Error\")]\n    SubDomain2Error,\n    #[error(\"Underlying Service 1\")]\n    ExternalService1Error(#[from] ExternalService1Error),\n}\n\n#[derive(Clone, Error, Debug)]\npub enum ExternalService1Error {\n    #[error(\"Service 1 Error\")]\n    Error,\n}\n\n#[derive(Clone, Error, Debug)]\npub enum ExternalService2Error {\n    #[error(\"Service 2 Error\")]\n    Error,\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/trait_impl.rs",
    "content": "use crate::ui_types::*;\n\nuse super::server_types::*;\nuse super::traits::*;\nuse axum::async_trait;\nuse axum::extract::FromRef;\nuse leptos::config::LeptosOptions;\n\n// So we can pass our server state as state into our leptos router.\nimpl<Handler: HandlerTrait + Clone> FromRef<ServerState<Handler>> for LeptosOptions {\n    fn from_ref(input: &ServerState<Handler>) -> Self {\n        input.leptos_options.clone()\n    }\n}\n\n#[async_trait]\nimpl<SubDomain1, SubDomain2> HandlerTrait for HandlerStruct<SubDomain1, SubDomain2>\nwhere\n    SubDomain1: SubDomainTrait1 + Send + Sync,\n    SubDomain2: SubDomainTrait2 + Send + Sync,\n{\n    async fn server_fn_1(&self) -> Result<DomainData, DomainError> {\n        Ok(self.sub_domain_1.sub_domain_1_method().await?.into())\n    }\n\n    async fn server_fn_2(&self) -> Result<DomainData2, DomainError> {\n        Ok(self.sub_domain_2.sub_domain_2_method().await?.into())\n    }\n\n    async fn server_fn_3(&self) -> Result<DomainData3, DomainError> {\n        Ok((\n            self.sub_domain_1.sub_domain_1_method().await?,\n            self.sub_domain_2.sub_domain_2_method().await?,\n        )\n            .into())\n    }\n}\n\n#[async_trait]\nimpl<ExternalService1, ExternalService2> SubDomainTrait1\n    for SubDomainStruct1<ExternalService1, ExternalService2>\nwhere\n    ExternalService1: ExternalServiceTrait1 + Send + Sync,\n    ExternalService2: ExternalServiceTrait2 + Send + Sync,\n{\n    async fn sub_domain_1_method(&self) -> Result<SubDomain1Data, SubDomain1Error> {\n        Ok((\n            self.external_service_1.external_service_1_method().await?,\n            self.external_service_2.external_service_2_method().await?,\n        )\n            .into())\n    }\n}\n\n#[async_trait]\nimpl<ExternalService1> SubDomainTrait2 for SubDomainStruct2<ExternalService1>\nwhere\n    ExternalService1: ExternalServiceTrait1 + Send + Sync,\n{\n    async fn sub_domain_2_method(&self) -> Result<SubDomain2Data, SubDomain2Error> {\n        Ok(self\n            .external_service_1\n            .external_service_1_method()\n            .await?\n            .into())\n    }\n}\n\n#[async_trait]\nimpl ExternalServiceTrait1 for ExternalService1_1 {\n    async fn external_service_1_method(\n        &self,\n    ) -> Result<ExternalService1Data, ExternalService1Error> {\n        println!(\"External Service 1 From External Service 1_1\");\n        Ok(ExternalService1Data)\n    }\n}\n#[async_trait]\nimpl ExternalServiceTrait1 for ExternalService1_2 {\n    async fn external_service_1_method(\n        &self,\n    ) -> Result<ExternalService1Data, ExternalService1Error> {\n        println!(\"External Service 1 From External Service 1_2\");\n        Ok(ExternalService1Data)\n    }\n}\n#[async_trait]\nimpl ExternalServiceTrait2 for ExternalService2_1 {\n    async fn external_service_2_method(\n        &self,\n    ) -> Result<ExternalService2Data, ExternalService2Error> {\n        println!(\"External Service 2 From External Service 2_1\");\n        Ok(ExternalService2Data)\n    }\n}\n#[async_trait]\nimpl ExternalServiceTrait2 for ExternalService2_2 {\n    async fn external_service_2_method(\n        &self,\n    ) -> Result<ExternalService2Data, ExternalService2Error> {\n        println!(\"External Service 2 From External Service 2_2\");\n        Ok(ExternalService2Data)\n    }\n}\n\n// Sub Domain mapping\nimpl From<(ExternalService1Data, ExternalService2Data)> for SubDomain1Data {\n    fn from(_: (ExternalService1Data, ExternalService2Data)) -> Self {\n        Self\n    }\n}\nimpl From<ExternalService1Data> for SubDomain2Data {\n    fn from(_: ExternalService1Data) -> Self {\n        Self\n    }\n}\n// Domain Mapping\nimpl From<SubDomain1Data> for DomainData {\n    fn from(_: SubDomain1Data) -> Self {\n        Self\n    }\n}\nimpl From<SubDomain2Data> for DomainData2 {\n    fn from(_: SubDomain2Data) -> Self {\n        Self\n    }\n}\nimpl From<(SubDomain1Data, SubDomain2Data)> for DomainData3 {\n    fn from(_: (SubDomain1Data, SubDomain2Data)) -> Self {\n        Self\n    }\n}\n\n// Ui Mapping\nimpl From<DomainData> for UiMappingFromDomainData {\n    fn from(_: DomainData) -> Self {\n        Self\n    }\n}\nimpl From<DomainData2> for UiMappingFromDomainData2 {\n    fn from(_: DomainData2) -> Self {\n        Self\n    }\n}\nimpl From<DomainData3> for UiMappingFromDomainData3 {\n    fn from(_: DomainData3) -> Self {\n        Self\n    }\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/traits.rs",
    "content": "use super::server_types::*;\nuse axum::async_trait;\nuse mockall::automock;\npub trait New {\n    fn new() -> Self;\n}\n\n#[automock]\n#[async_trait]\npub trait HandlerTrait {\n    async fn server_fn_1(&self) -> Result<DomainData, DomainError>;\n    async fn server_fn_2(&self) -> Result<DomainData2, DomainError>;\n    async fn server_fn_3(&self) -> Result<DomainData3, DomainError>;\n}\n\n#[automock]\n#[async_trait]\npub trait SubDomainTrait1 {\n    async fn sub_domain_1_method(&self) -> Result<SubDomain1Data, SubDomain1Error>;\n}\n\n#[automock]\n#[async_trait]\npub trait SubDomainTrait2 {\n    async fn sub_domain_2_method(&self) -> Result<SubDomain2Data, SubDomain2Error>;\n}\n\n#[automock]\n#[async_trait]\npub trait ExternalServiceTrait1 {\n    async fn external_service_1_method(\n        &self,\n    ) -> Result<ExternalService1Data, ExternalService1Error>;\n}\n\n#[automock]\n#[async_trait]\npub trait ExternalServiceTrait2 {\n    async fn external_service_2_method(\n        &self,\n    ) -> Result<ExternalService2Data, ExternalService2Error>;\n}\n"
  },
  {
    "path": "projects/hexagonal-architecture/src/ui_types.rs",
    "content": "use serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize)]\npub struct UiMappingFromDomainData;\n#[derive(Serialize, Deserialize)]\npub struct UiMappingFromDomainData2;\n#[derive(Serialize, Deserialize)]\npub struct UiMappingFromDomainData3;\n"
  },
  {
    "path": "projects/hexagonal-architecture/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n\ttext-align: center;\n}"
  },
  {
    "path": "projects/login_with_token_csr_only/Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\"client\", \"api-boundary\", \"server\"]\n\n[profile.release]\ncodegen-units = 1\nlto = true\n\n[workspace.dependencies]\napi-boundary = { path = \"api-boundary\" }\n\n[patch.crates-io]\nleptos = { path = \"../../leptos\" }\nleptos_router = { path = \"../../router\" }\n"
  },
  {
    "path": "projects/login_with_token_csr_only/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "projects/login_with_token_csr_only/README.md",
    "content": "# Leptos Login Example\n\nThis example demonstrates a scenario of a client-side rendered application\nthat uses an existing API that you cannot or do not want to change.\nThe authentications of this example are done using an API token.\n\nThe `api-boundary` crate contains data structures that are used by the server and the client.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\nYou will also need to run `cargo make stop` to end the server process.\n"
  },
  {
    "path": "projects/login_with_token_csr_only/api-boundary/Cargo.toml",
    "content": "[package]\nname = \"api-boundary\"\nversion = \"0.0.0\"\nedition = \"2021\"\npublish = false\n\n[dependencies]\nserde = { version = \"1.0\", features = [\"derive\"] }\n"
  },
  {
    "path": "projects/login_with_token_csr_only/api-boundary/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../../../\" }\n"
  },
  {
    "path": "projects/login_with_token_csr_only/api-boundary/src/lib.rs",
    "content": "use serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize)]\npub struct Credentials {\n    pub email: String,\n    pub password: String,\n}\n\n#[derive(Clone, Serialize, Deserialize)]\npub struct UserInfo {\n    pub email: String,\n}\n\n#[derive(Clone, Serialize, Deserialize)]\npub struct ApiToken {\n    pub token: String,\n}\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct Error {\n    pub message: String,\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/Cargo.toml",
    "content": "[package]\nname = \"client\"\nversion = \"0.0.0\"\nedition = \"2021\"\npublish = false\n\n[dependencies]\napi-boundary = { workspace = true }\n\nleptos = { path = \"../../../leptos\", features = [\"csr\"] }\nleptos_meta = { path = \"../../../meta\", features = [\"csr\"] }\nleptos_router = { path = \"../../../router\", features = [\"csr\"] }\n\nlog = \"0.4.22\"\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\ngloo-net = \"0.6.0\"\ngloo-storage = \"0.3.0\"\nserde = \"1.0\"\nthiserror = \"1.0\"\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/Makefile.toml",
    "content": "extend = [\n    { path = \"../../cargo-make/main.toml\" },\n    { path = \"../../cargo-make/trunk_server.toml\" },\n]\n\n[env]\nSERVER_PROCESS_NAME = \"server\"\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../../../\" }\n\n[tasks.start-server]\ncwd = \"../server\"\nscript = '''\ncargo run &\n'''\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/Trunk.toml",
    "content": "[[proxy]]\nrewrite = \"/api/\"\nbackend = \"http://127.0.0.1:3000/\"\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\" data-weak-refs/>\n\t</head>\n\t<body></body>\n</html>\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/api.rs",
    "content": "use api_boundary::*;\nuse gloo_net::http::{Request, RequestBuilder, Response};\nuse serde::de::DeserializeOwned;\nuse thiserror::Error;\n\n#[derive(Clone, Copy)]\npub struct UnauthorizedApi {\n    url: &'static str,\n}\n\n#[derive(Clone)]\npub struct AuthorizedApi {\n    url: &'static str,\n    token: ApiToken,\n}\n\nimpl UnauthorizedApi {\n    pub const fn new(url: &'static str) -> Self {\n        Self { url }\n    }\n    pub async fn register(&self, credentials: &Credentials) -> Result<()> {\n        let url = format!(\"{}/users\", self.url);\n        let response = Request::post(&url).json(credentials)?.send().await?;\n        into_json(response).await\n    }\n    pub async fn login(\n        &self,\n        credentials: &Credentials,\n    ) -> Result<AuthorizedApi> {\n        let url = format!(\"{}/login\", self.url);\n        let response = Request::post(&url).json(credentials)?.send().await?;\n        let token = into_json(response).await?;\n        Ok(AuthorizedApi::new(self.url, token))\n    }\n}\n\nimpl AuthorizedApi {\n    pub const fn new(url: &'static str, token: ApiToken) -> Self {\n        Self { url, token }\n    }\n    fn auth_header_value(&self) -> String {\n        format!(\"Bearer {}\", self.token.token)\n    }\n    async fn send<T>(&self, req: RequestBuilder) -> Result<T>\n    where\n        T: DeserializeOwned,\n    {\n        let response = req\n            .header(\"Authorization\", &self.auth_header_value())\n            .send()\n            .await?;\n        into_json(response).await\n    }\n    pub async fn logout(&self) -> Result<()> {\n        let url = format!(\"{}/logout\", self.url);\n        self.send(Request::post(&url)).await\n    }\n    pub async fn user_info(&self) -> Result<UserInfo> {\n        let url = format!(\"{}/users\", self.url);\n        self.send(Request::get(&url)).await\n    }\n    pub fn token(&self) -> &ApiToken {\n        &self.token\n    }\n}\n\ntype Result<T> = std::result::Result<T, Error>;\n\n#[derive(Debug, Error)]\npub enum Error {\n    #[error(transparent)]\n    Fetch(#[from] gloo_net::Error),\n    #[error(\"{0:?}\")]\n    Api(api_boundary::Error),\n}\n\nimpl From<api_boundary::Error> for Error {\n    fn from(e: api_boundary::Error) -> Self {\n        Self::Api(e)\n    }\n}\n\nasync fn into_json<T>(response: Response) -> Result<T>\nwhere\n    T: DeserializeOwned,\n{\n    // ensure we've got 2xx status\n    if response.ok() {\n        Ok(response.json().await?)\n    } else {\n        Err(response.json::<api_boundary::Error>().await?.into())\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/components/credentials.rs",
    "content": "use leptos::prelude::*;\n\n#[component]\npub fn CredentialsForm(\n    title: &'static str,\n    action_label: &'static str,\n    action: Action<(String, String), ()>,\n    error: Signal<Option<String>>,\n    disabled: Signal<bool>,\n) -> impl IntoView {\n    let (password, set_password) = create_signal(String::new());\n    let (email, set_email) = create_signal(String::new());\n\n    let dispatch_action =\n        move || action.dispatch((email.get(), password.get()));\n\n    let button_is_disabled = Signal::derive(move || {\n        disabled.get() || password.get().is_empty() || email.get().is_empty()\n    });\n\n    view! {\n        <form on:submit=|ev| ev.prevent_default()>\n            <p>{title}</p>\n            {move || {\n                error\n                    .get()\n                    .map(|err| {\n                        view! { <p style=\"color:red;\">{err}</p> }\n                    })\n            }}\n            <input\n                type=\"email\"\n                required\n                placeholder=\"Email address\"\n                prop:disabled=move || disabled.get()\n                on:keyup=move |ev: ev::KeyboardEvent| {\n                    let val = event_target_value(&ev);\n                    set_email.update(|v| *v = val);\n                }\n                on:change=move |ev| {\n                    let val = event_target_value(&ev);\n                    set_email.update(|v| *v = val);\n                }\n            />\n            <input\n                type=\"password\"\n                required\n                placeholder=\"Password\"\n                prop:disabled=move || disabled.get()\n                on:keyup=move |ev: ev::KeyboardEvent| {\n                    match &*ev.key() {\n                        \"Enter\" => {\n                            dispatch_action();\n                        }\n                        _ => {\n                            let val = event_target_value(&ev);\n                            set_password.update(|p| *p = val);\n                        }\n                    }\n                }\n                on:change=move |ev| {\n                    let val = event_target_value(&ev);\n                    set_password.update(|p| *p = val);\n                }\n            />\n            <button\n                prop:disabled=move || button_is_disabled.get()\n                on:click=move |_| dispatch_action()\n            >\n                {action_label}\n            </button>\n        </form>\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/components/mod.rs",
    "content": "pub mod credentials;\npub mod navbar;\n\npub use self::navbar::*;\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/components/navbar.rs",
    "content": "use crate::Page;\nuse leptos::prelude::*;\nuse leptos_router::*;\n\n#[component]\npub fn NavBar(\n    logged_in: Signal<bool>,\n    #[prop(into)] on_logout: Callback<()>,\n) -> impl IntoView {\n    view! {\n        <nav>\n            <Show\n                when=move || logged_in.get()\n                fallback=|| {\n                    view! {\n                        <A href=Page::Login.path()>\"Login\"</A>\n                        \" | \"\n                        <A href=Page::Register.path()>\"Register\"</A>\n                    }\n                }\n            >\n                <a\n                    href=\"#\"\n                    on:click=move |_| on_logout.call(())\n                >\n                    \"Logout\"\n                </a>\n            </Show>\n        </nav>\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/lib.rs",
    "content": "use api_boundary::*;\nuse gloo_storage::{LocalStorage, Storage};\nuse leptos::prelude::*;\nuse leptos_router::*;\n\nmod api;\nmod components;\nmod pages;\n\nuse self::{components::*, pages::*};\n\nconst DEFAULT_API_URL: &str = \"/api\";\nconst API_TOKEN_STORAGE_KEY: &str = \"api-token\";\n\n#[component]\npub fn App() -> impl IntoView {\n    // -- signals -- //\n\n    let authorized_api = RwSignal::new(None::<api::AuthorizedApi>);\n    let user_info = RwSignal::new(None::<UserInfo>);\n    let logged_in = Signal::derive(move || authorized_api.get().is_some());\n\n    // -- actions -- //\n\n    let fetch_user_info = create_action(move |_| async move {\n        match authorized_api.get() {\n            Some(api) => match api.user_info().await {\n                Ok(info) => {\n                    user_info.update(|i| *i = Some(info));\n                }\n                Err(err) => {\n                    log::error!(\"Unable to fetch user info: {err}\")\n                }\n            },\n            None => {\n                log::error!(\"Unable to fetch user info: not logged in\")\n            }\n        }\n    });\n\n    let logout = create_action(move |_| async move {\n        match authorized_api.get() {\n            Some(api) => match api.logout().await {\n                Ok(_) => {\n                    authorized_api.update(|a| *a = None);\n                    user_info.update(|i| *i = None);\n                }\n                Err(err) => {\n                    log::error!(\"Unable to logout: {err}\")\n                }\n            },\n            None => {\n                log::error!(\"Unable to logout user: not logged in\")\n            }\n        }\n    });\n\n    // -- callbacks -- //\n\n    let on_logout = move |_| {\n        logout.dispatch(());\n    };\n\n    // -- init API -- //\n\n    let unauthorized_api = api::UnauthorizedApi::new(DEFAULT_API_URL);\n    if let Ok(token) = LocalStorage::get(API_TOKEN_STORAGE_KEY) {\n        let api = api::AuthorizedApi::new(DEFAULT_API_URL, token);\n        authorized_api.update(|a| *a = Some(api));\n        fetch_user_info.dispatch(());\n    }\n\n    log::debug!(\"User is logged in: {}\", logged_in.get_untracked());\n\n    // -- effects -- //\n\n    create_effect(move |_| {\n        log::debug!(\"API authorization state changed\");\n        match authorized_api.get() {\n            Some(api) => {\n                log::debug!(\n                    \"API is now authorized: save token in LocalStorage\"\n                );\n                LocalStorage::set(API_TOKEN_STORAGE_KEY, api.token())\n                    .expect(\"LocalStorage::set\");\n            }\n            None => {\n                log::debug!(\n                    \"API is no longer authorized: delete token from \\\n                     LocalStorage\"\n                );\n                LocalStorage::delete(API_TOKEN_STORAGE_KEY);\n            }\n        }\n    });\n\n    view! {\n        <Router>\n            <NavBar logged_in on_logout/>\n            <main>\n                <Routes>\n                    <Route\n                        path=Page::Home.path()\n                        view=move || {\n                            view! { <Home user_info=user_info.into()/> }\n                        }\n                    />\n                    <Route\n                        path=Page::Login.path()\n                        view=move || {\n                            view! {\n                                <Login\n                                    api=unauthorized_api\n                                    on_success=move |api| {\n                                        log::info!(\"Successfully logged in\");\n                                        authorized_api.update(|v| *v = Some(api));\n                                        let navigate = use_navigate();\n                                        navigate(Page::Home.path(), Default::default());\n                                        fetch_user_info.dispatch(());\n                                    }\n                                />\n                            }\n                        }\n                    />\n                    <Route\n                        path=Page::Register.path()\n                        view=move || {\n                            view! { <Register api=unauthorized_api/> }\n                        }\n                    />\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/main.rs",
    "content": "use client::*;\nuse leptos::prelude::*;\n\npub fn main() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n    mount_to_body(|| view! { <App/> })\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/pages/home.rs",
    "content": "use crate::Page;\nuse api_boundary::UserInfo;\nuse leptos::prelude::*;\nuse leptos_router::*;\n\n#[component]\npub fn Home(user_info: Signal<Option<UserInfo>>) -> impl IntoView {\n    view! {\n        <h2>\"Leptos Login example\"</h2>\n        {move || match user_info.get() {\n            Some(info) => {\n                view! { <p>\"You are logged in with \" {info.email} \".\"</p> }\n                    .into_view()\n            }\n            None => {\n                view! {\n                    <p>\"You are not logged in.\"</p>\n                    <A href=Page::Login.path()>\"Login now.\"</A>\n                }\n                    .into_view()\n            }\n        }}\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/pages/login.rs",
    "content": "use crate::{\n    api::{self, AuthorizedApi, UnauthorizedApi},\n    components::credentials::*,\n    Page,\n};\nuse api_boundary::*;\nuse leptos::prelude::*;\nuse leptos_router::*;\n\n#[component]\npub fn Login(\n    api: UnauthorizedApi,\n    #[prop(into)] on_success: Callback<AuthorizedApi>,\n) -> impl IntoView {\n    let (login_error, set_login_error) = create_signal(None::<String>);\n    let (wait_for_response, set_wait_for_response) = create_signal(false);\n\n    let login_action =\n        create_action(move |(email, password): &(String, String)| {\n            log::debug!(\"Try to login with {email}\");\n            let email = email.to_string();\n            let password = password.to_string();\n            let credentials = Credentials { email, password };\n            async move {\n                set_wait_for_response.update(|w| *w = true);\n                let result = api.login(&credentials).await;\n                set_wait_for_response.update(|w| *w = false);\n                match result {\n                    Ok(res) => {\n                        set_login_error.update(|e| *e = None);\n                        on_success.call(res);\n                    }\n                    Err(err) => {\n                        let msg = match err {\n                            api::Error::Fetch(js_err) => {\n                                format!(\"{js_err:?}\")\n                            }\n                            api::Error::Api(err) => err.message,\n                        };\n                        log::error!(\n                            \"Unable to login with {}: {msg}\",\n                            credentials.email\n                        );\n                        set_login_error.update(|e| *e = Some(msg));\n                    }\n                }\n            }\n        });\n\n    let disabled = Signal::derive(move || wait_for_response.get());\n\n    view! {\n        <CredentialsForm\n            title=\"Please login to your account\"\n            action_label=\"Login\"\n            action=login_action\n            error=login_error.into()\n            disabled\n        />\n        <p>\"Don't have an account?\"</p>\n        <A href=Page::Register.path()>\"Register\"</A>\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/pages/mod.rs",
    "content": "pub mod home;\npub mod login;\npub mod register;\n\npub use self::{home::*, login::*, register::*};\n\n#[derive(Debug, Clone, Copy, Default)]\npub enum Page {\n    #[default]\n    Home,\n    Login,\n    Register,\n}\n\nimpl Page {\n    pub fn path(&self) -> &'static str {\n        match self {\n            Self::Home => \"/\",\n            Self::Login => \"/login\",\n            Self::Register => \"/register\",\n        }\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/client/src/pages/register.rs",
    "content": "use crate::{\n    api::{self, UnauthorizedApi},\n    components::credentials::*,\n    Page,\n};\nuse api_boundary::*;\nuse leptos::{logging::log, *};\nuse leptos_router::*;\n\n#[component]\npub fn Register(api: UnauthorizedApi) -> impl IntoView {\n    let (register_response, set_register_response) = create_signal(None::<()>);\n    let (register_error, set_register_error) = create_signal(None::<String>);\n    let (wait_for_response, set_wait_for_response) = create_signal(false);\n\n    let register_action =\n        create_action(move |(email, password): &(String, String)| {\n            let email = email.to_string();\n            let password = password.to_string();\n            let credentials = Credentials { email, password };\n            log!(\"Try to register new account for {}\", credentials.email);\n            async move {\n                set_wait_for_response.update(|w| *w = true);\n                let result = api.register(&credentials).await;\n                set_wait_for_response.update(|w| *w = false);\n                match result {\n                    Ok(res) => {\n                        set_register_response.update(|v| *v = Some(res));\n                        set_register_error.update(|e| *e = None);\n                    }\n                    Err(err) => {\n                        let msg = match err {\n                            api::Error::Fetch(js_err) => {\n                                format!(\"{js_err:?}\")\n                            }\n                            api::Error::Api(err) => err.message,\n                        };\n                        log::warn!(\n                            \"Unable to register new account for {}: {msg}\",\n                            credentials.email\n                        );\n                        set_register_error.update(|e| *e = Some(msg));\n                    }\n                }\n            }\n        });\n\n    let disabled = Signal::derive(move || wait_for_response.get());\n\n    view! {\n        <Show\n            when=move || register_response.get().is_some()\n            fallback=move || {\n                view! {\n                    <CredentialsForm\n                        title=\"Please enter the desired credentials\"\n                        action_label=\"Register\"\n                        action=register_action\n                        error=register_error.into()\n                        disabled\n                    />\n                    <p>\"Your already have an account?\"</p>\n                    <A href=Page::Login.path()>\"Login\"</A>\n                }\n            }\n        >\n            <p>\"You have successfully registered.\"</p>\n            <p>\"You can now \" <A href=Page::Login.path()>\"login\"</A> \" with your new account.\"</p>\n        </Show>\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/server/Cargo.toml",
    "content": "[package]\nname = \"server\"\nversion = \"0.0.0\"\nedition = \"2021\"\npublish = false\n\n[dependencies]\napi-boundary = { workspace = true }\n\nanyhow = \"1.0\"\naxum = \"0.7.5\"\naxum-extra = { version = \"0.9.3\", features = [\"typed-header\"] }\nenv_logger = \"0.11.5\"\nlog = \"0.4.22\"\nmailparse = \"0.15.0\"\npwhash = \"1.0\"\nthiserror = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\"] }\ntower-http = { version = \"0.5.2\", features = [\"cors\"] }\nuuid = { version = \"1.10\", features = [\"v4\"] }\nparking_lot = \"0.12.3\"\nheaders = \"0.4.0\"\n"
  },
  {
    "path": "projects/login_with_token_csr_only/server/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../../../\" }\n"
  },
  {
    "path": "projects/login_with_token_csr_only/server/src/adapters.rs",
    "content": "use crate::{application::*, Error};\nuse api_boundary as json;\nuse axum::{\n    http::StatusCode,\n    response::{IntoResponse, Json, Response},\n};\nuse thiserror::Error;\n\nimpl From<InvalidEmailAddress> for json::Error {\n    fn from(_: InvalidEmailAddress) -> Self {\n        Self {\n            message: \"Invalid email address\".to_string(),\n        }\n    }\n}\n\nimpl From<InvalidPassword> for json::Error {\n    fn from(err: InvalidPassword) -> Self {\n        let InvalidPassword::TooShort(min_len) = err;\n        Self {\n            message: format!(\"Invalid password (min. length = {min_len})\"),\n        }\n    }\n}\n\nimpl From<CreateUserError> for json::Error {\n    fn from(err: CreateUserError) -> Self {\n        let message = match err {\n            CreateUserError::UserExists => \"User already exits\".to_string(),\n        };\n        Self { message }\n    }\n}\n\nimpl From<LoginError> for json::Error {\n    fn from(err: LoginError) -> Self {\n        let message = match err {\n            LoginError::InvalidEmailOrPassword => {\n                \"Invalid email or password\".to_string()\n            }\n        };\n        Self { message }\n    }\n}\n\nimpl From<LogoutError> for json::Error {\n    fn from(err: LogoutError) -> Self {\n        let message = match err {\n            LogoutError::NotLoggedIn => \"No user is logged in\".to_string(),\n        };\n        Self { message }\n    }\n}\n\nimpl From<AuthError> for json::Error {\n    fn from(err: AuthError) -> Self {\n        let message = match err {\n            AuthError::NotAuthorized => \"Not authorized\".to_string(),\n        };\n        Self { message }\n    }\n}\n\nimpl From<CredentialParsingError> for json::Error {\n    fn from(err: CredentialParsingError) -> Self {\n        match err {\n            CredentialParsingError::EmailAddress(err) => err.into(),\n            CredentialParsingError::Password(err) => err.into(),\n        }\n    }\n}\n\n#[derive(Debug, Error)]\npub enum CredentialParsingError {\n    #[error(transparent)]\n    EmailAddress(#[from] InvalidEmailAddress),\n    #[error(transparent)]\n    Password(#[from] InvalidPassword),\n}\n\nimpl TryFrom<json::Credentials> for Credentials {\n    type Error = CredentialParsingError;\n    fn try_from(\n        json::Credentials { email, password }: json::Credentials,\n    ) -> Result<Self, Self::Error> {\n        let email: EmailAddress = email.parse()?;\n        let password = Password::try_from(password)?;\n        Ok(Self { email, password })\n    }\n}\n\nimpl IntoResponse for Error {\n    fn into_response(self) -> Response {\n        let (code, value) = match self {\n            Self::Logout(err) => {\n                (StatusCode::BAD_REQUEST, json::Error::from(err))\n            }\n            Self::Login(err) => {\n                (StatusCode::BAD_REQUEST, json::Error::from(err))\n            }\n            Self::Credentials(err) => {\n                (StatusCode::BAD_REQUEST, json::Error::from(err))\n            }\n            Self::CreateUser(err) => {\n                (StatusCode::BAD_REQUEST, json::Error::from(err))\n            }\n            Self::Auth(err) => {\n                (StatusCode::UNAUTHORIZED, json::Error::from(err))\n            }\n        };\n        (code, Json(value)).into_response()\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/server/src/application.rs",
    "content": "use mailparse::addrparse;\nuse pwhash::bcrypt;\nuse std::{collections::HashMap, str::FromStr};\nuse thiserror::Error;\nuse uuid::Uuid;\n\n#[derive(Default)]\npub struct AppState {\n    users: HashMap<EmailAddress, Password>,\n    tokens: HashMap<Uuid, EmailAddress>,\n}\n\nimpl AppState {\n    pub fn create_user(\n        &mut self,\n        credentials: Credentials,\n    ) -> Result<(), CreateUserError> {\n        let Credentials { email, password } = credentials;\n        let user_exists = self.users.contains_key(&email);\n        if user_exists {\n            return Err(CreateUserError::UserExists);\n        }\n        self.users.insert(email, password);\n        Ok(())\n    }\n\n    pub fn login(\n        &mut self,\n        email: EmailAddress,\n        password: &str,\n    ) -> Result<Uuid, LoginError> {\n        let valid_credentials = self\n            .users\n            .get(&email)\n            .map(|hashed_password| hashed_password.verify(password))\n            .unwrap_or(false);\n        if !valid_credentials {\n            Err(LoginError::InvalidEmailOrPassword)\n        } else {\n            let token = Uuid::new_v4();\n            self.tokens.insert(token, email);\n            Ok(token)\n        }\n    }\n\n    pub fn logout(&mut self, token: &str) -> Result<(), LogoutError> {\n        let token = token\n            .parse::<Uuid>()\n            .map_err(|_| LogoutError::NotLoggedIn)?;\n        self.tokens.remove(&token);\n        Ok(())\n    }\n\n    pub fn authorize_user(\n        &self,\n        token: &str,\n    ) -> Result<CurrentUser, AuthError> {\n        token\n            .parse::<Uuid>()\n            .map_err(|_| AuthError::NotAuthorized)\n            .and_then(|token| {\n                self.tokens\n                    .get(&token)\n                    .cloned()\n                    .map(|email| CurrentUser { email, token })\n                    .ok_or(AuthError::NotAuthorized)\n            })\n    }\n}\n\n#[derive(Debug, Error)]\npub enum CreateUserError {\n    #[error(\"The user already exists\")]\n    UserExists,\n}\n\n#[derive(Debug, Error)]\npub enum LoginError {\n    #[error(\"Invalid email or password\")]\n    InvalidEmailOrPassword,\n}\n\n#[derive(Debug, Error)]\npub enum LogoutError {\n    #[error(\"You are not logged in\")]\n    NotLoggedIn,\n}\n\n#[derive(Debug, Error)]\npub enum AuthError {\n    #[error(\"You are not authorized\")]\n    NotAuthorized,\n}\n\npub struct Credentials {\n    pub email: EmailAddress,\n    pub password: Password,\n}\n\n#[derive(Clone, Eq, PartialEq, Hash)]\npub struct EmailAddress(String);\n\n#[derive(Debug, Error)]\n#[error(\"The given email address is invalid\")]\npub struct InvalidEmailAddress;\n\nimpl FromStr for EmailAddress {\n    type Err = InvalidEmailAddress;\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        addrparse(s)\n            .ok()\n            .and_then(|parsed| parsed.extract_single_info())\n            .map(|single_info| Self(single_info.addr))\n            .ok_or(InvalidEmailAddress)\n    }\n}\n\nimpl EmailAddress {\n    pub fn into_string(self) -> String {\n        self.0\n    }\n}\n\n#[derive(Clone)]\npub struct CurrentUser {\n    pub email: EmailAddress,\n    #[allow(dead_code)] // possibly a lint regression, this is used at line 65\n    pub token: Uuid,\n}\n\nconst MIN_PASSWORD_LEN: usize = 3;\n\npub struct Password(String);\n\nimpl Password {\n    pub fn verify(&self, password: &str) -> bool {\n        bcrypt::verify(password, &self.0)\n    }\n}\n\n#[derive(Debug, Error)]\npub enum InvalidPassword {\n    #[error(\"Password is too short (min. length is {0})\")]\n    TooShort(usize),\n}\n\nimpl TryFrom<String> for Password {\n    type Error = InvalidPassword;\n    fn try_from(p: String) -> Result<Self, Self::Error> {\n        if p.len() < MIN_PASSWORD_LEN {\n            return Err(InvalidPassword::TooShort(MIN_PASSWORD_LEN));\n        }\n        let hashed = bcrypt::hash(&p).unwrap();\n        Ok(Self(hashed))\n    }\n}\n"
  },
  {
    "path": "projects/login_with_token_csr_only/server/src/main.rs",
    "content": "use api_boundary as json;\nuse axum::{\n    extract::State,\n    http::Method,\n    response::Json,\n    routing::{get, post},\n    Router,\n};\nuse axum_extra::TypedHeader;\nuse headers::{authorization::Bearer, Authorization};\nuse parking_lot::RwLock;\nuse std::{env, net::SocketAddr, sync::Arc};\nuse tokio::net::TcpListener;\nuse tower_http::cors::{Any, CorsLayer};\n\nmod adapters;\nmod application;\n\nuse self::application::*;\n\n#[tokio::main]\nasync fn main() -> anyhow::Result<()> {\n    if let Err(err) = env::var(\"RUST_LOG\") {\n        match err {\n            env::VarError::NotPresent => {\n                env::set_var(\"RUST_LOG\", \"debug\");\n            }\n            env::VarError::NotUnicode(_) => {\n                return Err(anyhow::anyhow!(\n                    \"The value of 'RUST_LOG' does not contain valid unicode \\\n                     data.\"\n                ));\n            }\n        }\n    }\n    env_logger::init();\n\n    let shared_state = Arc::new(RwLock::new(AppState::default()));\n\n    let cors_layer = CorsLayer::new()\n        .allow_methods([Method::GET, Method::POST])\n        .allow_origin(Any);\n\n    let app = Router::new()\n        .route(\"/login\", post(login))\n        .route(\"/logout\", post(logout))\n        .route(\"/users\", post(create_user))\n        .route(\"/users\", get(get_user_info))\n        .route_layer(cors_layer)\n        .with_state(shared_state);\n\n    let addr = \"0.0.0.0:3000\".parse::<SocketAddr>()?;\n    log::info!(\"Start listening on http://{addr}\");\n    let listener = TcpListener::bind(addr).await?;\n    axum::serve(listener, app.into_make_service()).await?;\n    Ok(())\n}\n\ntype Result<T> = std::result::Result<Json<T>, Error>;\n\n/// API error\n#[derive(thiserror::Error, Debug)]\n#[non_exhaustive]\nenum Error {\n    #[error(transparent)]\n    CreateUser(#[from] CreateUserError),\n    #[error(transparent)]\n    Login(#[from] LoginError),\n    #[error(transparent)]\n    Logout(#[from] LogoutError),\n    #[error(transparent)]\n    Auth(#[from] AuthError),\n    #[error(transparent)]\n    Credentials(#[from] adapters::CredentialParsingError),\n}\n\nasync fn create_user(\n    State(state): State<Arc<RwLock<AppState>>>,\n    Json(credentials): Json<json::Credentials>,\n) -> Result<()> {\n    let credentials = Credentials::try_from(credentials)?;\n    state.write().create_user(credentials)?;\n    Ok(Json(()))\n}\n\nasync fn login(\n    State(state): State<Arc<RwLock<AppState>>>,\n    Json(credentials): Json<json::Credentials>,\n) -> Result<json::ApiToken> {\n    let json::Credentials { email, password } = credentials;\n    log::debug!(\"{email} tries to login\");\n    let email = email.parse().map_err(|_|\n        // Here we don't want to leak detailed info.\n        LoginError::InvalidEmailOrPassword)?;\n    let token = state\n        .write()\n        .login(email, &password)\n        .map(|s| s.to_string())?;\n    Ok(Json(json::ApiToken { token }))\n}\n\nasync fn logout(\n    State(state): State<Arc<RwLock<AppState>>>,\n    TypedHeader(auth): TypedHeader<Authorization<Bearer>>,\n) -> Result<()> {\n    state.write().logout(auth.token())?;\n    Ok(Json(()))\n}\n\nasync fn get_user_info(\n    State(state): State<Arc<RwLock<AppState>>>,\n    TypedHeader(auth): TypedHeader<Authorization<Bearer>>,\n) -> Result<json::UserInfo> {\n    let user = state.read().authorize_user(auth.token())?;\n    let CurrentUser { email, .. } = user;\n    Ok(Json(json::UserInfo {\n        email: email.into_string(),\n    }))\n}\n"
  },
  {
    "path": "projects/meilisearch-searchbar/.gitignore",
    "content": "/target\nmeilisearch\ndata.ms\ndumps\nCargo.lock"
  },
  {
    "path": "projects/meilisearch-searchbar/Cargo.toml",
    "content": "[package]\nname = \"meilisearch_searchbar\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nleptos = { version = \"0.6.5\", features = [\"nightly\"] }\nleptos_axum = { version = \"0.6.5\", optional = true }\nmeilisearch-sdk = { version = \"0.27.0\", optional = true }\naxum = { version = \"0.7.4\", optional = true }\nleptos_meta = { version = \"0.6.5\", features = [\"nightly\"] }\nleptos_router = { version = \"0.6.5\", features = [\"nightly\"] }\nconsole_log = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nlog = \"0.4.20\"\ntower = { verison = \"0.4.13\", optional = true }\ntower-http = { version = \"0.5.1\", optional = true, features = [\"fs\"] }\nsimple_logger = { version = \"5.0\", optional = true }\ntokio = { version = \"1.0\", features = [\"full\"], optional = true }\nlazy_static = { version = \"1.4\", optional = true }\nserde = \"1.0\"\nserde_json = \"1.0\"\ncsv = { version = \"1.3\", optional = true }\n\n[features]\ndefault = [\"ssr\"]\nhydrate = [\"leptos/hydrate\", \"leptos_meta/hydrate\", \"leptos_router/hydrate\"]\nssr = [\n  \"tokio\",\n  \"lazy_static\",\n  \"simple_logger\",\n  \"dep:meilisearch-sdk\",\n  \"dep:axum\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"tower\",\n  \"tower-http\",\n  \"leptos_axum\",\n  \"csv\",\n]\nlazy_static = [\"dep:lazy_static\"]\n\n[package.metadata.leptos]\noutput-name = \"meilisearch_searchbar\"\nsite-root = \"target/site\"\nsite-pkg-dir = \"pkg\"\nassets-dir = \"public\"\nsite-addr = \"127.0.0.1:3000\"\nreload-port = 3001\nbrowserquery = \"defaults\"\nwatch = false\nenv = \"DEV\"\nbin-features = [\"ssr\"]\nbin-default-features = false\nlib-features = [\"hydrate\"]\nlib-default-features = false\n"
  },
  {
    "path": "projects/meilisearch-searchbar/Makefile.toml",
    "content": "[tasks.ci]\ndescription = \"Continuous Integration task\"\ncommand = \"cargo\"\nargs = [\"test\"] \n"
  },
  {
    "path": "projects/meilisearch-searchbar/README.md",
    "content": "# Meilisearch Searchbar\n\nThis show how to integrate meilisearch with a leptos app, including a search bar and showing the results to the user.\n<br><br>\nWe'll run meilisearch locally, as opposed to using their cloud service.\n<br><br>\nTo get started install meilisearch into this example's root.\n\n```sh\ncurl -L https://install.meilisearch.com | sh\n```\n\nRun it.\n\n```sh\n./meilisearch\n```\n\nThen set the environment variable and serve the app. I've included the address of my own local meilisearch server.\nI didn't provide a password to meilisearch during my setup, and I didn't provide one in my environment variables either.\n```sh\nMEILISEARCH_URL=http://localhost:7700 && cargo leptos serve\n```\n\nNavigate to 127.0.0.1:3000 and start typing in popular American company names. (Boeing, Pepsi, etc)\n\n## Thoughts, Feedback, Criticism, Comments?\nSend me any of the above, I'm @sjud on leptos discord. I'm always looking to improve and make these projects more helpful for the community. So please let me know how I can do that. Thanks!"
  },
  {
    "path": "projects/meilisearch-searchbar/data_set.csv",
    "content": "name,last,high,low,absolute_change,percent_change,vol\n\"Boeing\",\"209.16\",\"211.41\",\"207.91\",-0.06,-0.03,4310000\n\"General Motors\",\"38.56\",\"38.97\",\"38.45\",-0.09,-0.23,11720000\n\"Chevron\",\"151.02\",\"155.31\",\"150.99\",-3.04,-1.97,8040000\n\"Citigroup\",\"53.98\",\"54.44\",\"53.53\",-0.31,-0.57,11750000\n\"Bank of America\",\"33.05\",\"33.25\",\"32.83\",-0.07,-0.2,30560000\n\"AT&T\",\"16.83\",\"16.88\",\"16.57\",0.01,0.06,40000000\n\"Caterpillar\",\"316.89\",\"322.33\",\"315.57\",-5.11,-1.59,2780000\n\"Intel\",\"43.31\",\"43.52\",\"42.40\",0.81,1.91,43560000\n\"Microsoft\",\"420.55\",\"420.82\",\"415.09\",6.44,1.56,21300000\n\"Ford Motor\",\"12.68\",\"12.92\",\"12.64\",-0.15,-1.17,44810000\n\"eBay\",\"42.43\",\"42.69\",\"41.79\",0.41,0.98,4340000\n\"Walt Disney\",\"108.37\",\"110.14\",\"107.69\",-2.17,-1.96,19100000\n\"Dow\",\"53.99\",\"54.12\",\"53.56\",0.11,0.2,3730000\n\"Cisco\",\"50.10\",\"50.26\",\"49.63\",0.15,0.31,21620000\n\"Deere&Company\",\"381.32\",\"385.30\",\"380.46\",-4.51,-1.17,1410000\n\"FedEx\",\"242.59\",\"242.82\",\"240.14\",0.84,0.35,1720000\n\"General Mills\",\"62.34\",\"63.86\",\"62.09\",-1.73,-2.69,4070000\n\"Corning\",\"32.04\",\"32.05\",\"31.58\",0.31,0.98,2760000\n\"Goldman Sachs\",\"384.21\",\"386.13\",\"382.58\",-0.83,-0.22,1620000\n\"JPMorgan\",\"174.96\",\"175.10\",\"173.67\",0.16,0.09,4270000\n\"Kimberly-Clark\",\"119.81\",\"120.47\",\"119.12\",-0.46,-0.38,1570000\n\"Kraft Heinz\",\"35.97\",\"36.38\",\"35.80\",-0.5,-1.37,8020000\n\"Coca-Cola\",\"59.56\",\"59.58\",\"59.03\",-0.27,-0.45,12020000\n\"McDonald’s\",\"289.41\",\"292.49\",\"288.94\",-2.52,-0.86,3140000\n\"Eli Lilly\",\"739.51\",\"745.68\",\"733.61\",3.83,0.52,2410000\n\"Oracle\",\"116.60\",\"117.34\",\"115.75\",-0.08,-0.07,4990000\n\"Merck&Co\",\"125.43\",\"126.69\",\"125.04\",-1.18,-0.93,5890000\n\"Motorola\",\"330.98\",\"333.00\",\"323.23\",2.63,0.8,1180000\n\"3M\",\"92.89\",\"93.30\",\"92.39\",-0.31,-0.33,3620000\n\"Vertex\",\"422.91\",\"425.96\",\"419.51\",-0.15,-0.04,1100000\n\"Monster Beverage\",\"55.66\",\"56.57\",\"55.35\",-0.83,-1.47,3760000\n\"Fifth Third\",\"33.65\",\"33.74\",\"33.02\",0.26,0.78,4030000\n\"Cintas\",\"618.22\",\"620.32\",\"614.07\",1.06,0.17,248630\n\"Autodesk\",\"266.68\",\"269.53\",\"262.85\",5.17,1.98,1730000\n\"Gilead\",\"73.67\",\"74.11\",\"72.81\",-0.13,-0.18,10150000\n\"Alphabet A\",\"149.00\",\"149.44\",\"146.18\",3.09,2.12,26030000\n\"Fiserv\",\"144.23\",\"144.34\",\"142.74\",0.68,0.47,1730000\n\"Adobe\",\"627.21\",\"628.07\",\"615.80\",11.35,1.84,1950000\n\"Qualcomm\",\"151.00\",\"153.40\",\"148.35\",2.82,1.9,11310000\n\"Warner Bros Discovery\",\"9.64\",\"9.89\",\"9.57\",-0.19,-1.93,22450000\n\"Applied Materials\",\"185.84\",\"186.15\",\"178.62\",11.95,6.87,9240000\n\"Steel Dynamics\",\"124.99\",\"125.66\",\"124.21\",0.15,0.12,1120000\n\"Cadence Design\",\"311.94\",\"313.11\",\"308.82\",4.58,1.49,1300000\n\"Microchip\",\"85.44\",\"86.00\",\"84.67\",1.09,1.29,4750000\n\"Wynn Resorts\",\"105.60\",\"106.89\",\"104.26\",-0.51,-0.48,2400000\n\"Intuitive Surgical\",\"388.22\",\"389.92\",\"383.05\",1.28,0.33,1100000\n\"Nasdaq Inc\",\"57.25\",\"57.35\",\"56.65\",0.49,0.86,1250000\n\"Henry Schein\",\"73.72\",\"75.18\",\"73.43\",-1.18,-1.58,1330000\n\"Paychex\",\"123.03\",\"123.25\",\"121.84\",0.28,0.23,1680000\n\"VeriSign\",\"198.38\",\"203.00\",\"197.08\",-2.64,-1.31,1470000\n\"Apple\",\"188.85\",\"189.99\",\"188.00\",0.77,0.41,43180000\n\"Fastenal\",\"70.02\",\"70.06\",\"69.16\",0.76,1.1,2610000\n\"Dentsply\",\"32.79\",\"33.16\",\"32.73\",-0.37,-1.12,3230000\n\"Zions\",\"40.09\",\"40.41\",\"39.45\",0.2,0.5,2070000\n\"Northern Trust\",\"79.64\",\"79.73\",\"78.25\",1.17,1.49,1120000\n\"CH Robinson\",\"74.67\",\"74.85\",\"73.59\",0.01,0.01,775730\n\"PACCAR\",\"106.01\",\"106.08\",\"104.96\",0.4,0.38,1400000\n\"Amazon.com\",\"174.45\",\"175.00\",\"170.58\",4.61,2.71,52940000\n\"Ross Stores\",\"145.87\",\"146.42\",\"145.20\",-0.19,-0.13,1260000\n\"NetApp\",\"89.85\",\"90.33\",\"88.78\",0.93,1.05,1360000\n\"Garmin\",\"123.21\",\"123.70\",\"121.90\",0.89,0.73,580350\n\"Costco\",\"723.40\",\"725.53\",\"720.12\",-0.76,-0.1,1370000\n\"Lam Research\",\"911.58\",\"913.82\",\"874.86\",47.22,5.46,1820000\n\"Intuit\",\"658.16\",\"662.83\",\"654.69\",5.09,0.78,923900\n\"Expedia\",\"131.11\",\"132.80\",\"126.05\",-28.36,-17.78,18410000\n\"Cognizant A\",\"77.10\",\"78.57\",\"76.87\",-1.2,-1.53,5250000\n\"Akamai\",\"128.32\",\"129.16\",\"127.04\",1.54,1.21,1690000\n\"KLA Corp\",\"649.80\",\"651.26\",\"628.55\",31.31,5.06,1240000\n\"Juniper\",\"37.03\",\"37.06\",\"36.93\",0.08,0.22,2540000\n\"Amgen\",\"291.12\",\"295.00\",\"289.72\",-3.73,-1.27,3270000\n\"Expeditors Washington\",\"127.39\",\"127.39\",\"125.52\",0.21,0.17,837390\n\"Electronic Arts\",\"140.61\",\"140.98\",\"137.00\",1.76,1.27,1950000\n\"T Rowe\",\"106.33\",\"109.83\",\"105.90\",-2.58,-2.37,2420000\n\"Biogen\",\"240.98\",\"241.36\",\"238.90\",0.68,0.28,665370\n\"Charles Schwab\",\"63.37\",\"63.41\",\"62.35\",0.9,1.44,4530000\n\"Huntington Bancshares\",\"12.41\",\"12.48\",\"12.19\",0.09,0.73,13750000\n\"Gen Digital\",\"21.35\",\"21.39\",\"20.94\",0.48,2.3,5700000\n\"NVIDIA\",\"721.33\",\"721.85\",\"702.12\",24.92,3.58,42810000\n\"Starbucks\",\"97.30\",\"97.99\",\"96.31\",0.71,0.74,9300000\n\"Cincinnati Financial\",\"108.89\",\"109.48\",\"107.12\",1.29,1.2,610810\n\"Axon Enterprise\",\"270.98\",\"271.39\",\"268.44\",1.98,0.74,191750\n\"Hologic\",\"73.44\",\"74.14\",\"73.09\",0.01,0.01,1460000\n\"Comcast\",\"42.07\",\"42.28\",\"41.22\",0.82,1.99,26670000\n\"Medtronic\",\"84.97\",\"86.15\",\"84.46\",-1.21,-1.4,7820000\n\"Dover\",\"160.46\",\"160.93\",\"159.36\",0.35,0.22,999650\n\"Northrop Grumman\",\"454.99\",\"455.56\",\"450.52\",3.77,0.84,752290\n\"MGM\",\"46.74\",\"46.86\",\"46.21\",0.31,0.67,3840000\n\"Mastercard\",\"457.74\",\"458.98\",\"456.37\",-0.52,-0.11,2050000\n\"General Dynamics\",\"270.19\",\"271.36\",\"269.05\",1.19,0.44,728860\n\"DTE Energy\",\"104.43\",\"104.67\",\"103.14\",0.2,0.19,885020\n\"Analog Devices\",\"195.02\",\"195.53\",\"193.45\",0.96,0.49,3150000\n\"VF\",\"15.30\",\"15.50\",\"15.04\",-0.05,-0.33,7200000\n\"Cardinal Health\",\"104.94\",\"105.42\",\"103.28\",2.2,2.15,1990000\n\"Xcel Energy\",\"58.17\",\"58.20\",\"57.67\",0.17,0.29,2320000\n\"DR Horton\",\"144.93\",\"145.31\",\"142.95\",0.34,0.24,1400000\n\"IPG\",\"30.76\",\"31.71\",\"30.60\",-1.04,-3.27,6450000\n\"Lockheed Martin\",\"426.42\",\"428.00\",\"424.47\",-0.58,-0.14,887810\n\"Waters\",\"322.90\",\"327.57\",\"320.82\",-3.79,-1.16,397410\n\"Accenture\",\"371.63\",\"372.48\",\"367.95\",3.15,0.85,1420000\n\"Dominion Energy\",\"44.70\",\"44.72\",\"43.92\",0.52,1.18,2710000\n\"Exxon Mobil\",\"101.75\",\"104.84\",\"101.70\",-2.22,-2.14,16690000\n\"Cigna\",\"334.85\",\"335.83\",\"330.67\",2.56,0.77,1300000\n\"Public Service Enterprise\",\"58.41\",\"58.62\",\"57.83\",0.21,0.36,1360000\n\"NiSource\",\"25.24\",\"25.26\",\"24.96\",0.18,0.72,2330000\n\"Zimmer Biomet\",\"122.78\",\"125.31\",\"121.19\",-0.26,-0.21,3110000\n\"CSX\",\"36.89\",\"37.22\",\"36.74\",-0.18,-0.49,7940000\n\"ICE\",\"135.52\",\"135.69\",\"132.92\",1.4,1.04,3850000\n\"Southwest Airlines\",\"32.50\",\"32.64\",\"31.80\",0.17,0.53,8060000\n\"Illinois Tool Works\",\"255.71\",\"255.77\",\"253.63\",1.2,0.47,655100\n\"Darden Restaurants\",\"167.65\",\"168.34\",\"167.16\",-0.53,-0.32,549580\n\"Truist Financial Corp\",\"35.83\",\"36.07\",\"34.88\",0.32,0.9,9970000\n\"Halliburton\",\"34.50\",\"35.15\",\"34.43\",-0.53,-1.51,4550000\n\"Prologis\",\"132.45\",\"132.77\",\"130.45\",0.79,0.6,2780000\n\"McCormick&Co\",\"64.64\",\"65.94\",\"64.33\",-1.18,-1.79,1390000\n\"Host Hotels Resorts\",\"19.58\",\"19.62\",\"19.32\",0.03,0.15,4010000\n\"Estee Lauder\",\"143.33\",\"145.23\",\"140.61\",2.56,1.82,2340000\n\"International Paper\",\"35.26\",\"35.32\",\"34.63\",0.37,1.06,3130000\n\"Emerson\",\"103.19\",\"103.32\",\"102.04\",0.81,0.79,3340000\n\"Clorox\",\"153.12\",\"155.46\",\"152.44\",-1.1,-0.71,753850\n\"ConocoPhillips\",\"111.11\",\"114.26\",\"111.02\",-2.79,-2.45,5070000\n\"Colgate-Palmolive\",\"83.46\",\"84.26\",\"83.18\",-0.8,-0.95,3460000\n\"Pinnacle West\",\"67.02\",\"67.02\",\"66.18\",0.63,0.95,944540\n\"Regions Financial\",\"17.99\",\"18.10\",\"17.72\",0.1,0.56,9480000\n\"CenterPoint Energy\",\"27.53\",\"27.60\",\"27.30\",0.1,0.36,2090000\n\"MetLife\",\"67.49\",\"67.67\",\"66.40\",0.74,1.11,4590000\n\"Exelon\",\"33.84\",\"33.90\",\"33.35\",0.09,0.27,6090000\n\"Baxter\",\"39.55\",\"40.52\",\"38.79\",-0.9,-2.24,4580000\n\"Occidental\",\"57.46\",\"58.34\",\"57.24\",-0.59,-1.02,7450000\n\"Southern\",\"66.91\",\"67.20\",\"66.50\",-0.03,-0.04,3250000\n\"Tapestry\",\"42.01\",\"42.80\",\"41.27\",-0.98,-2.28,6310000\n\"Lennar\",\"153.03\",\"153.80\",\"151.32\",-0.26,-0.17,1370000\n\"Campbell Soup\",\"42.00\",\"42.91\",\"41.72\",-1.1,-2.55,2910000\n\"State Street\",\"72.81\",\"72.98\",\"72.11\",0.43,0.59,1270000\n\"Progressive\",\"182.52\",\"184.00\",\"182.30\",-0.44,-0.24,1710000\n\"Vulcan Materials\",\"240.05\",\"240.10\",\"237.40\",1.61,0.68,1020000\n\"Parker-Hannifin\",\"521.42\",\"521.50\",\"513.99\",5.91,1.15,472760\n\"Genuine Parts\",\"143.21\",\"143.21\",\"141.43\",0.9,0.63,742180\n\"CBRE A\",\"86.54\",\"86.64\",\"84.77\",0.58,0.67,1230000\n\"DuPont De Nemours\",\"67.66\",\"67.72\",\"66.57\",0.56,0.83,3220000\n\"Sherwin-Williams\",\"311.77\",\"312.99\",\"309.39\",-0.38,-0.12,885490\n\"Pfizer\",\"27.55\",\"27.59\",\"27.38\",-0.01,-0.05,24950000\n\"Wells Fargo&Co\",\"48.05\",\"48.35\",\"47.47\",-0.3,-0.62,13910000\n\"Walmart\",\"169.27\",\"169.73\",\"168.92\",-0.1,-0.06,3930000\n\"Edison\",\"64.69\",\"64.96\",\"64.34\",-0.03,-0.05,1470000\n\"Snap-On\",\"262.24\",\"268.14\",\"261.30\",-3.79,-1.42,459630\n\"Equifax\",\"249.23\",\"253.48\",\"246.07\",-2.83,-1.12,828980\n\"McKesson\",\"501.35\",\"504.31\",\"494.50\",7.12,1.44,1030000\n\"Entergy\",\"97.94\",\"98.02\",\"96.80\",0.54,0.55,917720\n\"CMS Energy\",\"56.06\",\"56.11\",\"55.61\",0.31,0.55,1290000\n\"Ameriprise Financial\",\"396.45\",\"398.15\",\"392.95\",1.62,0.41,235380\n\"AIG\",\"69.11\",\"69.23\",\"68.06\",0.7,1.03,3020000\n\"Ralph Lauren A\",\"175.04\",\"175.40\",\"169.60\",3.19,1.86,1730000\n\"Bath & Body Works\",\"44.74\",\"44.78\",\"43.79\",0.37,0.83,1500000\n\"IFF\",\"79.93\",\"80.30\",\"78.72\",-0.58,-0.72,3130000\n\"WW Grainger\",\"959.38\",\"961.91\",\"946.13\",11.31,1.19,194930\n\"Constellation Brands A\",\"242.37\",\"244.51\",\"241.65\",-0.68,-0.28,1170000\n\"American Tower\",\"194.41\",\"194.49\",\"191.64\",0.77,0.4,1710000\n\"Philip Morris\",\"89.12\",\"89.43\",\"88.55\",0.11,0.12,4300000\n\"Fidelity National Info\",\"61.78\",\"62.11\",\"61.40\",0.34,0.55,2680000\n\"Altria\",\"40.10\",\"40.15\",\"39.88\",0.01,0.02,8110000\n\"Ball\",\"59.16\",\"59.26\",\"58.21\",0.02,0.03,1350000\n\"Hartford\",\"90.80\",\"90.91\",\"89.31\",1.07,1.19,1510000\n\"Hershey Co\",\"195.39\",\"201.79\",\"194.68\",-6.92,-3.42,2950000\n\"Morgan Stanley\",\"85.88\",\"86.03\",\"85.32\",0.23,0.27,4620000\n\"PNC Financial\",\"147.76\",\"148.38\",\"146.68\",-0.17,-0.11,1120000\n\"Waste Management\",\"188.87\",\"189.78\",\"187.56\",-0.62,-0.33,1570000\n\"Cencora Inc\",\"230.69\",\"231.60\",\"228.77\",0.37,0.16,1370000\n\"Assurant\",\"174.48\",\"175.06\",\"171.86\",-0.09,-0.05,220360\n\"Kroger\",\"45.41\",\"45.54\",\"45.10\",-0.03,-0.07,3120000\n\"Molson Coors Brewing B\",\"60.23\",\"60.24\",\"59.37\",-0.04,-0.06,2060000\n\"Home Depot\",\"363.09\",\"364.43\",\"360.80\",-0.63,-0.17,1760000\n\"Becton Dickinson\",\"243.66\",\"244.05\",\"240.37\",2.95,1.23,2040000\n\"JM Smucker\",\"127.90\",\"130.43\",\"127.67\",-3.19,-2.43,894090\n\"Best Buy\",\"75.58\",\"75.68\",\"74.76\",0.09,0.12,1410000\n\"Archer-Daniels-Midland\",\"53.07\",\"53.26\",\"52.34\",0.37,0.7,4250000\n\"Brown Forman\",\"56.59\",\"56.94\",\"56.31\",-0.34,-0.6,847500\n\"IBM\",\"186.33\",\"187.18\",\"183.86\",1.97,1.07,4970000\n\"Union Pacific\",\"249.45\",\"249.55\",\"246.51\",0.63,0.25,1860000\n\"Micron\",\"85.56\",\"85.62\",\"83.96\",0.68,0.8,12070000\n\"Avery Dennison\",\"204.92\",\"205.15\",\"202.01\",1.95,0.96,409860\n\"Marathon Oil\",\"22.45\",\"22.87\",\"22.37\",-0.25,-1.1,8040000\n\"CF Industries\",\"78.08\",\"78.51\",\"76.67\",1.57,2.05,1780000\n\"APA Corp\",\"29.87\",\"30.69\",\"29.80\",-0.6,-1.97,5240000\n\"Duke Energy\",\"91.69\",\"92.72\",\"91.63\",-0.96,-1.04,3900000\n\"KeyCorp\",\"13.88\",\"13.97\",\"13.64\",0.01,0.11,13200000\n\"Laboratory America\",\"222.58\",\"223.60\",\"222.18\",-0.22,-0.1,376300\n\"Boston Properties\",\"64.18\",\"65.36\",\"63.60\",-0.89,-1.37,1240000\n\"Western Digital\",\"56.82\",\"57.35\",\"56.25\",-0.3,-0.53,5200000\n\"PPG Industries\",\"139.58\",\"139.61\",\"137.46\",0.95,0.69,949590\n\"S&P Global\",\"438.02\",\"441.04\",\"431.33\",1.39,0.32,1770000\n\"Williams\",\"34.03\",\"34.26\",\"33.85\",-0.01,-0.01,5830000\n\"Elevance Health\",\"506.07\",\"506.82\",\"500.18\",3.92,0.78,700780\n\"Jacobs Engineering\",\"145.54\",\"145.54\",\"143.12\",2.37,1.66,737440\n\"Eastman Chemical\",\"82.32\",\"82.83\",\"81.90\",-0.67,-0.81,622510\n\"Verizon\",\"39.72\",\"40.09\",\"39.26\",-0.19,-0.49,15090000\n\"Nucor\",\"186.52\",\"187.10\",\"185.18\",0.23,0.12,1170000\n\"Omnicom\",\"84.58\",\"87.09\",\"84.45\",-2.05,-2.37,1550000\n\"AvalonBay\",\"174.62\",\"174.63\",\"173.20\",0.09,0.05,573430\n\"Marriott Int\",\"247.02\",\"250.75\",\"245.45\",-2.56,-1.03,1370000\n\"Ingersoll Rand\",\"85.89\",\"86.51\",\"85.27\",0.51,0.6,3670000\n\"Bristol-Myers Squibb\",\"49.80\",\"49.83\",\"48.49\",1.09,2.24,13400000\n\"American Electric Power\",\"76.66\",\"76.73\",\"75.57\",0.72,0.95,2010000\n\"Thermo Fisher Scientific\",\"550.82\",\"554.13\",\"548.29\",-0.07,-0.01,1160000\n\"Newmont Goldcorp\",\"32.79\",\"33.22\",\"32.54\",-0.55,-1.65,10450000\n\"Public Storage\",\"284.04\",\"286.34\",\"280.59\",-0.86,-0.3,544300\n\"Travelers\",\"214.50\",\"214.99\",\"212.04\",0.69,0.32,793950\n\"Stanley Black Decker\",\"88.93\",\"89.45\",\"88.56\",-0.34,-0.38,778810\n\"Franklin Resources\",\"27.09\",\"27.12\",\"26.58\",0.26,0.97,2210000\n\"Humana\",\"370.16\",\"371.16\",\"366.30\",2.36,0.64,1110000\n\"Paramount Global B\",\"12.90\",\"13.17\",\"12.84\",-0.11,-0.85,9380000\n\"Chubb\",\"247.20\",\"247.26\",\"243.32\",2.86,1.17,1370000\n\"J&J\",\"156.74\",\"157.20\",\"155.71\",0.34,0.22,6280000\n\"Tyson Foods\",\"52.58\",\"53.83\",\"52.15\",-1.39,-2.58,3100000\n\"Target\",\"146.51\",\"147.57\",\"146.32\",-0.89,-0.6,2750000\n\"Jabil Circuit\",\"139.72\",\"140.26\",\"136.05\",3.8,2.79,1270000\n\"American Express\",\"212.40\",\"214.24\",\"210.42\",1.19,0.56,4100000\n\"Masco\",\"72.61\",\"74.20\",\"72.17\",-0.71,-0.97,3640000\n\"Stryker\",\"341.85\",\"344.33\",\"337.63\",2.82,0.83,1370000\n\"Discover\",\"109.19\",\"109.44\",\"108.03\",0.35,0.32,1050000\n\"Prudential Financial\",\"105.58\",\"107.65\",\"105.00\",-3.03,-2.79,2000000\n\"Abbott Labs\",\"111.78\",\"112.63\",\"111.19\",-0.65,-0.58,5560000\n\"General Electric\",\"139.27\",\"139.42\",\"138.21\",0.22,0.16,2900000\n\"Quest Diagnostics\",\"126.73\",\"127.60\",\"125.92\",0.19,0.15,473100\n\"United Parcel Service\",\"146.20\",\"147.83\",\"145.91\",-1.72,-1.16,2280000\n\"CVS Health Corp\",\"76.32\",\"76.35\",\"74.46\",1.24,1.65,7350000\n\"PPL\",\"25.87\",\"25.89\",\"25.54\",0.21,0.82,5330000\n\"Robert Half\",\"81.15\",\"81.62\",\"80.07\",0.6,0.74,1010000\n\"Simon Property\",\"146.94\",\"147.37\",\"144.06\",2.84,1.97,1790000\n\"Johnson Controls\",\"55.51\",\"55.72\",\"55.10\",0.4,0.72,4240000\n\"Cummins\",\"251.84\",\"251.91\",\"248.73\",1.92,0.77,572600\n\"Allstate\",\"160.06\",\"160.59\",\"158.53\",-1.69,-1.04,1280000\n\"Sempra Energy\",\"69.67\",\"70.03\",\"69.28\",-0.17,-0.24,1220000\n\"Devon Energy\",\"41.59\",\"42.58\",\"41.35\",-0.82,-1.93,7220000\n\"Conagra Brands\",\"27.39\",\"27.99\",\"27.08\",-0.7,-2.51,5110000\n\"TJX\",\"98.73\",\"99.07\",\"98.01\",0.37,0.38,3420000\n\"Whirlpool\",\"109.00\",\"110.43\",\"108.64\",-1.19,-1.08,669950\n\"FirstEnergy\",\"37.31\",\"37.54\",\"36.00\",1.52,4.26,9590000\n\"Globe Life\",\"125.91\",\"125.98\",\"123.99\",0.96,0.77,474680\n\"Rtx Corp\",\"90.52\",\"91.61\",\"90.33\",-0.52,-0.57,5870000\n\"PulteGroup\",\"103.09\",\"103.93\",\"102.15\",-0.17,-0.16,1090000\n\"Valero Energy\",\"143.08\",\"143.27\",\"141.16\",1.28,0.9,3460000\n\"Boston Scientific\",\"65.49\",\"65.60\",\"64.85\",0.47,0.72,6750000\n\"Capital One Financial\",\"135.17\",\"135.50\",\"133.35\",0.39,0.29,1330000\n\"PG E\",\"16.25\",\"16.39\",\"16.18\",0.01,0.03,18190000\n\"Norfolk Southern\",\"254.77\",\"256.36\",\"253.69\",0.72,0.28,900100\n\"Aflac\",\"78.20\",\"78.36\",\"77.56\",0.1,0.12,1500000\n\"Equity Residential\",\"58.86\",\"59.28\",\"58.65\",-0.34,-0.57,1440000\n\"Air Products\",\"219.79\",\"219.93\",\"216.50\",-0.12,-0.05,2170000\n\"Principal Financial\",\"78.20\",\"78.47\",\"76.82\",0.44,0.57,828190\n\"Texas Instruments\",\"162.40\",\"162.47\",\"160.63\",2.19,1.37,3820000\n\"HP Inc\",\"28.42\",\"28.52\",\"28.19\",0.13,0.46,4110000\n\"Honeywell\",\"194.84\",\"195.23\",\"192.83\",1.38,0.71,3570000\n\"AMD\",\"172.48\",\"175.10\",\"168.66\",3.13,1.85,55790000\n\"M&T Bank\",\"133.40\",\"133.51\",\"130.09\",1.78,1.35,1000000\n\"Mosaic\",\"29.91\",\"30.49\",\"29.82\",-0.17,-0.58,5290000\n\"Revvity\",\"103.74\",\"104.26\",\"102.86\",0.49,0.47,483210\n\"Las Vegas Sands\",\"53.62\",\"53.85\",\"53.09\",0.01,0.01,3320000\n\"Freeport-McMoran\",\"37.31\",\"37.91\",\"37.26\",-0.81,-2.12,14230000\n\"AutoZone\",\"2680.21\",\"2743.53\",\"2680.00\",-51.45,-1.88,195110\n\"Sysco\",\"79.57\",\"79.63\",\"79.02\",0.25,0.32,2120000\n\"Ameren\",\"68.67\",\"68.83\",\"67.59\",0.76,1.11,1840000\n\"Eaton\",\"277.95\",\"278.58\",\"273.00\",4.74,1.73,1530000\n\"Salesforce Inc\",\"291.27\",\"295.24\",\"291.07\",-0.68,-0.23,3730000\n\"Consolidated Edison\",\"89.05\",\"89.14\",\"88.32\",0.38,0.43,1400000\n\"The AES\",\"16.46\",\"16.48\",\"16.14\",0.26,1.6,5390000\n\"Textron\",\"87.18\",\"88.00\",\"87.00\",-0.41,-0.47,958370\n\"U.S. Bancorp\",\"40.18\",\"40.39\",\"39.77\",-0.19,-0.47,8700000\n\"Comerica\",\"51.34\",\"51.84\",\"50.14\",0.27,0.53,1370000\n\"Visa A\",\"276.40\",\"277.18\",\"274.09\",0.62,0.22,2850000\n\"Baker Hughes\",\"29.06\",\"29.46\",\"28.83\",-0.26,-0.89,7090000\n\"Hess\",\"142.04\",\"147.65\",\"142.02\",-4.23,-2.89,4550000\n\"Yum! Brands\",\"130.27\",\"130.41\",\"129.32\",-0.13,-0.1,1920000\n\"Marsh McLennan\",\"197.88\",\"197.92\",\"195.94\",1.08,0.55,800330\n\"Kellanova\",\"53.48\",\"54.74\",\"53.12\",-1.46,-2.65,3240000\n\"Kimco Realty\",\"20.10\",\"20.10\",\"19.65\",0.08,0.4,6400000\n\"Ecolab\",\"202.67\",\"203.62\",\"200.50\",-0.22,-0.11,972500\n\"EOG Resources\",\"111.05\",\"113.75\",\"110.67\",-2.01,-1.78,2470000\n\"Aon\",\"312.47\",\"312.55\",\"306.67\",5.29,1.72,753950\n\"Hasbro\",\"50.59\",\"51.10\",\"50.23\",-0.09,-0.18,1320000\n\"Bank of NY Mellon\",\"55.21\",\"55.36\",\"54.83\",0.1,0.18,2440000\n\"Schlumberger\",\"47.07\",\"47.85\",\"46.91\",-0.72,-1.51,8170000\n\"Walgreens Boots\",\"22.24\",\"22.63\",\"22.16\",-0.3,-1.33,8340000\n\"Rockwell Automation\",\"283.61\",\"284.58\",\"278.77\",4.9,1.76,1490000\n\"PepsiCo\",\"167.67\",\"171.39\",\"166.97\",-6.18,-3.55,12240000\n\"UnitedHealth\",\"518.11\",\"520.39\",\"516.34\",-1.98,-0.38,2600000\n\"Teradyne\",\"102.31\",\"102.42\",\"98.86\",3.84,3.9,1640000\n\"Danaher\",\"242.88\",\"247.25\",\"242.88\",-2.99,-1.22,2030000\n\"Seagate\",\"89.41\",\"89.47\",\"87.36\",1.73,1.97,1520000\n\"Agilent Technologies\",\"133.38\",\"135.15\",\"132.77\",-0.69,-0.51,864180\n\"Delta Air Lines\",\"40.51\",\"40.87\",\"39.96\",0.15,0.38,7570000\n\"Moody’s\",\"404.99\",\"405.32\",\"396.40\",6.99,1.76,920590\n\"Nike\",\"104.50\",\"104.93\",\"103.33\",0.73,0.7,4430000\n\"Procter&Gamble\",\"157.40\",\"158.34\",\"156.96\",-1.24,-0.78,5860000\n\"Weyerhaeuser\",\"33.28\",\"33.28\",\"32.97\",0.27,0.82,2300000\n\"ADP\",\"249.99\",\"250.99\",\"248.89\",-1.09,-0.43,1190000\n\"Keurig Dr Pepper\",\"31.15\",\"31.49\",\"30.94\",-0.34,-1.08,5120000\n\"Lowe’s\",\"222.27\",\"222.31\",\"219.17\",1.88,0.85,1070000\n\"United Airlines Holdings\",\"42.33\",\"43.03\",\"41.60\",0.71,1.71,9110000\n\"Netflix\",\"561.32\",\"566.00\",\"558.10\",2.79,0.5,3020000\n\"News Corp\",\"27.21\",\"27.34\",\"26.88\",0.2,0.74,874170\n\"Equinix\",\"855.76\",\"856.82\",\"844.90\",0.43,0.05,354810\n\"Booking\",\"3758.18\",\"3761.75\",\"3663.01\",-82.04,-2.14,395700\n\"O’Reilly Automotive\",\"1025.82\",\"1041.33\",\"1023.58\",4.99,0.49,484720\n\"BlackRock\",\"796.68\",\"800.80\",\"792.23\",3.48,0.44,426020\n\"CME Group\",\"205.09\",\"205.60\",\"203.61\",1.09,0.53,1420000\n\"Illumina\",\"137.84\",\"147.70\",\"135.30\",-5.49,-3.83,3470000\n\"JB Hunt\",\"215.58\",\"215.90\",\"211.95\",2.2,1.03,495810\n\"Loews\",\"72.74\",\"72.75\",\"71.66\",0.72,1,475990\n\"NextEra Energy\",\"56.56\",\"56.63\",\"55.72\",0.27,0.48,7840000\n\"First Solar\",\"151.50\",\"153.01\",\"144.00\",8.33,5.82,2730000\n\"Viatris\",\"11.68\",\"11.69\",\"11.45\",0.12,1.04,6170000\n\"F5 Networks\",\"186.61\",\"187.21\",\"184.44\",1.38,0.74,304830\n\"Edwards Lifesciences\",\"85.02\",\"86.68\",\"84.89\",-0.78,-0.91,2830000\n\"News Corp A\",\"26.04\",\"26.18\",\"25.70\",0.2,0.77,3940000\n\"Amphenol\",\"105.28\",\"105.41\",\"104.55\",0.42,0.4,1250000\n\"Berkshire Hathaway B\",\"398.33\",\"398.33\",\"395.85\",0.84,0.21,1920000\n\"Coterra Energy\",\"24.30\",\"24.52\",\"24.20\",-0.2,-0.82,4840000\n\"CarMax\",\"74.79\",\"74.86\",\"72.90\",1.49,2.03,1560000\n\"Chipotle Mexican Grill\",\"2636.06\",\"2659.11\",\"2615.93\",15.58,0.59,191430\n\"DaVita\",\"109.87\",\"111.27\",\"109.73\",-0.95,-0.86,677110\n\"EQT\",\"34.32\",\"34.81\",\"34.21\",-0.43,-1.24,3190000\n\"FMC\",\"51.76\",\"52.41\",\"50.63\",-0.28,-0.54,3270000\n\"L3Harris Technologies\",\"209.84\",\"210.11\",\"207.87\",0.54,0.26,517540\n\"Healthpeak Properties\",\"17.35\",\"17.80\",\"17.07\",-0.64,-3.56,13020000\n\"Welltower\",\"87.64\",\"87.68\",\"86.23\",0.67,0.76,1720000\n\"Hormel Foods\",\"29.07\",\"29.33\",\"28.97\",-0.41,-1.37,3560000\n\"Invesco\",\"15.89\",\"15.95\",\"15.68\",-0.01,-0.09,2870000\n\"Iron Mountain\",\"68.63\",\"68.94\",\"68.18\",0.07,0.1,642430\n\"Eversource Energy\",\"54.95\",\"55.27\",\"53.88\",0.88,1.63,3160000\n\"NRG\",\"52.37\",\"52.56\",\"51.98\",0.02,0.04,1720000\n\"ONEOK\",\"69.03\",\"69.49\",\"68.71\",0.07,0.09,2080000\n\"Pioneer Natural\",\"227.15\",\"233.37\",\"226.85\",-4.61,-1.99,1520000\n\"Republic Services\",\"173.49\",\"175.07\",\"172.79\",-1.15,-0.66,929990\n\"Roper Technologies\",\"550.26\",\"550.77\",\"545.11\",4.92,0.9,370800\n\"Ventas\",\"45.52\",\"45.68\",\"44.99\",0,0,1630000\n\"WEC Energy\",\"77.57\",\"77.74\",\"76.95\",0.17,0.22,2060000\n\"Blackstone\",\"127.68\",\"128.63\",\"125.99\",-0.16,-0.13,2640000\n\"Marathon Petroleum\",\"169.97\",\"170.69\",\"168.73\",0.29,0.17,2130000\n\"Broadcom\",\"1283.44\",\"1285.65\",\"1249.08\",8.68,0.68,2550000\n\"NXP\",\"233.55\",\"233.66\",\"228.32\",5.72,2.51,2340000\n\"Tesla\",\"193.57\",\"194.12\",\"189.49\",4.01,2.12,83610000\n\"Take-Two\",\"154.91\",\"158.11\",\"152.23\",-14.69,-8.66,6580000\n\"Dollar Tree\",\"139.50\",\"140.73\",\"139.17\",-1.4,-0.99,2290000\n\"Align\",\"296.37\",\"298.54\",\"291.53\",1.37,0.46,803120\n\"ANSYS\",\"342.28\",\"346.64\",\"341.89\",-3.05,-0.88,834790\n\"Builders FirstSource\",\"185.38\",\"186.54\",\"182.89\",0.13,0.07,1040000\n\"Charter Communications\",\"291.15\",\"293.40\",\"285.01\",8.55,3.03,2050000\n\"CoStar\",\"83.13\",\"83.45\",\"80.51\",1.36,1.66,4010000\n\"DexCom\",\"120.47\",\"124.99\",\"120.29\",-6.58,-5.18,5550000\n\"Fortinet\",\"70.44\",\"70.86\",\"68.20\",2.59,3.82,7090000\n\"IDEXX Labs\",\"572.21\",\"578.35\",\"565.52\",-1.4,-0.24,312340\n\"Incyte\",\"57.66\",\"57.96\",\"57.09\",0.3,0.52,1510000\n\"Jack Henry&Associates\",\"175.93\",\"176.10\",\"174.01\",1.88,1.08,448190\n\"MarketAxesss\",\"223.32\",\"226.60\",\"220.29\",-0.87,-0.39,280200\n\"Monolithic\",\"752.31\",\"761.50\",\"737.22\",15.24,2.07,654900\n\"Nordson\",\"263.63\",\"263.94\",\"260.81\",1.76,0.67,143780\n\"ON Semiconductor\",\"80.80\",\"81.59\",\"79.71\",0.09,0.11,6140000\n\"PTC\",\"183.10\",\"183.69\",\"181.60\",1.56,0.86,527660\n\"Insulet\",\"192.44\",\"197.46\",\"191.92\",-4.2,-2.14,736300\n\"Pool\",\"386.65\",\"391.01\",\"384.42\",-0.69,-0.18,211450\n\"Bio-Techne\",\"67.95\",\"68.19\",\"66.79\",0.05,0.07,883500\n\"Zebra\",\"253.09\",\"253.73\",\"248.36\",5.97,2.42,324810\n\"BorgWarner\",\"31.80\",\"32.00\",\"31.29\",0.32,1.03,2860000\n\"T-Mobile US\",\"162.19\",\"162.64\",\"160.28\",1.26,0.78,4280000\n\"Quanta Services\",\"210.03\",\"211.00\",\"206.43\",2.26,1.09,656270\n\"Leidos\",\"113.52\",\"114.23\",\"112.99\",0.25,0.22,621380\n\"TE Connectivity\",\"144.05\",\"144.45\",\"142.74\",0.94,0.66,821920\n\"Mid-America Apartment\",\"124.53\",\"124.95\",\"123.37\",0.05,0.04,1060000\n\"Charles River Laboratories\",\"222.21\",\"224.61\",\"217.13\",3.44,1.57,522620\n\"Huntington Ingalls Industries\",\"274.05\",\"274.32\",\"271.02\",1.92,0.71,188460\n\"Mettler-Toledo\",\"1175.31\",\"1233.99\",\"1169.94\",-49.66,-4.05,289160\n\"Federal Realty\",\"101.14\",\"102.03\",\"99.94\",-0.92,-0.9,855900\n\"Live Nation Entertainment\",\"89.54\",\"90.50\",\"87.93\",1.54,1.75,3140000\n\"Martin Marietta Materials\",\"527.15\",\"527.15\",\"522.10\",1.74,0.33,274340\n\"FactSet Research\",\"477.65\",\"477.65\",\"470.01\",7.59,1.61,153500\n\"Raymond James Financial\",\"114.90\",\"114.96\",\"112.04\",2.8,2.5,927470\n\"Bio-Rad Labs\",\"325.31\",\"327.48\",\"322.62\",-0.8,-0.25,99820\n\"Digital\",\"147.23\",\"147.94\",\"145.54\",0.17,0.12,1200000\n\"Essex Property\",\"229.86\",\"230.36\",\"228.46\",0.52,0.23,269920\n\"Fair Isaac\",\"1322.98\",\"1336.39\",\"1317.44\",1.54,0.12,94330\n\"Cooper\",\"376.60\",\"376.95\",\"370.49\",2.95,0.79,160050\n\"Molina Healthcare\",\"388.67\",\"390.00\",\"378.05\",7.18,1.88,460830\n\"Everest\",\"353.74\",\"355.03\",\"348.46\",-0.82,-0.23,685340\n\"Regency Centers\",\"61.32\",\"62.09\",\"60.01\",-0.82,-1.32,2270000\n\"Dollar General\",\"135.20\",\"135.91\",\"134.16\",-0.45,-0.33,1850000\n\"Transdigm\",\"1118.35\",\"1139.98\",\"1116.11\",-10.22,-0.91,279970\n\"Atmos Energy\",\"113.08\",\"113.50\",\"112.59\",0.15,0.13,532110\n\"Brown&Brown\",\"81.00\",\"81.01\",\"79.75\",1.05,1.32,991410\n\"Domino’s Pizza Inc\",\"424.98\",\"427.55\",\"424.45\",-1.6,-0.38,339630\n\"HCA\",\"306.41\",\"307.51\",\"305.57\",0.95,0.31,753540\n\"Hubbell\",\"363.13\",\"363.36\",\"357.50\",4.92,1.37,266750\n\"Rollins\",\"43.48\",\"44.12\",\"43.19\",-0.24,-0.56,1640000\n\"STERIS\",\"224.28\",\"224.57\",\"218.97\",4.02,1.83,600580\n\"ResMed\",\"184.65\",\"185.95\",\"182.27\",0.9,0.49,729020\n\"MSCI\",\"592.30\",\"594.24\",\"584.88\",6.8,1.16,272690\n\"NVR\",\"7441.0\",\"7476.6\",\"7364.2\",-45.7,-0.61,14930\n\"Carnival Corp\",\"15.30\",\"15.74\",\"14.96\",-0.39,-2.49,45420000\n\"West Pharmaceutical Services\",\"409.79\",\"412.67\",\"408.30\",-1.11,-0.27,269440\n\"Teledyne Technologies\",\"433.82\",\"434.27\",\"428.00\",3.15,0.73,140650\n\"Tyler Technologies\",\"441.04\",\"442.39\",\"433.11\",5.89,1.35,139190\n\"Targa Resources\",\"87.07\",\"88.63\",\"87.06\",-0.83,-0.94,1310000\n\"Meta Platforms\",\"468.11\",\"473.59\",\"467.46\",-1.89,-0.4,17910000\n\"Alexandria RE\",\"116.20\",\"118.18\",\"114.80\",-1.7,-1.44,1260000\n\"Teleflex\",\"252.04\",\"252.30\",\"248.59\",0.72,0.29,148180\n\"Westinghouse Air Brake\",\"136.68\",\"137.56\",\"136.15\",-0.05,-0.04,1300000\n\"Evergy\",\"49.08\",\"49.27\",\"48.40\",0.46,0.95,2230000\n\"Willis Towers Watson\",\"271.86\",\"272.99\",\"269.98\",1.75,0.65,462240\n\"Caesars\",\"44.50\",\"45.17\",\"44.23\",0.25,0.56,2900000\n\"Enphase\",\"122.47\",\"124.55\",\"117.30\",5.59,4.78,5270000\n\"Aptiv\",\"82.02\",\"82.39\",\"80.85\",0.73,0.9,2930000\n\"Crown Castle\",\"108.40\",\"108.53\",\"106.76\",0.9,0.84,2640000\n\"EPAM Systems\",\"286.27\",\"291.35\",\"286.07\",-0.9,-0.31,640890\n\"Pentair\",\"74.48\",\"74.76\",\"73.88\",0.04,0.05,1390000\n\"Mondelez\",\"73.17\",\"74.46\",\"72.83\",-1.59,-2.13,7300000\n\"Skyworks\",\"105.05\",\"105.58\",\"104.04\",0.46,0.44,1500000\n\"Tractor Supply\",\"235.08\",\"235.19\",\"231.34\",3.25,1.4,958700\n\"Lululemon Athletica\",\"470.24\",\"471.26\",\"458.78\",-2.74,-0.58,1110000\n\"Regeneron Pharma\",\"953.42\",\"957.51\",\"945.85\",6.99,0.74,500720\n\"United Rentals\",\"650.35\",\"653.08\",\"643.93\",3.35,0.52,363440\n\"Kinder Morgan\",\"16.61\",\"16.72\",\"16.53\",-0.03,-0.15,7420000\n\"Albemarle\",\"115.74\",\"116.19\",\"113.13\",1.41,1.23,2310000\n\"Centene\",\"77.32\",\"77.36\",\"75.89\",0.82,1.07,1930000\n\"Phillips 66\",\"145.66\",\"147.78\",\"145.35\",-1.16,-0.79,2450000\n\"Xylem\",\"124.30\",\"124.39\",\"122.06\",1.92,1.57,1550000\n\"UDR\",\"35.37\",\"35.67\",\"35.26\",-0.12,-0.34,2340000\n\"SBA Communications\",\"217.45\",\"218.13\",\"214.00\",1.25,0.58,946720\n\"Gartner\",\"463.63\",\"465.02\",\"456.59\",7.45,1.63,334900\n\"IDEX\",\"228.11\",\"228.28\",\"225.29\",2.28,1.01,422760\n\"Universal Health Services\",\"162.37\",\"162.80\",\"161.27\",0.41,0.25,264750\n\"LKQ\",\"47.94\",\"48.11\",\"47.43\",0.4,0.84,904460\n\"Broadridge\",\"198.98\",\"199.86\",\"198.50\",-0.02,-0.01,607820\n\"Extra Space Storage\",\"143.36\",\"144.18\",\"142.39\",-0.11,-0.08,694810\n\"Camden Property\",\"93.45\",\"94.10\",\"93.25\",-0.5,-0.53,615980\n\"Mohawk Industries\",\"110.06\",\"111.80\",\"101.83\",0.46,0.42,1980000\n\"American Water Works\",\"122.22\",\"122.62\",\"121.07\",0.39,0.32,718930\n\"Alliant Energy\",\"47.93\",\"47.99\",\"47.33\",0.45,0.95,1460000\n\"Celanese\",\"149.00\",\"149.06\",\"147.30\",0.1,0.07,425180\n\"Bunge\",\"88.45\",\"88.66\",\"86.81\",1.95,2.25,2580000\n\"Ametek\",\"168.49\",\"168.60\",\"166.41\",2.19,1.32,800310\n\"WR Berkley\",\"80.46\",\"80.51\",\"79.33\",0.58,0.73,891180\n\"LyondellBasell Industries\",\"95.42\",\"95.42\",\"94.37\",0.44,0.46,1380000\n\"Royal Caribbean Cruises\",\"116.99\",\"121.00\",\"115.58\",-3.98,-3.29,4120000\n\"FleetCor\",\"273.21\",\"275.53\",\"266.43\",8.09,3.05,770350\n\"Packaging America\",\"168.37\",\"168.39\",\"166.21\",1.5,0.9,450830\n\"Arthur J Gallagher\",\"238.81\",\"239.18\",\"236.29\",1.78,0.75,440910\n\"Church&Dwight\",\"98.82\",\"100.08\",\"98.16\",-1.12,-1.12,989910\n\"Generac\",\"126.97\",\"128.39\",\"125.16\",-0.17,-0.13,884720\n\"Global Payments\",\"136.00\",\"137.09\",\"134.91\",-0.63,-0.46,1510000\n\"Realty Income\",\"52.75\",\"53.25\",\"52.28\",-0.41,-0.77,5830000\n\"AO Smith\",\"80.55\",\"80.80\",\"79.98\",0.3,0.37,537460\n\"Arch Capital\",\"83.46\",\"83.52\",\"81.59\",0.7,0.85,1090000\n\"Cboe Global\",\"183.55\",\"185.92\",\"182.72\",-0.61,-0.33,574360\n\"Copart\",\"50.91\",\"51.15\",\"50.65\",-0.05,-0.1,3010000\n\"Old Dominion Freight Line\",\"435.33\",\"437.88\",\"428.67\",0.17,0.04,784180\n\"Synopsys\",\"575.30\",\"582.85\",\"571.92\",4.61,0.81,967450\n\"Trimble\",\"52.51\",\"52.64\",\"51.74\",0.43,0.83,1510000\n\"Ulta Beauty\",\"522.63\",\"524.34\",\"517.16\",3.51,0.68,480210\n\"Verisk\",\"250.61\",\"251.47\",\"248.18\",1.35,0.54,691700\n\"AbbVie\",\"174.04\",\"175.40\",\"173.06\",-0.75,-0.43,3400000\n\"Diamondback\",\"151.74\",\"154.22\",\"151.25\",-1.74,-1.13,963550\n\"Norwegian Cruise Line\",\"16.41\",\"17.54\",\"16.35\",-1.09,-6.23,21310000\n\"Zoetis Inc\",\"197.29\",\"198.22\",\"195.59\",1.54,0.79,1860000\n\"IQVIA Holdings\",\"218.26\",\"222.21\",\"216.18\",1.26,0.58,1020000\n\"ServiceNow Inc\",\"813.27\",\"815.27\",\"802.39\",13.86,1.73,847820\n\"Palo Alto Networks\",\"376.90\",\"380.84\",\"369.00\",9.88,2.69,3430000\n\"CDW Corp\",\"245.23\",\"245.41\",\"241.49\",1.27,0.52,870860\n\"Hilton Worldwide\",\"192.17\",\"194.02\",\"191.68\",-2.38,-1.22,1530000\n\"American Airlines\",\"14.88\",\"15.18\",\"14.71\",-0.07,-0.47,26850000\n\"Allegion PLC\",\"131.91\",\"132.95\",\"130.45\",1.39,1.07,760630\n\"Alphabet C\",\"150.22\",\"150.70\",\"147.43\",3,2.04,21350000\n\"Paycom Soft\",\"190.02\",\"197.53\",\"187.47\",-5.73,-2.93,1770000\n\"Arista Networks\",\"282.30\",\"284.82\",\"278.63\",6.41,2.32,2950000\n\"Synchrony Financial\",\"38.72\",\"39.04\",\"38.27\",-0.16,-0.41,2810000\n\"Catalent Inc\",\"56.73\",\"56.95\",\"56.10\",0.18,0.32,3640000\n\"Citizens Financial Group Inc\",\"31.51\",\"31.69\",\"30.89\",0.14,0.45,3170000\n\"Keysight Technologies\",\"161.53\",\"162.50\",\"161.04\",0.35,0.22,639790\n\"Qorvo Inc\",\"112.32\",\"113.79\",\"112.21\",-0.3,-0.27,963360\n\"Etsy Inc\",\"78.09\",\"78.78\",\"73.56\",3.6,4.83,3170000\n\"WestRock Co\",\"42.49\",\"42.78\",\"42.23\",-0.33,-0.77,1680000\n\"PayPal\",\"58.91\",\"59.22\",\"56.16\",2.78,4.95,30500000\n\"Hewlett Packard\",\"15.48\",\"15.55\",\"15.37\",-0.01,-0.1,6130000\n\"Match Group\",\"35.42\",\"35.95\",\"34.95\",0.39,1.11,2910000\n\"Fortive\",\"82.69\",\"82.72\",\"81.87\",0.45,0.55,899160\n\"Lamb Weston Holdings\",\"100.84\",\"101.17\",\"99.57\",-0.04,-0.04,881030\n\"Invitation Homes\",\"33.06\",\"33.10\",\"32.54\",0.37,1.13,2410000\n\"VICI Properties\",\"29.71\",\"29.73\",\"29.34\",0.04,0.15,4090000\n\"Dayforce\",\"70.69\",\"70.71\",\"68.39\",0.71,1.01,1280000\n\"Moderna\",\"87.41\",\"93.36\",\"86.41\",-6.25,-6.67,7110000\n\"Uber Tech\",\"70.89\",\"72.04\",\"69.69\",-0.72,-1.01,20300000\n\"Fox Corp A\",\"29.79\",\"29.85\",\"28.74\",1.01,3.51,5670000\n\"Fox Corp B\",\"27.45\",\"27.58\",\"26.61\",0.83,3.12,1740000\n\"Corteva\",\"53.59\",\"53.91\",\"52.88\",0.35,0.66,2400000\n\"Amcor PLC\",\"9.10\",\"9.14\",\"8.99\",-0.07,-0.71,7140000\n\"Trane Technologies\",\"275.46\",\"275.49\",\"269.20\",5.52,2.04,910240\n\"Otis Worldwide\",\"91.09\",\"91.09\",\"90.29\",0.22,0.24,1700000\n\"Carrier Global\",\"56.00\",\"56.02\",\"54.61\",0.94,1.71,4660000\n\"Howmet\",\"58.59\",\"59.36\",\"58.56\",-0.58,-0.98,2390000\n\"Airbnb\",\"147.60\",\"148.68\",\"145.18\",-2.94,-1.96,4950000\n\"Constellation Energy\",\"132.17\",\"132.51\",\"130.00\",1.45,1.11,827840\n\"GE HealthCare\",\"81.34\",\"82.75\",\"80.61\",-0.72,-0.88,2700000\n\"Kenvue\",\"19.32\",\"19.61\",\"19.08\",-0.01,-0.03,24460000\n\"Veralto\",\"82.17\",\"83.55\",\"81.99\",-0.39,-0.47,1180000\n\"Linde PLC\",\"419.42\",\"419.83\",\"412.29\",5.42,1.31,1310000"
  },
  {
    "path": "projects/meilisearch-searchbar/src/fallback.rs",
    "content": "use axum::{\n    body::Body,\n    extract::State,\n    http::{Request, Response, StatusCode, Uri},\n    response::{IntoResponse, Response as AxumResponse},\n};\nuse leptos::{view, LeptosOptions};\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\n\npub async fn file_and_error_handler(\n    uri: Uri,\n    State(options): State<LeptosOptions>,\n    req: Request<Body>,\n) -> AxumResponse {\n    let root = options.site_root.clone();\n    log::debug!(\"uri = {uri:?} root = {root} \");\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        let handler = leptos_axum::render_app_to_stream(\n            options.to_owned(),\n            || view! {\"Error! Error! Error!\"},\n        );\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {}\", err),\n        )),\n    }\n}\n"
  },
  {
    "path": "projects/meilisearch-searchbar/src/lib.rs",
    "content": "use leptos::*;\nuse leptos_meta::*;\nuse leptos_router::*;\n#[cfg(feature = \"ssr\")]\npub mod fallback;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    // initializes logging using the `log` crate\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n\n    leptos::mount_to_body(App);\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n    // Provide this two our search components, they'll share a read and write handle to a Vec<StockRow>.\n    let search_results = create_rw_signal(Vec::<StockRow>::new());\n    provide_context(search_results);\n    view! {\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/favicon.ico\"/>\n        <Meta name=\"description\" content=\"Leptos implementation of a Meilisearch backed Searchbar.\"/>\n        <Router>\n            <main>\n                <Routes>\n                    <Route path=\"/\" view=||view!{\n                        <SearchBar/>\n                        <SearchResults/>\n                    }/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n#[derive(Clone, serde::Deserialize, serde::Serialize, Debug)]\npub struct StockRow {\n    id: u32,\n    name: String,\n    last: String,\n    high: String,\n    low: String,\n    absolute_change: f32,\n    percentage_change: f32,\n    volume: u64,\n}\n\n#[leptos::server]\npub async fn search_query(query: String) -> Result<Vec<StockRow>, ServerFnError> {\n    use leptos_axum::extract;\n    // Wow, so ergonomic!\n    let axum::Extension::<meilisearch_sdk::Client>(client) = extract().await?;\n    // Meilisearch has great defaults, lots of things are thought of for out of the box utility.\n    // They limit the result length automatically (to 20), and have user friendly typo corrections and return similar words.\n    let hits = client\n        .get_index(\"stock_prices\")\n        .await\n        .unwrap()\n        .search()\n        .with_query(query.as_str())\n        .execute::<StockRow>()\n        .await\n        .map_err(|err| ServerFnError::new(err.to_string()))?\n        .hits;\n    \n    Ok(hits\n        .into_iter()\n        .map(|search_result| search_result.result)\n        .collect())\n}\n\n#[component]\npub fn SearchBar() -> impl IntoView {\n    let write_search_results = expect_context::<RwSignal<Vec<StockRow>>>().write_only();\n    let search_query = create_server_action::<SearchQuery>();\n    create_effect(move |_| {\n        if let Some(value) = search_query.value()() {\n            match value {\n                Ok(search_results) => {\n                    write_search_results.set(search_results);\n                }\n                Err(err) => {\n                    leptos::logging::log!(\"{err}\")\n                }\n            }\n        }\n    });\n\n    view! {\n        <div>\n            <label for=\"search\">Search</label>\n            <input id=\"search\" on:input=move|e|{\n                let query = event_target_value(&e);\n                search_query.dispatch(SearchQuery{query});\n            }/>\n        </div>\n    }\n}\n\n#[component]\npub fn SearchResults() -> impl IntoView {\n    let read_search_results = expect_context::<RwSignal<Vec<StockRow>>>().read_only();\n    view! {\n        <ul>\n               <For\n                    each=read_search_results\n                    key=|row| row.name.clone()\n                    children=move |StockRow{name,last,high,low,absolute_change,percentage_change,volume,..}: StockRow| {\n          view! {\n                <li>\n                    {format!(\"{name}; last: {last}; high: {high}; low: {low}; chg.: {absolute_change}; chg...:{percentage_change}; volume:{volume}\")}\n                </li>\n          }\n        }\n      />\n        </ul>\n    }\n}\n"
  },
  {
    "path": "projects/meilisearch-searchbar/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::{routing::get, Extension, Router};\n    use leptos::get_configuration;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use meilisearch_searchbar::StockRow;\n    use meilisearch_searchbar::{fallback::file_and_error_handler, *};\n\n    // simple_logger is a lightweight alternative to tracing, when you absolutely have to trace, use tracing.\n    simple_logger::SimpleLogger::new()\n        .with_level(log::LevelFilter::Debug)\n        .init()\n        .unwrap();\n\n    let mut rdr = csv::Reader::from_path(\"data_set.csv\").unwrap();\n\n    // Our data set doesn't have a good id for the purposes of meilisearch, Name is unique but it's not formatted correctly because it may have spaces.\n    let documents: Vec<StockRow> = rdr\n        .records()\n        .enumerate()\n        .map(|(i, rec)| {\n            // There's probably a better way to do this.\n            let mut record = csv::StringRecord::new();\n            record.push_field(&i.to_string());\n            for field in rec.unwrap().iter() {\n                record.push_field(field);\n            }\n            record\n                .deserialize::<StockRow>(None)\n                .expect(&format!(\"{:?}\", record))\n        })\n        .collect();\n\n    // My own check. I know how long I expect it to be, if it's not this length something is wrong.\n    assert_eq!(documents.len(), 503);\n\n    let client = meilisearch_sdk::Client::new(\n        std::env::var(\"MEILISEARCH_URL\").unwrap(),\n        std::env::var(\"MEILISEARCH_API_KEY\").ok(),\n    );\n    // An index is where the documents are stored.\n    let task = client\n        .create_index(\"stock_prices\", Some(\"id\"))\n        .await\n        .unwrap();\n\n    // Meilisearch may take some time to execute the request so we are going to wait till it's completed\n    client.wait_for_task(task, None, None).await.unwrap();\n\n    let task_2 = client\n        .get_index(\"stock_prices\")\n        .await\n        .unwrap()\n        .add_documents(&documents, Some(\"id\"))\n        .await\n        .unwrap();\n\n    client.wait_for_task(task_2, None, None).await.unwrap();\n    \n    drop(documents);\n\n    let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .route(\"/favicon.ico\", get(file_and_error_handler))\n        .leptos_routes(&leptos_options, routes, App)\n        .fallback(file_and_error_handler)\n        .layer(Extension(client))\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    println!(\"listening on {}\", addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/.gitignore",
    "content": "/target\n*/target\n.vscode\n"
  },
  {
    "path": "projects/nginx-mpmc/README.md",
    "content": "# Nginx Multiple Server Multiple Client Example\nThis example shows how multiple clients can communicate with multiple servers while being shared over a single domain i.e localhost:80 using nginx as a reverse proxy.\n\n### How to run this example\n```sh\n./run.sh \n```\nOr\n\n```sh\n./run_linux.sh\n```\n\n<br>\nThis will boot up nginx via it's docker image mapped to port 80, and the four servers. App-1, App-2, Shared-Server-1, Shared-Server-2.\n<br>\nApp-1, And App-2 are SSR rendering leptos servers.\n<br>\nIf you go to localhost (you'll get App-1), and localhost/app2 (you'll get app2).\n<br>\nThe two shared servers can be communicated with via actions and local resources, or resources (if using CSR).\n<br>\n`create_resource` Won't work as expected, when trying to communicate to different servers. It will instead try to run the server function on the server you are serving your server side rendered content from. This will cause errors if your server function relies on state that is not present.\n<br>\nWhen you are done with this example, run\n\n```sh\n./kill.sh\n```\n\nCasting ctrl-c multiple times won't close all the open programs.\n\n## Thoughts, Feedback, Criticism, Comments?\nSend me any of the above, I'm @sjud on leptos discord. I'm always looking to improve and make these projects more helpful for the community. So please let me know how I can do that. Thanks!"
  },
  {
    "path": "projects/nginx-mpmc/app-1/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/Cargo.toml",
    "content": "[package]\nname = \"app-1\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.0\"\nleptos_meta = { version = \"0.6.0\" }\nleptos_router = { version = \"0.6.0\" }\ntower = { version = \"0.4.0\", optional = true }\ntower-http = { version = \"0.5.0\", features = [\"fs\", \"trace\"], optional = true }\nwasm-bindgen = \"0.2.89\"\nthiserror = \"1.0\"\ntracing = { version = \"0.1.0\", optional = true }\n\nhttp = \"1.0\"\n\naxum = { version = \"0.7.0\", optional = true }\nleptos = \"0.6.0\"\nleptos_axum = { version = \"0.6.0\", optional = true }\ntokio = { version = \"1.0\", features = [\"rt-multi-thread\"], optional = true }\nshared-server = { path = \"../shared-server\", default-features = false }\nshared-server-2 = { path = \"../shared-server-2\", default-features = false }\ntracing-subscriber = { version = \"0.3.18\", features = [\"env-filter\"] }\n\n# Defines a size-optimized profile for the WASM bundle in release mode\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[features]\nhydrate = [\n  \"leptos/hydrate\",\n  \"leptos_meta/hydrate\",\n  \"leptos_router/hydrate\",\n  \"shared-server/hydrate\",\n  \"shared-server-2/hydrate\",\n]\nssr = [\n  \"shared-server/ssr\",\n  \"shared-server-2/ssr\",\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:leptos_axum\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:tracing\",\n]\n\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"app-1\"\n\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"public\"\n\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\n# we're listening inside of a docker container, so we need to set 0.0.0.0 to let it be accessed from outside the container.\nsite-addr = \"127.0.0.1:3000\"\n\n# The port to use for automatic reload monitoring\nreload-port = 3004\n\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\n# The profile to use for the lib target when compiling for release\n#\n# Optional. Defaults to \"release\".\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/README.md",
    "content": "<picture>\n    <source srcset=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg\" media=\"(prefers-color-scheme: dark)\">\n    <img src=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg\" alt=\"Leptos Logo\">\n</picture>\n\n# Leptos Axum Starter Template\n\nThis is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool using [Axum](https://github.com/tokio-rs/axum).\n\n## Creating your template repo\n\nIf you don't have `cargo-leptos` installed you can install it with\n\n```bash\ncargo install cargo-leptos\n```\n\nThen run\n```bash\ncargo leptos new --git leptos-rs/start-axum\n```\n\nto generate a new project template.\n\n```bash\ncd app-1\n```\n\nto go to your newly created project.  \nFeel free to explore the project structure, but the best place to start with your application code is in `src/app.rs`.  \nAddtionally, Cargo.toml may need updating as new versions of the dependencies are released, especially if things are not working after a `cargo update`.\n\n## Running your project\n\n```bash\ncargo leptos watch\n```\n\n## Installing Additional Tools\n\nBy default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.\n\n1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly\n2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly\n3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)\n4. `npm install -g sass` - install `dart-sass` (should be optional in future\n\n## Compiling for Release\n```bash\ncargo leptos build --release\n```\n\nWill generate your server binary in target/server/release and your site package in target/site\n\n## Testing Your Project\n```bash\ncargo leptos end-to-end\n```\n\n```bash\ncargo leptos end-to-end --release\n```\n\nCargo-leptos uses Playwright as the end-to-end test tool.  \nTests are located in end2end/tests directory.\n\n## Executing a Server on a Remote Machine Without the Toolchain\nAfter running a `cargo leptos build --release` the minimum files needed are:\n\n1. The server binary located in `target/server/release`\n2. The `site` directory and all files within located in `target/site`\n\nCopy these files to your remote server. The directory structure should be:\n```text\napp-1\nsite/\n```\nSet the following environment variables (updating for your project as needed):\n```text\nLEPTOS_OUTPUT_NAME=\"app-1\"\nLEPTOS_SITE_ROOT=\"site\"\nLEPTOS_SITE_PKG_DIR=\"pkg\"\nLEPTOS_SITE_ADDR=\"127.0.0.1:3000\"\nLEPTOS_RELOAD_PORT=\"3001\"\n```\nFinally, run the server binary.\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/src/app.rs",
    "content": "use crate::error_template::{AppError, ErrorTemplate};\nuse leptos::*;\nuse leptos_meta::*;\nuse leptos_router::*;\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n\n    view! {\n\n\n        // injects a stylesheet into the document <head>\n        // id=leptos means cargo-leptos will hot-reload this stylesheet\n        <Stylesheet id=\"leptos\" href=\"/pkg/app-1.css\"/>\n\n        // sets the document title\n        <Title text=\"Welcome to Leptos\"/>\n\n        // content for this welcome page\n        <Router fallback=|| {\n            let mut outside_errors = Errors::default();\n            outside_errors.insert_with_default_key(AppError::NotFound);\n            view! {\n                <ErrorTemplate outside_errors/>\n            }\n            .into_view()\n        }>\n            <main>\n                <Routes>\n                    <Route path=\"\" view=HomePage/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n/// Renders the home page of your application.\n#[component]\nfn HomePage() -> impl IntoView {\n    use shared_server::SharedServerFunction;\n    use shared_server_2::SharedServerFunction2;\n\n    // A local resource will wait for the client to load before attempting to initialize.\n    let hello_1 = create_local_resource(move || (), |_| shared_server::shared_server_function());\n    //  this won't work : let hello_1 = create_resource(move || (), |_| shared_server::shared_server_function());\n    // A resource is initialized on the rendering server when using SSR.\n    \n    let hello_1_action = Action::<SharedServerFunction,_>::server();\n    let hello_2_action = Action::<SharedServerFunction2,_>::server();\n\n    let value_1 = create_rw_signal(String::from(\"waiting for update from shared server.\"));\n    let value_2 = create_rw_signal(String::from(\"waiting for update from shared server 2.\"));\n\n    //let hello_2 = create_resource(move || (), shared_server_2::shared_server_function);\n    create_effect(move|_|{if let Some(Ok(msg)) = hello_1_action.value().get(){value_1.set(msg)}});\n    create_effect(move|_|{if let Some(Ok(msg)) = hello_2_action.value().get(){value_2.set(msg)}});\n\n    view! {\n        <h1> App 1</h1>\n        <div>Suspense</div>\n        <Suspense fallback=move || view! { <p>\"Loading (Suspense Fallback)...\"</p> }>\n        {move || {\n            hello_1.get().map(|data| match data {\n              Err(_) => view! {  <pre>\"Error\"</pre> }.into_view(),\n              Ok(hello) => hello.into_view(),\n            })\n          }\n        }\n      </Suspense>\n        <div> action response from server 1 </div>\n        <button on:click=move|_|hello_1_action.dispatch(SharedServerFunction{})>request from shared server 1</button>\n        {move || value_1.get()}\n        <div> action response from server 2 </div>\n        <button on:click=move|_|hello_2_action.dispatch(SharedServerFunction2{})>request from shared server 2</button>\n        {move || value_2.get()}\n    }\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/src/error_template.rs",
    "content": "use http::status::StatusCode;\nuse leptos::*;\nuse thiserror::Error;\n\n#[derive(Clone, Debug, Error)]\npub enum AppError {\n    #[error(\"Not Found\")]\n    NotFound,\n}\n\nimpl AppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            AppError::NotFound => StatusCode::NOT_FOUND,\n        }\n    }\n}\n\n// A basic function to display errors served by the error boundaries.\n// Feel free to do more complicated things here than just displaying the error.\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional)] errors: Option<RwSignal<Errors>>,\n) -> impl IntoView {\n    let errors = match outside_errors {\n        Some(e) => create_rw_signal(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n    // Get Errors from Signal\n    let errors = errors.get_untracked();\n\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors: Vec<AppError> = errors\n        .into_iter()\n        .filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())\n        .collect();\n    println!(\"Errors: {errors:#?}\");\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    #[cfg(feature = \"ssr\")]\n    {\n        use leptos_axum::ResponseOptions;\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors[0].status_code());\n        }\n    }\n\n    view! {\n        <h1>{if errors.len() > 1 {\"Errors\"} else {\"Error\"}}</h1>\n        <For\n            // a function that returns the items we're iterating over; a signal is fine\n            each= move || {errors.clone().into_iter().enumerate()}\n            // a unique key for each item as a reference\n            key=|(index, _error)| *index\n            // renders each item to a view\n            children=move |error| {\n                let error_string = error.1.to_string();\n                let error_code= error.1.status_code();\n                view! {\n                    <h2>{error_code.to_string()}</h2>\n                    <p>\"Error: \" {error_string}</p>\n                }\n            }\n        />\n    }\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/src/fileserv.rs",
    "content": "use axum::{\n    body::Body,\n    extract::State,\n    response::IntoResponse,\n    http::{Request, Response, StatusCode, Uri},\n};\nuse axum::response::Response as AxumResponse;\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\nuse leptos::*;\nuse crate::app::App;\n\npub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {\n    let root = options.site_root.clone();\n    tracing::debug!(\"APP 1\");\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        let handler = leptos_axum::render_app_to_stream(options.to_owned(), App);\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/src/lib.rs",
    "content": "pub mod app;\npub mod error_template;\n#[cfg(feature = \"ssr\")]\npub mod fileserv;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount_to_body(App);\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use leptos::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use app_1::app::*;\n    use app_1::fileserv::file_and_error_handler;\n    use axum::routing::post;\n    \n    tracing_subscriber::fmt()\n    .pretty()\n    .with_thread_names(true)\n    // enable everything\n    .with_max_level(tracing::Level::TRACE)\n    // sets this to be the default, global collector for this application.\n    .init();\n\n    // Setting get_configuration(None) means we'll be using cargo-leptos's env values\n    // For deployment these variables are:\n    // <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>\n    // Alternately a file can be specified such as Some(\"Cargo.toml\")\n    // The file would need to be included with the executable when moved to deployment\n    let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .route(\"/api_app1/*fn_name\", post(leptos_axum::handle_server_fns))\n        .leptos_routes(&leptos_options, routes, App)\n        .fallback(file_and_error_handler)\n        .layer(tower_http::trace::TraceLayer::new_for_http())\n        .with_state(leptos_options);\n\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    logging::log!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for a purely client-side app\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-1/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n\ttext-align: center;\n}"
  },
  {
    "path": "projects/nginx-mpmc/app-2/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/Cargo.toml",
    "content": "[package]\nname = \"app-2\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nconsole_error_panic_hook = \"0.1.0\"\nleptos_meta = { version = \"0.6.0\" }\nleptos_router = { version = \"0.6.0\" }\ntower = { version = \"0.4.0\", optional = true }\ntower-http = { version = \"0.5.0\", features = [\"fs\"], optional = true }\nwasm-bindgen = \"0.2.89\"\nthiserror = \"1.0\"\ntracing = { version = \"0.1.0\", optional = true }\nhttp = \"1.0\"\n\naxum = { version = \"0.7.0\", optional = true }\nleptos = \"0.6.0\"\nleptos_axum = { version = \"0.6.0\", optional = true }\ntokio = { version = \"1.0\", features = [\"rt-multi-thread\"], optional = true }\nshared-server = { path = \"../shared-server\", default-features = false }\nshared-server-2 = { path = \"../shared-server-2\", default-features = false }\n\n# Defines a size-optimized profile for the WASM bundle in release mode\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[features]\nhydrate = [\n  \"leptos/hydrate\",\n  \"leptos_meta/hydrate\",\n  \"leptos_router/hydrate\",\n  \"shared-server/hydrate\",\n  \"shared-server-2/hydrate\",\n]\nssr = [\n  \"shared-server/ssr\",\n  \"shared-server-2/ssr\",\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:leptos_axum\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:tracing\",\n]\n\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"app-2\"\n\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n\n#\n#\n### WE CHANGED THIS IN THIS EXAMPLE\n#\n#\nsite-pkg-dir = \"pkg2\"\n\n\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"public\"\n\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3001\"\n\n# The port to use for automatic reload monitoring\nreload-port = 3005\n\n\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\n# The profile to use for the lib target when compiling for release\n#\n# Optional. Defaults to \"release\".\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/README.md",
    "content": "<picture>\n    <source srcset=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg\" media=\"(prefers-color-scheme: dark)\">\n    <img src=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg\" alt=\"Leptos Logo\">\n</picture>\n\n# Leptos Axum Starter Template\n\nThis is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool using [Axum](https://github.com/tokio-rs/axum).\n\n## Creating your template repo\n\nIf you don't have `cargo-leptos` installed you can install it with\n\n```bash\ncargo install cargo-leptos\n```\n\nThen run\n```bash\ncargo leptos new --git leptos-rs/start-axum\n```\n\nto generate a new project template.\n\n```bash\ncd app-2\n```\n\nto go to your newly created project.  \nFeel free to explore the project structure, but the best place to start with your application code is in `src/app.rs`.  \nAddtionally, Cargo.toml may need updating as new versions of the dependencies are released, especially if things are not working after a `cargo update`.\n\n## Running your project\n\n```bash\ncargo leptos watch\n```\n\n## Installing Additional Tools\n\nBy default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.\n\n1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly\n2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly\n3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)\n4. `npm install -g sass` - install `dart-sass` (should be optional in future\n\n## Compiling for Release\n```bash\ncargo leptos build --release\n```\n\nWill generate your server binary in target/server/release and your site package in target/site\n\n## Testing Your Project\n```bash\ncargo leptos end-to-end\n```\n\n```bash\ncargo leptos end-to-end --release\n```\n\nCargo-leptos uses Playwright as the end-to-end test tool.  \nTests are located in end2end/tests directory.\n\n## Executing a Server on a Remote Machine Without the Toolchain\nAfter running a `cargo leptos build --release` the minimum files needed are:\n\n1. The server binary located in `target/server/release`\n2. The `site` directory and all files within located in `target/site`\n\nCopy these files to your remote server. The directory structure should be:\n```text\napp-2\nsite/\n```\nSet the following environment variables (updating for your project as needed):\n```text\nLEPTOS_OUTPUT_NAME=\"app-2\"\nLEPTOS_SITE_ROOT=\"site\"\nLEPTOS_SITE_PKG_DIR=\"pkg\"\nLEPTOS_SITE_ADDR=\"127.0.0.1:3000\"\nLEPTOS_RELOAD_PORT=\"3001\"\n```\nFinally, run the server binary.\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/src/app.rs",
    "content": "use crate::error_template::{AppError, ErrorTemplate};\nuse leptos::*;\nuse leptos_meta::*;\nuse leptos_router::*;\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n\n    view! {\n\n\n        // injects a stylesheet into the document <head>\n        // id=leptos means cargo-leptos will hot-reload this stylesheet\n        <Stylesheet id=\"leptos\" href=\"/pkg2/app-2.css\"/>\n\n        // sets the document title\n        <Title text=\"Welcome to Leptos\"/>\n\n        // content for this welcome page\n        <Router fallback=|| {\n            let mut outside_errors = Errors::default();\n            outside_errors.insert_with_default_key(AppError::NotFound);\n            view! {\n                <ErrorTemplate outside_errors/>\n            }\n            .into_view()\n        }>\n            <main>\n                <Routes>\n                    <Route path=\"app2\" view=HomePage/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n/// Renders the home page of your application.\n#[component]\nfn HomePage() -> impl IntoView {\n    use shared_server::SharedServerFunction;\n    use shared_server_2::SharedServerFunction2;\n\n    let hello_1_action = Action::<SharedServerFunction,_>::server();\n    let hello_2_action = Action::<SharedServerFunction2,_>::server();\n\n    let value_1 = create_rw_signal(String::from(\"waiting for update from shared server.\"));\n    let value_2 = create_rw_signal(String::from(\"waiting for update from shared server 2.\"));\n\n    //let hello_2 = create_resource(move || (), shared_server_2::shared_server_function);\n    create_effect(move|_|{if let Some(Ok(msg)) = hello_1_action.value().get(){value_1.set(msg)}});\n    create_effect(move|_|{if let Some(Ok(msg)) = hello_2_action.value().get(){value_2.set(msg)}});\n\n    view! {\n        <h1> App 2</h1>\n        <div> action response from server 1 </div>\n        <button on:click=move|_|hello_1_action.dispatch(SharedServerFunction{})>request from shared server 1</button>\n        {move || value_1.get()}\n        <div> action response from server 2 </div>\n        <button on:click=move|_|hello_2_action.dispatch(SharedServerFunction2{})>request from shared server 2</button>\n        {move || value_2.get()}\n    }\n}\n\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/src/error_template.rs",
    "content": "use http::status::StatusCode;\nuse leptos::*;\nuse thiserror::Error;\n\n#[derive(Clone, Debug, Error)]\npub enum AppError {\n    #[error(\"Not Found\")]\n    NotFound,\n}\n\nimpl AppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            AppError::NotFound => StatusCode::NOT_FOUND,\n        }\n    }\n}\n\n// A basic function to display errors served by the error boundaries.\n// Feel free to do more complicated things here than just displaying the error.\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional)] errors: Option<RwSignal<Errors>>,\n) -> impl IntoView {\n    let errors = match outside_errors {\n        Some(e) => create_rw_signal(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n    // Get Errors from Signal\n    let errors = errors.get_untracked();\n\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors: Vec<AppError> = errors\n        .into_iter()\n        .filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())\n        .collect();\n    println!(\"Errors: {errors:#?}\");\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    #[cfg(feature = \"ssr\")]\n    {\n        use leptos_axum::ResponseOptions;\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors[0].status_code());\n        }\n    }\n\n    view! {\n        <h1>{if errors.len() > 1 {\"Errors\"} else {\"Error\"}}</h1>\n        <For\n            // a function that returns the items we're iterating over; a signal is fine\n            each= move || {errors.clone().into_iter().enumerate()}\n            // a unique key for each item as a reference\n            key=|(index, _error)| *index\n            // renders each item to a view\n            children=move |error| {\n                let error_string = error.1.to_string();\n                let error_code= error.1.status_code();\n                view! {\n                    <h2>{error_code.to_string()}</h2>\n                    <p>\"Error: \" {error_string}</p>\n                }\n            }\n        />\n    }\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/src/fileserv.rs",
    "content": "use axum::{\n    body::Body,\n    extract::State,\n    response::IntoResponse,\n    http::{Request, Response, StatusCode, Uri},\n};\nuse axum::response::Response as AxumResponse;\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\nuse leptos::*;\nuse crate::app::App;\n\npub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {\n    let root = options.site_root.clone();\n\n    tracing::debug!(\"APP 2\");\n\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        let handler = leptos_axum::render_app_to_stream(options.to_owned(), App);\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/src/lib.rs",
    "content": "pub mod app;\npub mod error_template;\n#[cfg(feature = \"ssr\")]\npub mod fileserv;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount_to_body(App);\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::{\n        Router,\n        routing::get,\n    };\n    use leptos::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use app_2::app::*;\n    use app_2::fileserv::file_and_error_handler;\n\n    // Setting get_configuration(None) means we'll be using cargo-leptos's env values\n    // For deployment these variables are:\n    // <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>\n    // Alternately a file can be specified such as Some(\"Cargo.toml\")\n    // The file would need to be included with the executable when moved to deployment\n    let conf = get_configuration(Some(\"Cargo.toml\")).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, App)\n        .fallback(file_and_error_handler)\n        .layer(tower_http::trace::TraceLayer::new_for_http())\n        .with_state(leptos_options);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    logging::log!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for a purely client-side app\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/app-2/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n\ttext-align: center;\n}"
  },
  {
    "path": "projects/nginx-mpmc/kill.sh",
    "content": "lsof -ti :3000 | xargs kill && \\\nlsof -ti :3001 | xargs kill &&  \\\nlsof -ti :3002 | xargs kill && \\\nlsof -ti :3003 | xargs kill && \\\nlsof -ti :80 | xargs kill"
  },
  {
    "path": "projects/nginx-mpmc/nginx.conf",
    "content": "events {\n\n}\nhttp {\n    # set aliases\n    upstream app_server {\n        server host.docker.internal:3000;\n    }\n    upstream app_2_server {\n        server host.docker.internal:3001;\n    }\n    upstream shared_server {\n        server host.docker.internal:3002;\n    }\n    upstream shared_server_2 {\n        server host.docker.internal:3003;\n    }\n\n    server {\n        listen 80;\n        #server_name _;\n        # /app2 will serve the client for app2, and any client can call the api by calling /app2/api\n        location /app2 {\n            proxy_pass http://app_2_server;\n        }\n        # We need to set app2 to have a different pkg directory, and to forward on that.\n        location /pkg2 {\n            proxy_pass http://app_2_server;\n        }\n        # /api_shared will call the server functions registered on shared_server\n        location /api_shared {\n            proxy_pass http://shared_server;\n        }\n        # /api_shared_2 will call the server functions registered on shared_server_2\n        location /api_shared2 {\n            proxy_pass http://shared_server_2;\n        }\n        # we will by default serve the client for app-1\n        location / {\n            proxy_pass http://app_server;\n        }\n    }\n}"
  },
  {
    "path": "projects/nginx-mpmc/nginx_linux.conf",
    "content": "events {\n\n}\nhttp {\n    # set aliases\n    upstream app_server {\n        server 127.0.0.1:3000;\n    }\n    upstream app_2_server {\n        server 127.0.0.1:3001;\n    }\n    upstream shared_server {\n        server 127.0.0.1:3002;\n    }\n    upstream shared_server_2 {\n        server 127.0.0.1:3003;\n    }\n\n    server {\n        listen 80;\n        #server_name _;\n        # /app2 will serve the client for app2, and any client can call the api by calling /app2/api\n        location /app2 {\n            proxy_pass http://app_2_server;\n        }\n        # /api_shared will call the server functions registered on shared_server\n        location /api_shared {\n            proxy_pass http://shared_server;\n        }\n        # /api_shared_2 will call the server functions registered on shared_server_2\n        location /api_shared2 {\n            proxy_pass http://shared_server_2;\n        }\n        # we will by default serve the client for app-1\n        location / {\n            proxy_pass http://app_server;\n        }\n    }\n}"
  },
  {
    "path": "projects/nginx-mpmc/run.sh",
    "content": "# save pwd variable\n# append pwd to nginx.conf prefix\n# run this command with the new nginx.conf path\n(cd app-1 && cargo leptos serve)  & \\\n(cd app-2 && cargo leptos serve) & \\\n(cd shared-server-1 && cargo run) & \\\n(cd shared-server-2 && cargo run) & \\\n( current_dir=$(pwd) && \\\ndocker run --rm -v \"$current_dir\"/nginx.conf:/etc/nginx/nginx.conf:ro -p 80:80 nginx)\n"
  },
  {
    "path": "projects/nginx-mpmc/run_linux.sh",
    "content": "# save pwd variable\n# append pwd to nginx.conf prefix\n# run this command with the new nginx.conf path\n(cd app-1 && cargo leptos serve)  & \\\n(cd app-2 && cargo leptos serve) & \\\n(cd shared-server-1 && cargo run) & \\\n(cd shared-server-2 && cargo run) & \\\n( current_dir=$(pwd) && \\\ndocker run --rm -v \"$current_dir\"/nginx_linux.conf:/etc/nginx/nginx.conf:ro -p 80:80 --network=\"host\" nginx)\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-1/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-1/Cargo.toml",
    "content": "[package]\nname = \"shared-server-1\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.7.0\", optional = true }\nleptos = \"0.6.0\"\nleptos_axum = { version = \"0.6.0\", optional = true }\ntokio = { version = \"1.0\", features = [\"rt-multi-thread\"], optional = true }\ntower-http = { version = \"0.5.0\", optional = true, features = [\"trace\"] }\ntracing = { version = \"0.1.40\", optional = true }\ntracing-subscriber = { version = \"0.3.18\", optional = true }\n\n[features]\ndefault = [\"ssr\"]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"dep:leptos_axum\",\n  \"dep:tracing\",\n  \"dep:tracing-subscriber\",\n  \"dep:tower-http\",\n  \"leptos/ssr\",\n]\n\n#We don't need cargo leptos options because we're not using cargo leptos.\n\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-1/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-1/README.md",
    "content": "<picture>\n    <source srcset=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg\" media=\"(prefers-color-scheme: dark)\">\n    <img src=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg\" alt=\"Leptos Logo\">\n</picture>\n\n# Leptos Axum Starter Template\n\nThis is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool using [Axum](https://github.com/tokio-rs/axum).\n\n## Creating your template repo\n\nIf you don't have `cargo-leptos` installed you can install it with\n\n```bash\ncargo install cargo-leptos\n```\n\nThen run\n```bash\ncargo leptos new --git leptos-rs/start-axum\n```\n\nto generate a new project template.\n\n```bash\ncd shared-server\n```\n\nto go to your newly created project.  \nFeel free to explore the project structure, but the best place to start with your application code is in `src/app.rs`.  \nAddtionally, Cargo.toml may need updating as new versions of the dependencies are released, especially if things are not working after a `cargo update`.\n\n## Running your project\n\n```bash\ncargo leptos watch\n```\n\n## Installing Additional Tools\n\nBy default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.\n\n1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly\n2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly\n3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)\n4. `npm install -g sass` - install `dart-sass` (should be optional in future\n\n## Compiling for Release\n```bash\ncargo leptos build --release\n```\n\nWill generate your server binary in target/server/release and your site package in target/site\n\n## Testing Your Project\n```bash\ncargo leptos end-to-end\n```\n\n```bash\ncargo leptos end-to-end --release\n```\n\nCargo-leptos uses Playwright as the end-to-end test tool.  \nTests are located in end2end/tests directory.\n\n## Executing a Server on a Remote Machine Without the Toolchain\nAfter running a `cargo leptos build --release` the minimum files needed are:\n\n1. The server binary located in `target/server/release`\n2. The `site` directory and all files within located in `target/site`\n\nCopy these files to your remote server. The directory structure should be:\n```text\nshared-server\nsite/\n```\nSet the following environment variables (updating for your project as needed):\n```text\nLEPTOS_OUTPUT_NAME=\"shared-server\"\nLEPTOS_SITE_ROOT=\"site\"\nLEPTOS_SITE_PKG_DIR=\"pkg\"\nLEPTOS_SITE_ADDR=\"127.0.0.1:3000\"\nLEPTOS_RELOAD_PORT=\"3001\"\n```\nFinally, run the server binary.\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-1/src/lib.rs",
    "content": "use leptos::*;\n\n#[cfg(feature=\"ssr\")]\n#[derive(Clone)]\npub struct SharedServerState;\n\n\n#[tracing::instrument]\n#[server(prefix=\"/api_shared\",endpoint=\"/a\")]\npub async fn shared_server_function() -> Result<String,ServerFnError> {\n    tracing::debug!(\"SHARED SERVER 1\");\n\n    let _ : axum::Extension<SharedServerState> = leptos_axum::extract().await?;\n    Ok(\"This message is from the shared server.\".to_string())\n}\n\n//http://127.0.0.1:3002/api/shared/shared_server_function\n// No hydrate function on a server function only server."
  },
  {
    "path": "projects/nginx-mpmc/shared-server-1/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use axum::routing::post;\n\n    tracing_subscriber::fmt()\n    .pretty()\n    .with_thread_names(true)\n    // enable everything\n    .with_max_level(tracing::Level::TRACE)\n    // sets this to be the default, global collector for this application.\n    .init();\n\n    // In production you wouldn't want to use a hardcoded address like this.\n    let addr = \"127.0.0.1:3002\";\n    // build our application with a route\n    let app = Router::new()\n        .route(\"/api_shared/*fn_name\", post(leptos_axum::handle_server_fns))\n        .layer(tower_http::trace::TraceLayer::new_for_http())\n        .layer(axum::Extension(shared_server::SharedServerState));\n\n    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();\n    println!(\"shared server listening on http://{}\", addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // our server is SSR only, we have no client pair.\n    // We'll only ever run this with cargo run --features ssr\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-1/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n\ttext-align: center;\n}"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-2/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-2/Cargo.toml",
    "content": "[package]\nname = \"shared-server-2\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.7.0\", optional = true }\nleptos = \"0.6.0\"\nleptos_axum = { version = \"0.6.0\", optional = true }\ntokio = { version = \"1.0\", features = [\"rt-multi-thread\"], optional = true }\ntower-http = { version = \"0.5.0\", optional = true, features = [\"trace\"] }\ntracing = { version = \"0.1.40\", optional = true }\ntracing-subscriber = { version = \"0.3.18\", optional = true }\n\n[features]\ndefault = [\"ssr\"]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"dep:leptos_axum\",\n  \"dep:tracing\",\n  \"dep:tracing-subscriber\",\n  \"dep:tower-http\",\n  \"leptos/ssr\",\n]\n\n#We don't need cargo leptos options because we're not using cargo leptos.\n\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-2/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-2/README.md",
    "content": "<picture>\n    <source srcset=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg\" media=\"(prefers-color-scheme: dark)\">\n    <img src=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg\" alt=\"Leptos Logo\">\n</picture>\n\n# Leptos Axum Starter Template\n\nThis is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool using [Axum](https://github.com/tokio-rs/axum).\n\n## Creating your template repo\n\nIf you don't have `cargo-leptos` installed you can install it with\n\n```bash\ncargo install cargo-leptos\n```\n\nThen run\n```bash\ncargo leptos new --git leptos-rs/start-axum\n```\n\nto generate a new project template.\n\n```bash\ncd shared-server\n```\n\nto go to your newly created project.  \nFeel free to explore the project structure, but the best place to start with your application code is in `src/app.rs`.  \nAddtionally, Cargo.toml may need updating as new versions of the dependencies are released, especially if things are not working after a `cargo update`.\n\n## Running your project\n\n```bash\ncargo leptos watch\n```\n\n## Installing Additional Tools\n\nBy default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.\n\n1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly\n2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly\n3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)\n4. `npm install -g sass` - install `dart-sass` (should be optional in future\n\n## Compiling for Release\n```bash\ncargo leptos build --release\n```\n\nWill generate your server binary in target/server/release and your site package in target/site\n\n## Testing Your Project\n```bash\ncargo leptos end-to-end\n```\n\n```bash\ncargo leptos end-to-end --release\n```\n\nCargo-leptos uses Playwright as the end-to-end test tool.  \nTests are located in end2end/tests directory.\n\n## Executing a Server on a Remote Machine Without the Toolchain\nAfter running a `cargo leptos build --release` the minimum files needed are:\n\n1. The server binary located in `target/server/release`\n2. The `site` directory and all files within located in `target/site`\n\nCopy these files to your remote server. The directory structure should be:\n```text\nshared-server\nsite/\n```\nSet the following environment variables (updating for your project as needed):\n```text\nLEPTOS_OUTPUT_NAME=\"shared-server\"\nLEPTOS_SITE_ROOT=\"site\"\nLEPTOS_SITE_PKG_DIR=\"pkg\"\nLEPTOS_SITE_ADDR=\"127.0.0.1:3000\"\nLEPTOS_RELOAD_PORT=\"3001\"\n```\nFinally, run the server binary.\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-2/src/lib.rs",
    "content": "use leptos::*;\n\n#[cfg(feature=\"ssr\")]\n#[derive(Clone)]\npub struct SharedServerState2;\n\n#[tracing::instrument]\n#[server(prefix=\"/api_shared2\",endpoint=\"/a\")]\npub async fn shared_server_function2() -> Result<String,ServerFnError> {\n    tracing::debug!(\"SHARED SERVER 2\");\n\n    let _ : axum::Extension<SharedServerState2> = leptos_axum::extract().await?;\n    Ok(\"This message is from the shared server 2.\".to_string())\n}\n\n//http://127.0.0.1:3002/api/shared/shared_server_function\n// No hydrate function on a server function only server."
  },
  {
    "path": "projects/nginx-mpmc/shared-server-2/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use axum::routing::post;\n    // In production you wouldn't want to use a hardcoded address like this.\n    let addr = \"127.0.0.1:3003\";\n    // build our application with a route\n    let app = Router::new()\n        .route(\"/api_shared2/*fn_name\", post(leptos_axum::handle_server_fns))\n        .layer(tower_http::trace::TraceLayer::new_for_http())\n        .layer(axum::Extension(shared_server_2::SharedServerState2));\n\n    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();\n    println!(\"shared server listening on http://{}\", addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // our server is SSR only, we have no client pair.\n    // We'll only ever run this with cargo run --features ssr\n}\n"
  },
  {
    "path": "projects/nginx-mpmc/shared-server-2/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n\ttext-align: center;\n}"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# node e2e test tools and outputs\nnode_modules/\ntest-results/\nend2end/playwright-report/\nplaywright/.cache/\n\n.secret_key"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/Cargo.toml",
    "content": "[package]\nname = \"openapi-openai-api-swagger-ui\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.7.0\", optional = true }\nconsole_error_panic_hook = \"0.1.0\"\nleptos = { version = \"0.6.0\", features = [\"nightly\"] }\nleptos_axum = { version = \"0.6.0\", optional = true }\nleptos_meta = { version = \"0.6.0\", features = [\"nightly\"] }\nleptos_router = { version = \"0.6.0\", features = [\"nightly\"] }\ntokio = { version = \"1.0\", features = [\"rt-multi-thread\"], optional = true }\ntower = { version = \"0.4.0\", optional = true }\ntower-http = { version = \"0.5.0\", features = [\"fs\"], optional = true }\nwasm-bindgen = \"0.2.92\"\nthiserror = \"1.0\"\ntracing = { version = \"0.1.0\", optional = true }\nutoipa = { version = \"4.2\", optional = true, features = [\"debug\"] }\nutoipa-swagger-ui = { version = \"6.0\", optional = true, features = [\"axum\"] }\nhttp = \"1.0\"\nserde = \"1.0\"\nserde_json = { version = \"1.0\", optional = true }\nopenai_dive = { version = \"0.5.7\", optional = true }\nreqwest = \"0.12.4\"\nuuid = { version = \"1.8\", features = [\"v4\"] }\n\n[features]\nhydrate = [\"leptos/hydrate\", \"leptos_meta/hydrate\", \"leptos_router/hydrate\"]\nssr = [\n  \"dep:openai_dive\",\n  \"dep:serde_json\",\n  \"dep:utoipa-swagger-ui\",\n  \"dep:utoipa\",\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:leptos_axum\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:tracing\",\n]\n\n# Defines a size-optimized profile for the WASM bundle in release mode\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"openapi-swagger-ui\"\n\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"public\"\n\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n\n# The port to use for automatic reload monitoring\nreload-port = 3001\n\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\nend2end-cmd = \"npx playwright test\"\nend2end-dir = \"end2end\"\n\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\n# The profile to use for the lib target when compiling for release\n#\n# Optional. Defaults to \"release\".\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/README.md",
    "content": "#OpenAPI Swagger-Ui OpenAI GPT\n\nThis example shows how to document server functions via OpenAPI schema generated using Utoipa and serve the swagger ui via /swagger-ui endpoint. More than that, this example shows how to take said OpenAPI spec and turn it into a function list to feed to OpenAI's chat completion endpoint to generate the JSON values to feed back into our server functions.\n\nThe example shows an input and if you tell it to do something that is covered, say hello, or generate a list of names it will do that. \n\nTo use the AI part of this project provide your openAPI key in an environment variable when running cargo leptos.\n\n```sh\nOPENAI_API_KEY=my_secret_key cargo leptos serve\n```\n\n\n## Thoughts, Feedback, Criticism, Comments?\nSend me any of the above, I'm @sjud on leptos discord. I'm always looking to improve and make these projects more helpful for the community. So please let me know how I can do that. Thanks!"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/end2end/package.json",
    "content": "{\n  \"name\": \"end2end\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {},\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.28.0\"\n  }\n}\n"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/end2end/playwright.config.ts",
    "content": "import type { PlaywrightTestConfig } from \"@playwright/test\";\nimport { devices } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://github.com/motdotla/dotenv\n */\n// require('dotenv').config();\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nconst config: PlaywrightTestConfig = {\n  testDir: \"./tests\",\n  /* Maximum time one test can run for. */\n  timeout: 30 * 1000,\n  expect: {\n    /**\n     * Maximum time expect() should wait for the condition to be met.\n     * For example in `await expect(locator).toHaveText();`\n     */\n    timeout: 5000,\n  },\n  /* Run tests in files in parallel */\n  fullyParallel: true,\n  /* Fail the build on CI if you accidentally left test.only in the source code. */\n  forbidOnly: !!process.env.CI,\n  /* Retry on CI only */\n  retries: process.env.CI ? 2 : 0,\n  /* Opt out of parallel tests on CI. */\n  workers: process.env.CI ? 1 : undefined,\n  /* Reporter to use. See https://playwright.dev/docs/test-reporters */\n  reporter: \"html\",\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */\n    actionTimeout: 0,\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    // baseURL: 'http://localhost:3000',\n\n    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */\n    trace: \"on-first-retry\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"chromium\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n      },\n    },\n\n    {\n      name: \"firefox\",\n      use: {\n        ...devices[\"Desktop Firefox\"],\n      },\n    },\n\n    {\n      name: \"webkit\",\n      use: {\n        ...devices[\"Desktop Safari\"],\n      },\n    },\n\n    /* Test against mobile viewports. */\n    // {\n    //   name: 'Mobile Chrome',\n    //   use: {\n    //     ...devices['Pixel 5'],\n    //   },\n    // },\n    // {\n    //   name: 'Mobile Safari',\n    //   use: {\n    //     ...devices['iPhone 12'],\n    //   },\n    // },\n\n    /* Test against branded browsers. */\n    // {\n    //   name: 'Microsoft Edge',\n    //   use: {\n    //     channel: 'msedge',\n    //   },\n    // },\n    // {\n    //   name: 'Google Chrome',\n    //   use: {\n    //     channel: 'chrome',\n    //   },\n    // },\n  ],\n\n  /* Folder for test artifacts such as screenshots, videos, traces, etc. */\n  // outputDir: 'test-results/',\n\n  /* Run your local dev server before starting the tests */\n  // webServer: {\n  //   command: 'npm run start',\n  //   port: 3000,\n  // },\n};\n\nexport default config;\n"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/end2end/tests/example.spec.ts",
    "content": "import { test, expect } from \"@playwright/test\";\n\ntest(\"homepage has title and links to intro page\", async ({ page }) => {\n  await page.goto(\"http://localhost:3000/\");\n\n  await expect(page).toHaveTitle(\"Welcome to Leptos\");\n\n  await expect(page.locator(\"h1\")).toHaveText(\"Welcome to Leptos!\");\n});\n"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/src/app.rs",
    "content": "use crate::error_template::{AppError, ErrorTemplate};\nuse leptos::*;\nuse leptos_meta::*;\nuse leptos_router::*;\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n\n    view! {\n\n\n        // injects a stylesheet into the document <head>\n        // id=leptos means cargo-leptos will hot-reload this stylesheet\n        <Stylesheet id=\"leptos\" href=\"/pkg/openapi-swagger-ui.css\"/>\n\n        // sets the document title\n        <Title text=\"Welcome to Leptos\"/>\n\n        // content for this welcome page\n        <Router fallback=|| {\n            let mut outside_errors = Errors::default();\n            outside_errors.insert_with_default_key(AppError::NotFound);\n            view! {\n                <ErrorTemplate outside_errors/>\n            }\n            .into_view()\n        }>\n            <main>\n                <Routes>\n                    <Route path=\"\" view=HomePage/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n/// Renders the home page of your application.\n#[component]\nfn HomePage() -> impl IntoView {\n    let hello = Action::<HelloWorld,_>::server();\n    view! {\n        <button on:click = move |_| hello.dispatch(HelloWorld{say_whut:SayHello{say:true}})>\n            \"hello world\"\n        </button>\n\n\n        <ErrorBoundary\n            fallback=|err| view! { <p>{format!(\"{err:#?}\")}</p>}>\n            {\n                move || hello.value().get().map(|h|match h {\n                    Ok(h) => h.into_view(),\n                    err => err.into_view()\n                })\n            }\n        </ErrorBoundary>\n\n        <AiSayHello/>\n    }\n}\n\n#[cfg_attr(feature=\"ssr\",derive(utoipa::ToSchema))]\n#[derive(Debug,Copy,Clone,serde::Serialize,serde::Deserialize)]\npub struct SayHello {\n   say:bool,\n}\n\n// the following function comment is what our GPT will get\n/// Call to say hello world, or call to not say hello world.\n#[cfg_attr(feature=\"ssr\",utoipa::path(\n    post,\n    path = \"/api/hello_world\",\n    responses(\n        (status = 200, description = \"Hello world from server or maybe not?\", body = String),\n    ),\n    params(\n        (\"say_whut\" = SayHello, description = \"If true then say hello, if false then don't.\"),\n    )\n))]\n#[server(\n    // we need to encoude our server functions as json because that's what openai generates\n    input=server_fn::codec::Json,\n    endpoint=\"hello_world\"\n)]\npub async fn hello_world(say_whut:SayHello) -> Result<String,ServerFnError> {\n    if say_whut.say {\n        Ok(\"hello world\".to_string())\n    } else {\n        Ok(\"not hello\".to_string())\n    }\n}\n\n\n/// Takes a list of names\n#[cfg_attr(feature=\"ssr\",utoipa::path(\n    post,\n    path = \"/api/name_list\",\n    responses(\n        (status = 200, description = \"The same list you got back\", body = String),\n    ),\n    params(\n        (\"list\" = Vec<String>, description = \"A list of names\"),\n    )\n))]\n#[server(\n    input=server_fn::codec::Json,\n    endpoint=\"name_list\"\n)]\npub async fn name_list(list:Vec<String>) -> Result<Vec<String>,ServerFnError> {\n    Ok(list)\n}\n\n\n\n#[derive(Clone,Debug,PartialEq,serde::Serialize,serde::Deserialize)]\npub struct AiServerCall{\n    pub path:String,\n    pub args:String,\n}\n\n\n// Don't include our AI function in the OpenAPI\n#[server]\npub async fn ai_msg(msg:String) -> Result<AiServerCall,ServerFnError> {\n    crate::open_ai::call_gpt_with_api(msg).await.get(0).cloned().ok_or(ServerFnError::new(\"No first message\"))\n}\n\n#[component]\npub fn AiSayHello() -> impl IntoView {\n    let ai_msg = Action::<AiMsg, _>::server();\n    let result = create_rw_signal(Vec::new());\n    view!{\n        <ActionForm action=ai_msg>\n        <label> \"Tell the AI what function to call.\"\n        <input name=\"msg\"/>\n        </label>\n        <input type=\"submit\"/>\n        </ActionForm>\n        <div>\n        {\n            move || if let Some(Ok(AiServerCall{path,args})) = ai_msg.value().get() {\n                spawn_local(async move {\n                    let text = \n                    reqwest::Client::new()\n                    .post(format!(\"http://127.0.0.1:3000/api/{}\",path))\n                    .header(\"content-type\",\"application/json\")\n                    .body(args)\n                    .send()\n                    .await\n                    .unwrap()\n                    .text()\n                    .await\n                    .unwrap();\n                    result.update(|list|\n                            list.push(\n                                text\n                            )\n                        );\n                });\n            }\n        }\n        <For\n        each=move || result.get()\n        key=|_| uuid::Uuid::new_v4()\n        children=move |s:String| {\n          view! {\n            <p>{s}</p>\n          }\n        }\n      />\n        </div>\n    }\n}"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/src/error_template.rs",
    "content": "use http::status::StatusCode;\nuse leptos::*;\nuse thiserror::Error;\n\n#[derive(Clone, Debug, Error)]\npub enum AppError {\n    #[error(\"Not Found\")]\n    NotFound,\n}\n\nimpl AppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            AppError::NotFound => StatusCode::NOT_FOUND,\n        }\n    }\n}\n\n// A basic function to display errors served by the error boundaries.\n// Feel free to do more complicated things here than just displaying the error.\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional)] errors: Option<RwSignal<Errors>>,\n) -> impl IntoView {\n    let errors = match outside_errors {\n        Some(e) => create_rw_signal(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n    // Get Errors from Signal\n    let errors = errors.get_untracked();\n\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors: Vec<AppError> = errors\n        .into_iter()\n        .filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())\n        .collect();\n    println!(\"Errors: {errors:#?}\");\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    #[cfg(feature = \"ssr\")]\n    {\n        use leptos_axum::ResponseOptions;\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors[0].status_code());\n        }\n    }\n\n    view! {\n        <h1>{if errors.len() > 1 {\"Errors\"} else {\"Error\"}}</h1>\n        <For\n            // a function that returns the items we're iterating over; a signal is fine\n            each= move || {errors.clone().into_iter().enumerate()}\n            // a unique key for each item as a reference\n            key=|(index, _error)| *index\n            // renders each item to a view\n            children=move |error| {\n                let error_string = error.1.to_string();\n                let error_code= error.1.status_code();\n                view! {\n                    <h2>{error_code.to_string()}</h2>\n                    <p>\"Error: \" {error_string}</p>\n                }\n            }\n        />\n    }\n}\n"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/src/fileserv.rs",
    "content": "use axum::{\n    body::Body,\n    extract::State,\n    response::IntoResponse,\n    http::{Request, Response, StatusCode, Uri},\n};\nuse axum::response::Response as AxumResponse;\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\nuse leptos::*;\nuse crate::app::App;\n\npub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {\n    let root = options.site_root.clone();\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        let handler = leptos_axum::render_app_to_stream(options.to_owned(), App);\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/src/lib.rs",
    "content": "pub mod app;\npub mod error_template;\n#[cfg(feature = \"ssr\")]\npub mod fileserv;\n#[cfg(feature=\"ssr\")]\npub mod open_ai;\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount_to_body(App);\n}\n\n\n#[cfg(feature=\"ssr\")]\npub mod api_doc {\n    use crate::app::__path_hello_world;\n    use crate::app::SayHello;\n    use crate::app::__path_name_list;\n    #[derive(utoipa::OpenApi)]\n    #[openapi(\n        info(description = \"My Api description\"),\n        paths(hello_world,name_list), components(schemas(SayHello)),\n    )]\n    pub struct ApiDoc;\n}"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::Router;\n    use leptos::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use openapi_swagger_ui::app::*;\n    use openapi_swagger_ui::api_doc::ApiDoc;\n    use openapi_swagger_ui::fileserv::file_and_error_handler;\n    use utoipa::OpenApi;\n\n    // Setting get_configuration(None) means we'll be using cargo-leptos's env values\n    // For deployment these variables are:\n    // <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>\n    // Alternately a file can be specified such as Some(\"Cargo.toml\")\n    // The file would need to be included with the executable when moved to deployment\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, App)\n        .fallback(file_and_error_handler)\n        .merge(utoipa_swagger_ui::SwaggerUi::new(\"/swagger-ui\")\n        .url(\"/api-docs/openapi.json\", ApiDoc::openapi()))\n        .with_state(leptos_options);\n\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    logging::log!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for a purely client-side app\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/src/open_ai.rs",
    "content": "/*\nFollows \nhttps://cookbook.openai.com/examples/function_calling_with_an_openapi_spec\nclosely\n*/\n\npub static SYSTEM_MESSAGE :&'static str = \"\nYou are a helpful assistant.\nRespond to the following prompt by using function_call and then summarize actions.\nAsk for clarification if a user request is ambiguous.\n\";\nuse serde_json::Map;\nuse openai_dive::v1::api::Client;\nuse openai_dive::v1::models::Gpt4Engine;\nuse std::env;\nuse openai_dive::v1::resources::chat::{\n    ChatCompletionFunction, ChatCompletionParameters, ChatCompletionTool, ChatCompletionToolType, ChatMessage,\n    ChatMessageContent,Role,\n};\nuse utoipa::openapi::schema::Array;\nuse serde_json::Value;\nuse utoipa::openapi::schema::SchemaType;\nuse utoipa::openapi::schema::Schema;\nuse utoipa::OpenApi;\nuse serde_json::json;\nuse utoipa::openapi::path::{PathItemType,Parameter};\nuse utoipa::openapi::Required;\nuse utoipa::openapi::schema::Object;\nuse utoipa::openapi::RefOr;\npub fn make_openapi_call_via_gpt(message:String) -> ChatCompletionParameters {\n    let docs = super::api_doc::ApiDoc::openapi();\n    let mut functions = vec![];\n    // get each path and it's path item object\n    for (path,path_item) in docs.paths.paths.iter(){\n        // all our server functions are post.\n        let operation = path_item.operations.get(&PathItemType::Post).expect(\"Expect POST op\");\n        // This name will be given to the OpenAI API as part of our functions\n        let name = operation.operation_id.clone().expect(\"Each operation to have an operation id\");\n\n        // we'll use the description\n        let desc = operation.description.clone().expect(\"Each operation to have a description, this is how GPT knows what the functiond does and it is helpful for calling it.\");\n        let mut required_list = vec![];\n        let mut properties = serde_json::Map::new();\n        if let Some(params) = operation.parameters.clone() {\n            leptos::logging::log!(\"{params:#?}\");\n            for Parameter{name,description,required,schema,..} in params.into_iter() {\n                if required == Required::True {\n                    required_list.push(name.clone());\n                }\n                let description = description.unwrap_or_default();\n                if let Some(RefOr::Ref(utoipa::openapi::schema::Ref{ref_location,..})) = schema {\n                    let schema_name = ref_location.split('/').last().expect(\"Expecting last after split\");\n                    let RefOr::T(schema) = docs.components\n                    .as_ref()\n                    .expect(\"components\")\n                    .schemas\n                    .get(schema_name)\n                    .cloned()\n                    .expect(\"{schema_name} to be in components as a schema\") else {panic!(\"expecting T\")};\n                let mut output = Map::new();\n                parse_schema_into_openapi_property(name.clone(),schema,&mut output);\n                properties.insert(name,serde_json::Value::Object(output));\n                } else if let Some(RefOr::T(schema)) = schema {\n                    let mut output = Map::new();\n                    parse_schema_into_openapi_property(name.clone(),schema,&mut output);\n                    properties.insert(name.clone(),serde_json::Value::Object(output));                   \n                }\n                \n            }\n        } \n        let parameters = json!({\n            \"type\": \"object\",\n            \"properties\": properties,\n            \"required\": required_list,\n        });\n        leptos::logging::log!(\"{parameters}\");\n\n        functions.push(\n            ChatCompletionFunction {\n                name,\n                description: Some(desc),\n                parameters,\n            }\n        )\n    }\n\n    ChatCompletionParameters {\n        model: Gpt4Engine::Gpt41106Preview.to_string(),\n        messages: vec![\n            ChatMessage {\n                role:Role::System,\n                content: ChatMessageContent::Text(SYSTEM_MESSAGE.to_string()),\n                ..Default::default()\n            },\n            ChatMessage {\n                role:Role::User,\n                content: ChatMessageContent::Text(message),\n            ..Default::default()\n        }],\n        tools: Some(functions.into_iter().map(|function|{\n            ChatCompletionTool {\n                r#type: ChatCompletionToolType::Function,\n                function,\n            }\n        }).collect::<Vec<ChatCompletionTool>>()),\n        ..Default::default()\n    }\n}\n\n\npub fn parse_schema_into_openapi_property(\n    name:String,\n    schema:Schema,\noutput: &mut serde_json::Map::<String,serde_json::Value>) {\n\n    let docs = super::api_doc::ApiDoc::openapi();\n    match schema {\n        Schema::Object(Object{\n            schema_type,\n            required,\n            properties,\n            ..\n        }) => match schema_type{\n            SchemaType::Object => {\n                output.insert(\"type\".to_string(),Value::String(\"object\".to_string()));\n                output.insert(\"required\".to_string(),Value::Array(required.into_iter()\n                    .map(|s|Value::String(s))\n                    .collect::<Vec<Value>>()));\n                    output.insert(\"properties\".to_string(),{\n                    let mut map = Map::new();\n                    for (key,val) in properties\n                        .into_iter()\n                        .map(|(key,val)|{\n                        let RefOr::T(schema) = val else {panic!(\"expecting t\")};\n                        let mut output = Map::new();\n                        parse_schema_into_openapi_property(name.clone(),schema,&mut output);\n                        (key,output)\n                    }) {\n                        map.insert(key,Value::Object(val));\n                    }\n                    Value::Object(map)\n                });\n    \n            },\n            SchemaType::Value => {\n                panic!(\"not expecting Value here.\");\n                \n            },\n            SchemaType::String => {\n                output.insert(\"type\".to_string(),serde_json::Value::String(\"string\".to_string()));\n                \n            },\n            SchemaType::Integer => {\n                output.insert(\"type\".to_string(),serde_json::Value::String(\"integer\".to_string()));\n                \n            },\n            SchemaType::Number => {\n                output.insert(\"type\".to_string(),serde_json::Value::String(\"number\".to_string()));\n                \n            },\n            SchemaType::Boolean => {\n                output.insert(\"type\".to_string(),serde_json::Value::String(\"boolean\".to_string()));\n                \n            },\n            SchemaType::Array => {\n                output.insert(\"type\".to_string(),serde_json::Value::String(\"array\".to_string()));\n                \n            },\n            \n        },\n        Schema::Array(Array{schema_type,items,..}) => {\n            match schema_type {\n                SchemaType::Array => {\n                    let mut map = Map::new();\n                    if let RefOr::Ref(utoipa::openapi::schema::Ref{ref_location,..}) = *items {\n                        let schema_name = ref_location.split('/').last().expect(\"Expecting last after split\");\n                        let RefOr::T(schema) = docs.components\n                        .as_ref()\n                        .expect(\"components\")\n                        .schemas\n                        .get(schema_name)\n                        .cloned()\n                        .expect(\"{schema_name} to be in components as a schema\") else {panic!(\"expecting T\")};\n                    let mut map = Map::new();\n                    parse_schema_into_openapi_property(name.clone(),schema,&mut map);\n                    output.insert(name.clone(),serde_json::Value::Object(map));\n                    } else if let RefOr::T(schema) = *items {\n                        let mut map = Map::new();\n                        parse_schema_into_openapi_property(name.clone(),schema,&mut map);\n                        output.insert(name,serde_json::Value::Object(map));\n                    }\n                },\n                _ => panic!(\"if schema is an array, then I'm expecting schema type to be an array \")\n            }\n        }\n        _ => panic!(\"I don't know how to handle this yet.\")\n    }\n    \n}\n//     let docs = super::api_doc::ApiDoc::openapi();\nuse crate::app::AiServerCall;\npub async fn call_gpt_with_api(message:String) -> Vec<AiServerCall> {\n    let api_key = std::env::var(\"OPENAI_API_KEY\").expect(\"$OPENAI_API_KEY is not set\");\n\n    let client = Client::new(api_key);\n    \n    let completion_parameters = make_openapi_call_via_gpt(message);\n\n    let result = client.chat().create(completion_parameters).await.unwrap();\n    let message = result.choices[0].message.clone();\n    let mut res = vec![];\n    if let Some(tool_calls) = message.clone().tool_calls {\n        for tool_call in tool_calls {\n            let name = tool_call.function.name;\n            let arguments = tool_call.function.arguments;\n            res.push(AiServerCall{\n                path:name,\n                args:arguments,\n            });\n        }\n    }\n    res\n}\n\n/*\ndef openapi_to_functions(openapi_spec):\n    functions = []\n\n    for path, methods in openapi_spec[\"paths\"].items():\n        for method, spec_with_ref in methods.items():\n            # 1. Resolve JSON references.\n            spec = jsonref.replace_refs(spec_with_ref)\n\n            # 2. Extract a name for the functions.\n            function_name = spec.get(\"operationId\")\n\n            # 3. Extract a description and parameters.\n            desc = spec.get(\"description\") or spec.get(\"summary\", \"\")\n\n            schema = {\"type\": \"object\", \"properties\": {}}\n\n            req_body = (\n                spec.get(\"requestBody\", {})\n                .get(\"content\", {})\n                .get(\"application/json\", {})\n                .get(\"schema\")\n            )\n            if req_body:\n                schema[\"properties\"][\"requestBody\"] = req_body\n\n            params = spec.get(\"parameters\", [])\n            if params:\n                param_properties = {\n                    param[\"name\"]: param[\"schema\"]\n                    for param in params\n                    if \"schema\" in param\n                }\n                schema[\"properties\"][\"parameters\"] = {\n                    \"type\": \"object\",\n                    \"properties\": param_properties,\n                }\n\n            functions.append(\n                {\"type\": \"function\", \"function\": {\"name\": function_name, \"description\": desc, \"parameters\": schema}}\n            )\n\n    return functions */"
  },
  {
    "path": "projects/openapi-openai-api-swagger-ui/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n\ttext-align: center;\n}"
  },
  {
    "path": "projects/ory-kratos/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/target/\npkg\n.vscode \n# These are backup files generated by rustfmt\n**/*.rs.bk\n\ne2e/target\ne2e/chromedriver_screenshot.png\ne2e/html\ne2e/network_output\ne2e/screenshots/*\n\ne2e/console_logs\n\n\nlocalhost+2-key.pem\nlocalhost+2.pem\nhost.docker.internal+3-key.pem\nhost.docker.internal+3.pem\nkey.pem\ncert.pem\nrootCA.pem\n\napp.db\napp.db-shm\napp.db-wal\n.DS_Store\n*/.DS_Stre"
  },
  {
    "path": "projects/ory-kratos/Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\"app\", \"frontend\", \"ids\", \"server\", \"e2e\"]\n\n# need to be applied only to wasm build\n[profile.release]\ncodegen-units = 1\nlto = true\nopt-level = 'z'\n\n[workspace.dependencies]\nleptos = { version = \"0.6.13\", features = [\"nightly\"] }\nleptos_meta = { version = \"0.6.13\", features = [\"nightly\"] }\nleptos_router = { version = \"0.6.13\", features = [\"nightly\"] }\nleptos_axum = { version = \"0.6.13\" }\nleptos-use = { version = \"0.11.3\" }\n\naxum = \"0.7.5\"\naxum-server = { version = \"0.6.0\", features = [\"tls-rustls\"] }\naxum-extra = { version = \"0.9.3\", features = [\"cookie\"] }\ncfg-if = \"1.0\"\nconsole_error_panic_hook = \"0.1.7\"\nconsole_log = \"1.0\"\nhttp = \"1.1\"\nids = { path = \"./ids\" }\n# this goes to this personal branch because of https://github.com/ory/sdk/issues/325#issuecomment-1960834676\nory-kratos-client = { git = \"https://github.com/sjud/kratos-client-rust\" }\nory-keto-client = { version = \"0.11.0-alpha.0\" }\nreqwest = { version = \"0.12.5\", features = [\"json\", \"cookies\"] }\nserde = \"1.0\"\nserde_json = \"1.0\"\nsqlx = { version = \"0.8.0\", features = [\"runtime-tokio\", \"sqlite\", \"macros\"] }\nthiserror = \"1.0\"\ntime = \"0.3.36\"\ntokio = { version = \"1.39\", features = [\"full\"] }\ntower = { version = \"0.4.13\", features = [\"full\"] }\ntower-http = { version = \"0.5.2\", features = [\"full\"] }\ntracing = \"0.1.40\"\ntracing-subscriber = { version = \"0.3.18\", features = [\"env-filter\"] }\nurl = \"2.5\"\nuuid = { version = \"1.10\", features = [\"v4\", \"serde\"] }\nwasm-bindgen = \"0.2.92\"\nweb-sys = { version = \"0.3.69\", features = [\n  \"HtmlDocument\",\n  \"HtmlFormElement\",\n  \"FormData\",\n] }\n\n\n# See https://github.com/akesson/cargo-leptos for documentation of all the parameters.\n\n# A leptos project defines which workspace members\n# that are used together frontend (lib) & server (bin)\n[[workspace.metadata.leptos]]\n# this name is used for the wasm, js and css file names\nname = \"ory-auth-example\"\n\n# the package in the workspace that contains the server binary (binary crate)\nbin-package = \"server\"\n\n# the package in the workspace that contains the frontend wasm binary (library crate)\nlib-package = \"frontend\"\n\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"style/main.scss\"\n\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"public\"\n\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n\n# The port to use for automatic reload monitoring\nreload-port = 3001\n\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"cargo test --test app_suite\"\nend2end-dir = \"e2e\"\n\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = []\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = []\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "projects/ory-kratos/LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  },
  {
    "path": "projects/ory-kratos/README.md",
    "content": "# Leptos Ory Kratos Integration (With Axum)\nThis repo used [start-axum-workspace](https://github.com/leptos-rs/start-axum-workspace/) as a base.\n\n## How to run the example.\n\nRun in different terminal windows (for the best result)\n\n```sh\ncargo leptos serve\n```\n\n```sh\ndocker compose up\n```\n\n```sh\ncargo test --test app_suite\n```\n\nThis will run our server, set up our compose file (MailCrab, Ory Kratos, Ory Ketos) and run the test suite that walks through logging in, registration, verification etc.\n\nThe e2e testing uses [chromiumoxide](https://crates.io/crates/chromiumoxide) and does things like monitor network requests, console messages, take screenshots during the flow and produces them when any of our feature tests fail. This can be a helpful starting point in debugging. Currently it just prints the output into files in the e2e directory but it could be modified to pipe them somewhere like a tool to help with the development process.\n\n\n## High Level Overview\n\nOur project runs a leptos server alongside various Ory Kratos. Kratos provides identification, and we use it when registering users, and credentialing them.\n<br>\nA normal flow would look something like:<br>\n<ul>\n<li>\nI go to the homepage,I click register\n</li>\n</li>\nI am redirected to the register page, the register page isn't hardcoded but is rendered by parsing the UI data structure given by Ory Kratos. The visible portions correspond to the fields we've set in our ./kratos/email.schema.json schema file, but it includes\nhidden fields (i.e a CSRF token to prevent CSRF). This project includes unstyled parsing code for the UI data structure.\n</li>\n<li>\nI sign up with an email and password\n</li>\n<li>\nOur leptos server will intercept the form data and then pass it on to the ory kratos service.\n</li>\n<li>\nOry Kratos validates those inputs given the validation criteria ./kratos/email.schema.json schema file\n</li>\n<li>\nOry Kratos then verifies me by sending me an email.\n</li>\n<li>\nIn this example we catch the email with an instance of mailcrab (an email server for testing purposes we run in our docker compose)\n. You can use mailcrab locally 127.0.0.1:1080\n</li>\n<li>\nI look inside the email, I see a code and a link where I will input the code.\n</li>\n<li>\nI click through and input the code, and I am verified.\n</li>\n<li>\nWhen I go to the login page, it's rendered based on the same method as the registration page. I.e Kratos sends a UI data structure which is parsed into the UI we show the user.\n</li>\n<li>\nI use my password and email on the login page to login.\n</li>\n<li>\nAgain, Our leptos server acts as the inbetween between the client and the Ory Kratos service. There were some pecularities between the CSRF token being set in the headers (which Ory Kratos updates with every step in the flow), SSR, and having the client communicate directly with Ory Kratos which lead me to use this approach where our server is the intermediary between the client and Ory Kratos.\n</li>\n<li>\nOry Kratos is session based, so after it recieves valid login credentials it creates a session and returns the session token. The session token is passed via cookies with every future request. All this does is establish the identity of the caller, to perform authentication we need a way to establish permissions given an individuals identity and how that relates to the content on the website. In this example I just use tables in the database but this example could be extended to use Ory Ketos, with is to Authorization a Ory Kratos is to Identification.\n</li>\n</ul>\n\nWhen given bad input in a field, Ory Kratos issues a new render UI data structure with error messages and we rerender the login page.\n\n## With regards to Ory Oathkeeper And Ory Ketos.\n\nOry Oathkeeper is a reverse proxy that sits between your server and the client, it takes the session token, looks to see what is being requested in the request and then checks the configuration files of your Ory Services to see if such a thing is allowed. It will communicate with the Ory services on your behalf and then pass on the authorized request to the appropriate location or reject it otherwise.\n<br>\nOry Ketos is the authorization part of the Ory suite, Ory Kratos simplies identifies the user (this is often conflated with authorization but authorization is different). Authorization is the process of after having confirmed a user's identity provisioning services based on some permission structure. I.e Role Based Authorization, Document based permissions, etc. Ory Ketos uses a similar configuration file based set up to Ory Kratos.\n<br>\nInstead of either of those, in this example we use an extractor to extract the session cookie and verify it with our kratos service and then perform our own checks. This is simpler to set up, more inutitive, and thus better for smaller projects. Identification is complicated, and it's nice to have it be modularized for whatever app we are building. This will save a lot of time when building multiple apps. The actual provisioning of services for most apps is much simpler, i.e database lookup tied to identification and some logic checks. Is the user preiumum? How much have they used the API compared to the maximum? Using Ory Kratos can reduce complexity and decrease your time to market, especially over multiple attempts.\n<br>\nIn production you'd have a virtual private server and you'd serve your leptos server behind Nginx, Nginx routes the calls to the Leptos Server and never to our Ory Kratos. Our Rust server handles all the communication between the client and Ory services. This is simpler from an implementation perspective then including Ory Oathkeeper and Ory Ketos. Ory Kratos/Ketos presume all api calls they recieve are valid by default, so it's best not to expose them at all to any traffic from the outside world. And when building our leptos app we'll have a clear idea about when and how these services are being communicated with when our service acts as the intermediary.\n\n## How this project is tested\n\nWe use Gherkin feature files to describe the behavior of the application. We use [cucumber](https://docs.rs/cucumber/latest/cucumber/) as our test harness and match the feature files to [chromiumoxide](https://docs.rs/chromiumoxide/latest/chromiumoxide/) code to drive a local chromium application. I'm using e2e testing mostly to confirm that the service provides the value to the user, in this case just authorization testing. And that, that value proposition doesn't break when we change some middleware code that touches everything etc.\n<br>\nThe `ids` crate includes a list of static strings that we'll use in our chromiumoxide lookups and our frontend to make our testing as smooth as possible. There are other ways to do this, such as find by text, which would find the \"Sign Up\" text and click it etc. So these tests don't assert anything with regards to presentation, just functionality.\n\n## How to use mkcert to get a locally signed certificate (and why)\nWe need to use https because we are sending cookies with the `Secure;` flag, cookies with the Secure flag can't be used \nunless delivered over https. Since we're using chromedriver for e2e testing let's use mkcert to create a cert that will allow \nhttps://127.0.0.1:3000/ to be a valid url.\nInstall mkcert and then\n\n```sh\nmkcert -install localhost 127.0.0.1 ::1\n```\n\nCopy your cert.pem, key.pem and rootCA.pem into this crate's root.\n\n\n## Thoughts, Feedback, Criticism, Comments?\nSend me any of the above, I'm @sjud on leptos discord. I'm always looking to improve and make these projects more helpful for the community. So please let me know how I can do that. Thanks!\n"
  },
  {
    "path": "projects/ory-kratos/app/Cargo.toml",
    "content": "[package]\nname = \"app\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nleptos.workspace = true\nleptos_meta.workspace = true\nleptos_router.workspace = true\nleptos_axum = { workspace = true, optional = true }\nleptos-use.workspace = true\n\naxum = { workspace = true, optional = true }\naxum-extra = { workspace = true, optional = true }\nhttp.workspace = true\ncfg-if.workspace = true\nthiserror.workspace = true\nserde_json.workspace = true\nserde.workspace = true\n\nory-kratos-client.workspace = true\nreqwest  = { workspace = true, optional = true }\ntime = {workspace  = true, optional = true }\ntracing = { workspace = true, optional = true }\nurl = { workspace = true, optional = true }\nuuid = { workspace = true}\nids.workspace = true\nwasm-bindgen = { workspace = true, optional = true}\nweb-sys = { workspace = true}\n\nsqlx = { workspace = true, optional = true}\n\n[features]\ndefault = []\nhydrate = [\"leptos/hydrate\", \"leptos_meta/hydrate\", \"leptos_router/hydrate\",\"dep:wasm-bindgen\"]\nssr = [\"leptos/ssr\", \"leptos_meta/ssr\", \"leptos_router/ssr\", \"dep:sqlx\",\"leptos-use/axum\",\"leptos-use/ssr\",\"dep:time\",\n\"dep:leptos_axum\",\"dep:axum\",\"dep:tracing\",\"dep:reqwest\",\"dep:url\",\"dep:axum-extra\"]\n\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/extractors.rs",
    "content": "use axum::{async_trait, extract::FromRequestParts, RequestPartsExt};\nuse axum_extra::extract::CookieJar;\nuse http::request::Parts;\nuse ory_kratos_client::models::session::Session;\nuse sqlx::SqlitePool;\n\nuse crate::database_calls::UserRow;\n\n#[derive(Clone, Debug, PartialEq)]\npub struct ExtractSession(pub Session);\n\n#[async_trait]\nimpl<S> FromRequestParts<S> for ExtractSession\nwhere\n    S: Send + Sync,\n{\n    type Rejection = String;\n\n    #[tracing::instrument(err(Debug), skip_all)]\n    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {\n        let cookie_jar = parts.extract::<CookieJar>().await.unwrap();\n        let csrf_cookie = cookie_jar\n            .iter()\n            .filter(|cookie| cookie.name().contains(\"csrf_token\"))\n            .next()\n            .ok_or(\n                \"Expecting a csrf_token cookie to already be set if fetching a pre-existing flow\"\n                    .to_string(),\n            )?;\n        let session_cookie = cookie_jar\n            .get(\"ory_kratos_session\")\n            .ok_or(\"Ory Kratos Session cookie does not exist.\".to_string())?;\n        let client = reqwest::ClientBuilder::new()\n            .redirect(reqwest::redirect::Policy::none())\n            .build()\n            .unwrap();\n\n        let resp = client\n            .get(\"http://127.0.0.1:4433/sessions/whoami\")\n            .header(\"accept\", \"application/json\")\n            .header(\n                \"cookie\",\n                format!(\"{}={}\", csrf_cookie.name(), csrf_cookie.value()),\n            )\n            .header(\n                \"cookie\",\n                format!(\"{}={}\", session_cookie.name(), session_cookie.value()),\n            )\n            .send()\n            .await\n            .map_err(|err| format!(\"Error sending resp to whoami err:{:#?}\", err).to_string())?;\n        let session = resp\n            .json::<Session>()\n            .await\n            .map_err(|err| format!(\"Error getting json from body err:{:#?}\", err).to_string())?;\n        Ok(Self(session))\n    }\n}\n\n#[derive(Clone, Debug, PartialEq)]\npub struct ExtractUserRow(pub UserRow);\n\n#[async_trait]\nimpl<S> FromRequestParts<S> for ExtractUserRow\nwhere\n    S: Send + Sync,\n{\n    type Rejection = String;\n\n    #[tracing::instrument(err(Debug), skip_all)]\n    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {\n        let identity_id = parts\n            .extract::<ExtractSession>()\n            .await?\n            .0\n            .identity\n            .ok_or(\"No identity\")?\n            .id;\n        let pool = parts\n            .extract::<axum::Extension<SqlitePool>>()\n            .await\n            .map_err(|err| format!(\"{err:#?}\"))?\n            .0;\n        let user = crate::database_calls::read_user_by_identity_id(&pool, &identity_id)\n            .await\n            .map_err(|err| format!(\"{err:#?}\"))?;\n\n        Ok(Self(user))\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/kratos_error.rs",
    "content": "use super::*;\n\n#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]\npub struct KratosError {\n    code: Option<usize>,\n    message: Option<String>,\n    reason: Option<String>,\n    debug: Option<String>,\n}\n\nimpl KratosError {\n    pub fn to_err_msg(self) -> String {\n        format!(\n            \"{}\\n{}\\n{}\\n{}\\n\",\n            self.code\n                .map(|code| code.to_string())\n                .unwrap_or(\"No Code included in error message\".to_string()),\n            self.message\n                .unwrap_or(\"No message in Kratos Error\".to_string()),\n            self.reason\n                .unwrap_or(\"No reason included in Kratos Error\".to_string()),\n            self.debug\n                .unwrap_or(\"No debug included in Kratos Error\".to_string())\n        )\n    }\n}\n\nimpl IntoView for KratosError {\n    fn into_view(self) -> View {\n        view!{\n            <div>{self.code.map(|code|code.to_string()).unwrap_or(\"No Code included in error message\".to_string())}</div>\n            <div>{self.message.unwrap_or(\"No message in Kratos Error\".to_string())}</div>\n            <div>{self.reason.unwrap_or(\"No reason included in Kratos Error\".to_string())}</div>\n            <div>{self.debug.unwrap_or(\"No debug included in Kratos Error\".to_string())}</div>\n        }.into_view()\n    }\n}\n\n#[server]\npub async fn fetch_error(id: String) -> Result<KratosError, ServerFnError> {\n    use ory_kratos_client::models::flow_error::FlowError;\n\n    let client = reqwest::ClientBuilder::new()\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    //https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors\n    let flow_error = client\n        .get(\"http://127.0.0.1:4433/self-service/errors\")\n        .query(&[(\"id\", id)])\n        .send()\n        .await?\n        .json::<FlowError>()\n        .await?;\n\n    let error = flow_error.error.ok_or(ServerFnError::new(\n        \"Flow error does not contain an actual error. This is a server error.\",\n    ))?;\n    Ok(serde_json::from_value::<KratosError>(error)?)\n}\n\n#[component]\npub fn KratosErrorPage() -> impl IntoView {\n    let id = move || use_query_map().get().get(\"id\").cloned().unwrap_or_default();\n    let fetch_error_resource = create_resource(move || id(), |id| fetch_error(id));\n    view! {\n        <Suspense fallback=||\"Error loading...\".into_view()>\n            <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n            { move ||\n                fetch_error_resource.get().map(|resp| match resp {\n                    // kratos error isn't an error type, it's just a ui/data representation of a kratos error.\n                    Ok(kratos_error) => kratos_error.into_view(),\n                    // notice how we don't deconstruct i.e Err(err), this will bounce up to the error boundary\n                    server_error => server_error.into_view()\n                })\n            }\n            </ErrorBoundary>\n        </Suspense>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/kratos_html.rs",
    "content": "use super::*;\nuse ory_kratos_client::models::ui_node_attributes::UiNodeAttributes;\nuse ory_kratos_client::models::ui_node_attributes::UiNodeAttributesTypeEnum;\nuse ory_kratos_client::models::UiNode;\nuse ory_kratos_client::models::UiText;\nuse std::collections::HashMap;\n\n/// https://www.ory.sh/docs/kratos/concepts/ui-user-interface\npub fn kratos_html(node: UiNode, body: RwSignal<HashMap<String, String>>) -> impl IntoView {\n    // the label that goes as the child of our label\n    let label_text = node.meta.label.map(|text| text.text);\n    // each node MAY have messages (i.e password is bad, email is wrong form etc)\n    let messages_html = view! {\n        <For\n        // a function that returns the items we're iterating over; a signal is fine\n        each=move || node.messages.clone()\n        // a unique key for each item\n        key=|ui_text| ui_text.id\n        // renders each item to a view\n        children=move |UiText { text,_type,.. }: UiText| {\n            // colored red, because we assume _type == error...\n            view!{<p style=\"color:red;\">{text}</p>}\n        }\n      />\n    };\n\n    let node_html = match *node.attributes {\n        UiNodeAttributes::UiNodeInputAttributes {\n            autocomplete,\n            disabled,\n            name,\n            required,\n            _type,\n            value,\n            // this is often empty for some reason?\n            label: _label,\n            ..\n        } => {\n            let autocomplete =\n                autocomplete.map_or(String::new(), |t| serde_json::to_string(&t).unwrap());\n            let label = label_text.unwrap_or(String::from(\"Unlabeled Input\"));\n            let required = required.unwrap_or_default();\n            let _type_str = serde_json::to_string(&_type).unwrap();\n            let name_clone = name.clone();\n            let name_clone_2 = name.clone();\n            let value = if let Some(serde_json::Value::String(value)) = value {\n                value\n            } else if value.is_none() {\n                \"\".to_string()\n            } else {\n                match serde_json::to_string(&value) {\n                    Ok(value) => value,\n                    Err(err) => {\n                        leptos::logging::log!(\"ERROR: not value? {:?}\", err);\n                        \"\".to_string()\n                    }\n                }\n            };\n            if _type == UiNodeAttributesTypeEnum::Submit {\n                body.update(|map| {\n                    _ = map.insert(name.clone(), value.clone());\n                });\n                view! {\n                    // will be something like value=\"password\" name=\"method\"\n                    // or value=\"oidc\" name=\"method\"\n                    <input type=\"hidden\" value=value name=name/>\n                    <input type=\"submit\" value=label/>\n                }\n                .into_view()\n            } else if _type != UiNodeAttributesTypeEnum::Hidden {\n                let id = ids::match_name_to_id(name.clone());\n\n                view! {\n                    <label>\n                       <span>{&label}</span>\n                      <input name=name\n                      id=id\n                      // we use replace here and in autocomplete because serde_json adds double quotes for some reason?\n                      type=_type_str.replace(\"\\\"\",\"\")\n                      value=move||body.get().get(&name_clone_2).cloned().unwrap_or_default()\n                      autocomplete=autocomplete.replace(\"\\\"\",\"\")\n                    disabled=disabled\n                    required=required placeholder=label\n                      on:input=move |ev|{\n                        let name = name_clone.clone();\n                        body.update(|map|{_=map.insert(name,event_target_value(&ev));})\n                      }\n                        />\n                    </label>\n                }\n                .into_view()\n            } else {\n                body.update(|map| {\n                    _ = map.insert(name.clone(), value.clone());\n                });\n                // this expects the identifier to be an email, but it could be telephone etc so code is extra fragile\n                view! {<input type=\"hidden\" value=value name=name /> }.into_view()\n            }\n        }\n        UiNodeAttributes::UiNodeAnchorAttributes { href, id, title } => {\n            let inner = title.text;\n            view! {<a href=href id=id>{inner}</a>}.into_view()\n        }\n        UiNodeAttributes::UiNodeImageAttributes {\n            height,\n            id,\n            src,\n            width,\n        } => view! {<img src=src height=height width=width id=id/>}.into_view(),\n        UiNodeAttributes::UiNodeScriptAttributes { .. } => view! {script not supported}.into_view(),\n        UiNodeAttributes::UiNodeTextAttributes {\n            id,\n            text:\n                box UiText {\n                    // not sure how to make use of context yet.\n                    context: _context,\n                    // redundant id?\n                    id: _id,\n                    text,\n                    // This could be, info, error, success. i.e context for msg responses on bad input etc\n                    _type,\n                },\n        } => view! {<p id=id>{text}</p>}.into_view(),\n    };\n    view! {\n        {node_html}\n        {messages_html}\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/login.rs",
    "content": "use super::*;\nuse ory_kratos_client::models::LoginFlow;\nuse ory_kratos_client::models::UiContainer;\nuse ory_kratos_client::models::UiText;\nuse std::collections::HashMap;\n\n#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\npub struct ViewableLoginFlow(LoginFlow);\nimpl IntoView for ViewableLoginFlow {\n    fn into_view(self) -> View {\n        format!(\"{:?}\", self).into_view()\n    }\n}\n#[tracing::instrument]\n#[server]\npub async fn init_login() -> Result<LoginResponse, ServerFnError> {\n    let client = reqwest::ClientBuilder::new()\n        .cookie_store(true)\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    // Get the csrf_token cookie.\n    let resp = client\n        .get(\"http://127.0.0.1:4433/self-service/login/browser\")\n        .send()\n        .await?;\n    let first_cookie = resp\n        .cookies()\n        .next()\n        .ok_or(ServerFnError::new(\"Expecting a first cookie\"))?;\n    let csrf_token = first_cookie.value();\n    let location = resp\n        .headers()\n        .get(\"Location\")\n        .ok_or(ServerFnError::new(\"expecting location in headers\"))?\n        .to_str()?;\n    // Parses the url and takes first query which will be flow=FLOW_ID and we get FLOW_ID at .1\n    let location_url = url::Url::parse(location)?;\n    let id = location_url\n        .query_pairs()\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting query in location header value\",\n        ))?\n        .1;\n    let set_cookie = resp\n        .headers()\n        .get(\"set-cookie\")\n        .ok_or(ServerFnError::new(\"expecting set-cookie in headers\"))?\n        .to_str()?;\n    let flow = client\n        .get(\"http://127.0.0.1:4433/self-service/login/flows\")\n        .query(&[(\"id\", id)])\n        .header(\"x-csrf-token\", csrf_token)\n        .send()\n        .await?\n        .json::<ViewableLoginFlow>()\n        .await?;\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n    opts.append_header(\n        axum::http::HeaderName::from_static(\"set-cookie\"),\n        axum::http::HeaderValue::from_str(set_cookie)?,\n    );\n    Ok(LoginResponse::Flow(flow))\n}\n\n#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\npub enum LoginResponse {\n    Flow(ViewableLoginFlow),\n    Success,\n}\nimpl IntoView for LoginResponse {\n    fn into_view(self) -> View {\n        match self {\n            Self::Flow(view) => view.into_view(),\n            _ => ().into_view(),\n        }\n    }\n}\n\n#[tracing::instrument]\n#[server]\npub async fn login(mut body: HashMap<String, String>) -> Result<LoginResponse, ServerFnError> {\n    use ory_kratos_client::models::error_browser_location_change_required::ErrorBrowserLocationChangeRequired;\n    use ory_kratos_client::models::generic_error::GenericError;\n    use reqwest::StatusCode;\n\n    let action = body\n        .remove(\"action\")\n        .ok_or(ServerFnError::new(\"Can't find action on body.\"))?;\n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    let csrf_cookie = cookie_jar\n        .iter()\n        .filter(|cookie| cookie.name().contains(\"csrf_token\"))\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting a csrf_token cookie to already be set if fetching a pre-existing flow\",\n        ))?;\n    let client = reqwest::ClientBuilder::new()\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    let resp = client\n        .post(&action)\n        .header(\"content-type\", \"application/json\")\n        .header(\n            \"cookie\",\n            format!(\"{}={}\", csrf_cookie.name(), csrf_cookie.value()),\n        )\n        .body(serde_json::to_string(&body)?)\n        .send()\n        .await?;\n\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n    opts.insert_header(\n        axum::http::HeaderName::from_static(\"cache-control\"),\n        axum::http::HeaderValue::from_str(\"private, no-cache, no-store, must-revalidate\")?,\n    );\n    for value in resp.headers().get_all(\"set-cookie\").iter() {\n        opts.append_header(\n            axum::http::HeaderName::from_static(\"set-cookie\"),\n            axum::http::HeaderValue::from_str(value.to_str()?)?,\n        );\n    }\n    if resp.status() == StatusCode::BAD_REQUEST {\n        Ok(LoginResponse::Flow(resp.json::<ViewableLoginFlow>().await?))\n    } else if resp.status() == StatusCode::OK {\n        // ory_kratos_session cookie set above.\n        Ok(LoginResponse::Success)\n    } else if resp.status() == StatusCode::GONE {\n        let err = resp.json::<GenericError>().await?;\n        let err = format!(\"{:#?}\", err);\n        Err(ServerFnError::new(err))\n    } else if resp.status() == StatusCode::UNPROCESSABLE_ENTITY {\n        let err = resp.json::<ErrorBrowserLocationChangeRequired>().await?;\n        let err = format!(\"{:#?}\", err);\n        Err(ServerFnError::new(err))\n    } else if resp.status() == StatusCode::TEMPORARY_REDIRECT {\n        let text = format!(\"{:#?}\", resp);\n        Err(ServerFnError::new(text))\n    } else {\n        // this is a status code that isn't covered by the documentation\n        // https://www.ory.sh/docs/reference/api#tag/frontend/operation/updateLoginFlow\n        let status_code = resp.status().as_u16();\n        Err(ServerFnError::new(format!(\n            \"{status_code} is not covered under the ory documentation?\"\n        )))\n    }\n}\n\n#[component]\npub fn LoginPage() -> impl IntoView {\n    let login = Action::<Login, _>::server();\n    let login_flow = create_local_resource(|| (), |_| async move { init_login().await });\n\n    let login_resp = create_rw_signal(None::<Result<LoginResponse, ServerFnError>>);\n    // after user tries to login we update the signal resp.\n    create_effect(move |_| {\n        if let Some(resp) = login.value().get() {\n            login_resp.set(Some(resp))\n        }\n    });\n    let login_flow = Signal::derive(move || {\n        if let Some(resp) = login_resp.get() {\n            Some(resp)\n        } else {\n            login_flow.get()\n        }\n    });\n    let body = create_rw_signal(HashMap::new());\n    view! {\n      <Suspense fallback=||view!{Loading Login Details}>\n        <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n        {\n          move ||\n            login_flow.get().map(|resp|\n                match resp {\n                    Ok(resp) => {\n                        match resp {\n                            LoginResponse::Flow(ViewableLoginFlow(LoginFlow{ui:box UiContainer{nodes,action,messages,..},..})) => {\n                                let form_inner_html = nodes.into_iter().map(|node|kratos_html(node,body)).collect_view();\n                                body.update(move|map|{_=map.insert(String::from(\"action\"),action);});\n                                    view!{\n                                        <form id=ids::LOGIN_FORM_ID\n                                        on:submit=move|e|{\n                                            e.prevent_default();\n                                            e.stop_propagation();\n                                            login.dispatch(Login{body:body.get_untracked()});\n                                        }>\n                                        {form_inner_html}\n                                        {messages.map(|messages|{\n                                            view!{\n                                                <For\n                                                    each=move || messages.clone().into_iter()\n                                                    key=|text| text.id\n                                                    children=move |text: UiText| {\n                                                      view! {\n                                                        <p id=text.id>{text.text}</p>\n                                                      }\n                                                    }\n                                                />\n                                            }\n                                        }).unwrap_or_default()}\n                                        </form>\n                                    }.into_view()\n                            },\n                            LoginResponse::Success => {\n                                view!{<Redirect path=\"/\"/>}.into_view()\n                            }\n                        }\n                    }\n                    err => err.into_view(),\n                })\n          }\n        </ErrorBoundary>\n      </Suspense>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/logout.rs",
    "content": "use super::*;\n\n#[tracing::instrument]\n#[server]\npub async fn logout() -> Result<(), ServerFnError> {\n    use ory_kratos_client::models::logout_flow::LogoutFlow;\n    use ory_kratos_client::models::ErrorGeneric;\n    use reqwest::StatusCode;\n\n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    let ory_kratos_session = cookie_jar\n        .get(\"ory_kratos_session\")\n        .ok_or(ServerFnError::new(\n            \"No `ory_kratos_session` cookie found. Logout shouldn't be visible.\",\n        ))?;\n    let client = reqwest::ClientBuilder::new()\n        .cookie_store(true)\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    // get logout url\n    let resp = client\n        .get(\"http://127.0.0.1:4433/self-service/logout/browser\")\n        .header(\n            \"cookie\",\n            format!(\n                \"{}={}\",ory_kratos_session.name(),ory_kratos_session.value()\n            ),\n        )\n        .send()\n        .await?;\n    let status = resp.status();\n    if status == StatusCode::NO_CONTENT || status == StatusCode::OK {\n        let LogoutFlow {\n            logout_token,\n            logout_url,\n        } = resp.json::<LogoutFlow>().await?;\n        tracing::error!(\"token : {logout_token} url : {logout_url}\");\n        let resp = client\n            .get(logout_url)\n            .query(&[(\"token\", logout_token), (\"return_to\", \"/\".to_string())])\n            .header(\"accept\",\"application/json\")\n            .header(\n                \"cookie\",\n                format!(\n                    \"{}={}\",\n                    ory_kratos_session.name(),\n                    ory_kratos_session.value()\n                ),\n            )\n            .send()\n            .await?;\n        let status = resp.status();\n        if status != StatusCode::OK && status != StatusCode::NO_CONTENT{\n            let error = resp.json::<ErrorGeneric>().await?;\n            return Err(ServerFnError::new(format!(\"{error:#?}\")));        \n        }\n        // set cookies to clear on the client.\n        crate::clear_cookies_inner().await?;\n        Ok(())\n    } else {\n        let location = resp\n            .headers()\n            .get(\"Location\")\n            .ok_or(ServerFnError::new(\"expecting location in headers\"))?\n            .to_str()?;\n        // Parses the url and takes first query which will be flow=FLOW_ID and we get FLOW_ID at .1\n        let location_url = url::Url::parse(location)?;\n        tracing::debug!(\"{}\", location_url);\n        let id = location_url\n            .query_pairs()\n            .next()\n            .ok_or(ServerFnError::new(\n                \"Expecting query in location header value\",\n            ))?\n            .1;\n        let kratos_err = kratos_error::fetch_error(id.to_string()).await?;\n        //let error = resp.json::<ory_keto_client::models::ErrorGeneric>().await?;\n        Err(ServerFnError::new(kratos_err.to_err_msg()))\n    }\n}\n\n#[component]\npub fn LogoutButton() -> impl IntoView {\n    let logout = Action::<Logout, _>::server();\n    view! {\n        <button id=ids::LOGOUT_BUTTON_ID on:click=move|_|logout.dispatch(Logout{})>\n            Logout\n            <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n            { move || logout.value().get().map(|resp|resp.into_view())}\n            </ErrorBoundary>\n        </button>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/mod.rs",
    "content": "use super::error_template::ErrorTemplate;\nuse leptos::*;\nuse leptos_router::*;\nuse leptos_meta::*;\npub mod kratos_html;\nuse kratos_html::kratos_html;\npub mod registration;\npub use registration::RegistrationPage;\npub mod verification;\nuse serde::{Deserialize, Serialize};\npub use verification::VerificationPage;\npub mod login;\npub use login::LoginPage;\npub mod session;\npub use session::HasSession;\n#[cfg(feature = \"ssr\")]\npub mod extractors;\npub mod kratos_error;\npub use kratos_error::KratosErrorPage;\npub mod logout;\npub use logout::LogoutButton;\npub mod recovery;\npub use recovery::RecoveryPage;\npub mod settings;\npub use settings::SettingsPage;\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/recovery.rs",
    "content": "use std::collections::HashMap;\n\nuse super::*;\nuse ory_kratos_client::models::{\n    ContinueWith, ContinueWithSettingsUiFlow, ErrorGeneric, RecoveryFlow, UiContainer, UiText,\n};\n/*\n    User clicks recover account button and is directed to the initiate recovery page\n    On the initiate recovery page they are asked for their email\n    We send an email to them with a recovery code to recover the identity\n    and a link to the recovery page which will prompt them for the code.\n    We validate the code\n    and we then direct them to the settings page for them to change their password.\n*/\n\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct ViewableRecoveryFlow(RecoveryFlow);\n// Implment IntoView, not because we want to use IntoView - but, just so we can use ErrorBoundary on the error.\nimpl IntoView for ViewableRecoveryFlow {\n    fn into_view(self) -> View {\n        format!(\"{:?}\", self).into_view()\n    }\n}\n\npub struct ViewableContinueWith(pub Vec<ContinueWith>);\nimpl IntoView for ViewableContinueWith {\n    fn into_view(self) -> View {\n        if let Some(first) = self.0.first() {\n            match first {\n                ContinueWith::ContinueWithSetOrySessionToken { ory_session_token } => todo!(),\n                ContinueWith::ContinueWithRecoveryUi { flow } => todo!(),\n                ContinueWith::ContinueWithSettingsUi {\n                    flow: box ContinueWithSettingsUiFlow { id },\n                } => view! {<Redirect path=format!(\"/settings?flow={id}\")/>}.into_view(),\n                ContinueWith::ContinueWithVerificationUi { flow } => todo!(),\n            }\n        } else {\n            ().into_view()\n        }\n    }\n}\n#[tracing::instrument]\n#[server]\npub async fn init_recovery_flow() -> Result<ViewableRecoveryFlow, ServerFnError> {\n    let client = reqwest::ClientBuilder::new()\n        .cookie_store(true)\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    // Get the csrf_token cookie.\n    let resp = client\n        .get(\"http://127.0.0.1:4433/self-service/recovery/browser\")\n        .header(\"accept\", \"application/json\")\n        .send()\n        .await?;\n\n    let cookie = resp\n        .headers()\n        .get(\"set-cookie\")\n        .ok_or(ServerFnError::new(\"Expecting a cookie\"))?\n        .to_str()?;\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n    opts.append_header(\n        axum::http::HeaderName::from_static(\"set-cookie\"),\n        axum::http::HeaderValue::from_str(cookie)?,\n    );\n    let status = resp.status();\n    if status == reqwest::StatusCode::OK {\n        let flow = resp.json::<RecoveryFlow>().await?;\n        Ok(ViewableRecoveryFlow(flow))\n    } else if status == reqwest::StatusCode::BAD_REQUEST {\n        let error = resp.json::<ErrorGeneric>().await?;\n        Err(ServerFnError::new(format!(\"{error:#?}\")))\n    } else {\n        tracing::error!(\n            \" UNHANDLED STATUS: {} \\n text: {}\",\n            status,\n            resp.text().await?\n        );\n        Err(ServerFnError::new(\"Developer made an oopsies.\"))\n    }\n}\n\n#[tracing::instrument(ret)]\n#[server]\npub async fn process_recovery(\n    mut body: HashMap<String, String>,\n) -> Result<ViewableRecoveryFlow, ServerFnError> {\n    use ory_kratos_client::models::error_browser_location_change_required::ErrorBrowserLocationChangeRequired;\n    use ory_kratos_client::models::generic_error::GenericError;\n    use reqwest::StatusCode;\n\n    let action = body\n        .remove(\"action\")\n        .ok_or(ServerFnError::new(\"Can't find action on body.\"))?;\n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    let csrf_cookie = cookie_jar\n        .iter()\n        .filter(|cookie| cookie.name().contains(\"csrf_token\"))\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting a csrf_token cookie to already be set if fetching a pre-existing flow\",\n        ))?;\n    let csrf_token = csrf_cookie.value();\n    let client = reqwest::ClientBuilder::new()\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    let resp = client\n        .post(&action)\n        .header(\"x-csrf-token\", csrf_token)\n        .header(\"content-type\", \"application/json\")\n        .header(\"accept\", \"application/json\")\n        .header(\n            \"cookie\",\n            format!(\"{}={}\", csrf_cookie.name(), csrf_cookie.value()),\n        )\n        .body(serde_json::to_string(&body)?)\n        .send()\n        .await?;\n\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n    opts.insert_header(\n        axum::http::HeaderName::from_static(\"cache-control\"),\n        axum::http::HeaderValue::from_str(\"private, no-cache, no-store, must-revalidate\")?,\n    );\n    for value in resp.headers().get_all(\"set-cookie\").iter() {\n        opts.append_header(\n            axum::http::HeaderName::from_static(\"set-cookie\"),\n            axum::http::HeaderValue::from_str(value.to_str()?)?,\n        );\n    }\n    if resp.status() == StatusCode::BAD_REQUEST || resp.status() == StatusCode::OK {\n        Ok(resp.json::<ViewableRecoveryFlow>().await?)\n    } else if resp.status() == StatusCode::SEE_OTHER {\n        let see_response = format!(\"{resp:#?}\");\n        let resp_text = resp.text().await?;\n        let err = format!(\"Developer needs to handle 303 SEE OTHER resp : \\n  {see_response} \\n body: \\n {resp_text}\");\n        Err(ServerFnError::new(err))\n    } else if resp.status() == StatusCode::GONE {\n        let err = resp.json::<GenericError>().await?;\n        let err = format!(\"{:#?}\", err);\n        Err(ServerFnError::new(err))\n    } else if resp.status() == StatusCode::UNPROCESSABLE_ENTITY {\n        let err = resp.json::<ErrorBrowserLocationChangeRequired>().await?;\n        let err = format!(\"{:#?}\", err);\n        Err(ServerFnError::new(err))\n    } else {\n        // this is a status code that isn't covered by the documentation\n        // https://www.ory.sh/docs/reference/api#tag/frontend/operation/updateRecoveryFlow\n        let status_code = resp.status().as_u16();\n        Err(ServerFnError::new(format!(\n            \"{status_code} is not covered under the ory documentation?\"\n        )))\n    }\n}\n\n#[component]\npub fn RecoveryPage() -> impl IntoView {\n    let recovery_flow = create_local_resource(|| (), |_| init_recovery_flow());\n    let recovery = Action::<ProcessRecovery, _>::server();\n\n    let recovery_resp = create_rw_signal(None::<Result<ViewableRecoveryFlow, ServerFnError>>);\n    create_effect(move |_| {\n        if let Some(resp) = recovery.value().get() {\n            recovery_resp.set(Some(resp))\n        }\n    });\n    let recovery_flow = Signal::derive(move || {\n        if let Some(resp) = recovery_resp.get() {\n            Some(resp)\n        } else {\n            recovery_flow.get()\n        }\n    });\n    let body = create_rw_signal(HashMap::new());\n    view! {\n        <Suspense fallback=||view!{}>\n            <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n            {\n                move ||\n                recovery_flow.get().map(|resp|\n                      match resp {\n                        Ok(ViewableRecoveryFlow(RecoveryFlow{\n                            continue_with,\n                            ui:box UiContainer{nodes,action,messages,..},..})) => {\n                                if let Some(continue_with) = continue_with {\n                                    return ViewableContinueWith(continue_with).into_view();\n                                }\n                            let form_inner_html = nodes.into_iter().map(|node|kratos_html(node,body)).collect_view();\n                            body.update(move|map|{_=map.insert(String::from(\"action\"),action);});\n                                view!{\n                                    <form id=ids::RECOVERY_FORM_ID\n                                    on:submit=move|e|{\n                                        if body.get().get(&String::from(\"code\")).is_some() {\n                                            // if we have a code we need to drop the email which will be stored from earlier.\n                                            // if we include the email then ory kratos server will not try to validate the code.\n                                            // but instead send another recovery email.\n                                            body.update(move|map|{_=map.remove(&String::from(\"email\"));});\n                                        }\n                                        e.prevent_default();\n                                        e.stop_propagation();\n                                        recovery.dispatch(ProcessRecovery{body:body.get_untracked()});\n                                    }>\n                                    {form_inner_html}\n                                    {messages.map(|messages|{\n                                        view!{\n                                            <For\n                                                each=move || messages.clone().into_iter()\n                                                key=|text| text.id\n                                                children=move |text: UiText| {\n                                                  view! {\n                                                    <p id=text.id>{text.text}</p>\n                                                  }\n                                                }\n                                            />\n                                        }\n                                    }).unwrap_or_default()}\n                                    </form>\n                                }.into_view()\n                          },\n                          err => err.into_view(),\n                      })\n                }\n            </ErrorBoundary>\n        </Suspense>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/registration.rs",
    "content": "use super::kratos_html;\nuse super::*;\nuse ory_kratos_client::models::RegistrationFlow;\nuse ory_kratos_client::models::UiContainer;\nuse ory_kratos_client::models::UiText;\nuse std::collections::HashMap;\n\n#[cfg(feature = \"ssr\")]\nuse reqwest::StatusCode;\n\n#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\npub struct ViewableRegistrationFlow(RegistrationFlow);\nimpl IntoView for ViewableRegistrationFlow {\n    fn into_view(self) -> View {\n        format!(\"{:?}\", self).into_view()\n    }\n}\n#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\npub enum RegistrationResponse {\n    Flow(ViewableRegistrationFlow),\n    Success,\n}\nimpl IntoView for RegistrationResponse {\n    fn into_view(self) -> View {\n        match self {\n            Self::Flow(view) => view.into_view(),\n            _ => ().into_view(),\n        }\n    }\n}\n#[tracing::instrument]\n#[server]\npub async fn init_registration() -> Result<RegistrationResponse, ServerFnError> {\n    let client = reqwest::ClientBuilder::new()\n        .cookie_store(true)\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    // Get the csrf_token cookie.\n    let resp = client\n        .get(\"http://127.0.0.1:4433/self-service/registration/browser\")\n        .send()\n        .await?;\n    let first_cookie = resp\n        .cookies()\n        .filter(|c| c.name().contains(\"csrf_token\"))\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting a cookie with csrf_token in name\",\n        ))?;\n    let csrf_token = first_cookie.value();\n    let location = resp\n        .headers()\n        .get(\"Location\")\n        .ok_or(ServerFnError::new(\"expecting location in headers\"))?\n        .to_str()?;\n    // Parses the url and takes first query which will be flow=FLOW_ID and we get FLOW_ID at .1\n    let location_url = url::Url::parse(location)?;\n    let id = location_url\n        .query_pairs()\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting query in location header value\",\n        ))?\n        .1;\n    let set_cookie = resp\n        .headers()\n        .get(\"set-cookie\")\n        .ok_or(ServerFnError::new(\"expecting set-cookie in headers\"))?\n        .to_str()?;\n    let resp = client\n        .get(\"http://127.0.0.1:4433/self-service/registration/flows\")\n        .query(&[(\"id\", id)])\n        .header(\"x-csrf-token\", csrf_token)\n        .send()\n        .await?;\n    let flow = resp.json::<ViewableRegistrationFlow>().await?;\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n    opts.insert_header(\n        axum::http::HeaderName::from_static(\"cache-control\"),\n        axum::http::HeaderValue::from_str(\"private, no-cache, no-store, must-revalidate\")?,\n    );\n    opts.append_header(\n        axum::http::HeaderName::from_static(\"set-cookie\"),\n        axum::http::HeaderValue::from_str(set_cookie)?,\n    );\n    Ok(RegistrationResponse::Flow(flow))\n}\n\n#[tracing::instrument(err)]\n#[server]\npub async fn register(\n    mut body: HashMap<String, String>,\n) -> Result<RegistrationResponse, ServerFnError> {\n    use ory_kratos_client::models::error_browser_location_change_required::ErrorBrowserLocationChangeRequired;\n    use ory_kratos_client::models::generic_error::GenericError;\n    use ory_kratos_client::models::successful_native_registration::SuccessfulNativeRegistration;\n\n    let pool = leptos_axum::extract::<axum::Extension<sqlx::SqlitePool>>().await?;\n\n    let action = body\n        .remove(\"action\")\n        .ok_or(ServerFnError::new(\"Can't find action on body.\"))?;\n    let email = body\n        .get(\"traits.email\")\n        .cloned()\n        .ok_or(ServerFnError::new(\"Can't find traits.email on body.\"))?;\n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    let csrf_cookie = cookie_jar\n        .iter()\n        .filter(|cookie| cookie.name().contains(\"csrf_token\"))\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting a csrf_token cookie to already be set if fetching a pre-existing flow\",\n        ))?;\n    \n    let client = reqwest::ClientBuilder::new()\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    let resp = client\n        .post(&action)\n        //.header(\"content-type\", \"application/json\")\n        .header(\n            \"cookie\",\n            format!(\"{}={}\", csrf_cookie.name(), csrf_cookie.value()),\n        )\n        .json(&body)\n        .send()\n        .await?;\n\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n    opts.insert_header(\n        axum::http::HeaderName::from_static(\"cache-control\"),\n        axum::http::HeaderValue::from_str(\"private, no-cache, no-store, must-revalidate\")?,\n    );\n    for value in resp.headers().get_all(\"set-cookie\").iter() {\n        opts.append_header(\n            axum::http::HeaderName::from_static(\"set-cookie\"),\n            axum::http::HeaderValue::from_str(value.to_str()?)?,\n        );\n    }\n    if resp.status() == StatusCode::BAD_REQUEST {\n        Ok(RegistrationResponse::Flow(\n            resp.json::<ViewableRegistrationFlow>().await?,\n        ))\n    } else if resp.status() == StatusCode::OK {\n        // get identity, session, session token\n        let SuccessfulNativeRegistration { identity, .. } =\n            resp.json::<SuccessfulNativeRegistration>().await?;\n        let identity_id = identity.id;\n        crate::database_calls::create_user(&pool, &identity_id, &email).await?;\n        //discard all? what about session_token? I guess we aren't allowing logging in after registration without verification..\n        Ok(RegistrationResponse::Success)\n    } else if resp.status() == StatusCode::GONE {\n        let err = resp.json::<GenericError>().await?;\n        let err = format!(\"{:#?}\", err);\n        Err(ServerFnError::new(err))\n    } else if resp.status() == StatusCode::UNPROCESSABLE_ENTITY {\n        let err = resp.json::<ErrorBrowserLocationChangeRequired>().await?;\n        let err = format!(\"{:#?}\", err);\n        Err(ServerFnError::new(err))\n    } else if resp.status() == StatusCode::TEMPORARY_REDIRECT {\n        let text = format!(\"{:#?}\", resp);\n        Err(ServerFnError::new(text))\n    } else {\n        // this is a status code that isn't covered by the documentation\n        // https://www.ory.sh/docs/reference/api#tag/frontend/operation/updateRegistrationFlow\n        let status_code = resp.status().as_u16();\n        Err(ServerFnError::new(format!(\n            \"{status_code} is not covered under the ory documentation?\"\n        )))\n    }\n}\n\n#[component]\npub fn RegistrationPage() -> impl IntoView {\n    let register = Action::<Register, _>::server();\n\n    // when we hit the page initiate a flow with kratos and get back data for ui renering.\n    let registration_flow =\n        create_local_resource(|| (), |_| async move { init_registration().await });\n    // Is none if user hasn't submitted data.\n    let register_resp = create_rw_signal(None::<Result<RegistrationResponse, ServerFnError>>);\n    // after user tries to register we update the signal resp.\n    create_effect(move |_| {\n        if let Some(resp) = register.value().get() {\n            register_resp.set(Some(resp))\n        }\n    });\n    // Merge our resource and our action results into a single signal.\n    // if the user hasn't tried to register yet we'll render the initial flow.\n    // if they have, we'll render the updated flow (including error messages etc).\n    let registration_flow = Signal::derive(move || {\n        if let Some(resp) = register_resp.get() {\n            Some(resp)\n        } else {\n            registration_flow.get()\n        }\n    });\n    // this is the body of our registration form, we don't know what the inputs are so it's a stand in for some\n    // json map of unknown argument length with type of string.\n    let body = create_rw_signal(HashMap::new());\n    view! {\n        // we'll render the fallback when the user hits the page for the first time\n      <Suspense fallback=||view!{Loading Registration Details}>\n        // if we get any errors, from either server functions we've merged we'll render them here.\n        <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n        {\n          move ||\n          // this is the resource XOR the results of the register action.\n          registration_flow.get().map(|resp|{\n                match resp {\n                    // TODO add Oauth using the flow args (see type docs)\n                    Ok(resp) => {\n                        match resp {\n                            RegistrationResponse::Flow(ViewableRegistrationFlow(RegistrationFlow{ui:box UiContainer{nodes,action,messages,..},..}))\n                            => {\n                                let form_inner_html = nodes.into_iter().map(|node|kratos_html(node,body)).collect_view();\n                                body.update(move|map|{_=map.insert(String::from(\"action\"),action);});\n\n                                view!{\n                                    <form\n\n                                    on:submit=move|e|{\n                                        e.prevent_default();\n                                        e.stop_propagation();\n                                        register.dispatch(Register{body:body.get_untracked()});\n                                    }\n                                    id=ids::REGISTRATION_FORM_ID\n                                    >\n                                    {form_inner_html}\n                                    // kratos_html renders messages for each node and these are the messages attached to the entire form.\n                                    {messages.map(|messages|{\n                                        view!{\n                                            <For\n                                                each=move || messages.clone().into_iter()\n                                                key=|text| text.id\n                                                children=move |text: UiText| {\n                                                  view! {\n                                                    <p id=text.id>{text.text}</p>\n                                                  }\n                                                }\n                                            />\n                                        }\n                                    }).unwrap_or_default()}\n                                    </form>\n                                }.into_view()\n\n                        },\n                        RegistrationResponse::Success => {\n                            view!{<div id=ids::VERIFY_EMAIL_DIV_ID>\"Check Email for Verification\"</div>}.into_view()\n                           }\n                        }\n                    },\n                    err => err.into_view(),\n                }\n            })\n          }\n        </ErrorBoundary>\n      </Suspense>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/session.rs",
    "content": "use super::*;\nuse ory_kratos_client::models::session::Session;\n\n#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\npub struct ViewableSession(pub Session);\nimpl IntoView for ViewableSession {\n    fn into_view(self) -> View {\n        format!(\"{:#?}\", self).into_view()\n    }\n}\n\n#[tracing::instrument]\n#[server]\npub async fn session_who_am_i() -> Result<ViewableSession, ServerFnError> {\n    use self::extractors::ExtractSession;\n    let session = leptos_axum::extract::<ExtractSession>().await?.0;\n    Ok(ViewableSession(session))\n}\n\n#[component]\npub fn HasSession() -> impl IntoView {\n    let check_session = Action::<SessionWhoAmI, _>::server();\n    view! {\n        <button on:click=move|_|check_session.dispatch(SessionWhoAmI{})>\n            Check Session Status\n            <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n                { move || check_session.value().get().map(|sesh|sesh.into_view()) }\n            </ErrorBoundary>\n        </button>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/settings.rs",
    "content": "use std::collections::HashMap;\n\nuse super::*;\nuse ory_kratos_client::models::{SettingsFlow, UiContainer, UiText};\n\n#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]\npub struct ViewableSettingsFlow(SettingsFlow);\n\nimpl IntoView for ViewableSettingsFlow {\n    fn into_view(self) -> View {\n        format!(\"{self:#?}\").into_view()\n    }\n}\n\n#[tracing::instrument(ret)]\n#[server]\npub async fn init_settings_flow(\n    flow_id: Option<String>,\n) -> Result<ViewableSettingsFlow, ServerFnError> {\n    use reqwest::StatusCode;\n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    let session_cookie = cookie_jar\n        .iter()\n        .filter_map(|cookie| {\n            if cookie.name().contains(\"ory_kratos_session\") {\n                Some(format!(\"{}={}\", cookie.name(), cookie.value()))\n            } else {\n                None\n            }\n        })\n        .next()\n        .ok_or(ServerFnError::new(\"Expecting session cookie\"))?;\n    let csrf_token = cookie_jar\n    .iter()\n    .filter_map(|cookie| {\n        if cookie.name().contains(\"csrf_token\") {\n            Some(format!(\"{}={}\", cookie.name(), cookie.value()))\n        } else {\n            None\n        }\n    })\n    .next()\n    .ok_or(ServerFnError::new(\"Expecting csrf token cookie.\"))?;\n    let client = reqwest::ClientBuilder::new()\n        .cookie_store(true)\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n\n    opts.insert_header(\n        axum::http::HeaderName::from_static(\"cache-control\"),\n        axum::http::HeaderValue::from_str(\"private, no-cache, no-store, must-revalidate\")?,\n    );\n    if let Some(flow_id) = flow_id {\n        // use flow id to get pre-existing session flow\n\n        let resp = client\n            .get(\"http://127.0.0.1:4433/self-service/settings/flows\")\n            .query(&[(\"id\", flow_id)])\n            .header(\"accept\", \"application/json\")\n            .header(\"cookie\", format!(\"{}; {}\",csrf_token,session_cookie))\n            .send()\n            .await?;\n\n        /*let cookie = resp\n            .headers()\n            .get(\"set-cookie\")\n            .ok_or(ServerFnError::new(\"Expecting a cookie\"))?\n            .to_str()?;\n        tracing::error!(\"set cookie init {cookie}\");\n        let opts = expect_context::<leptos_axum::ResponseOptions>();\n        opts.append_header(\n            axum::http::HeaderName::from_static(\"set-cookie\"),\n            axum::http::HeaderValue::from_str(cookie)?,\n        );*/\n        // expecting 200:settingsflow ok 401,403,404,410:errorGeneric\n        let status = resp.status();\n        if status == StatusCode::OK {\n            let flow = resp.json::<SettingsFlow>().await?;\n            Ok(ViewableSettingsFlow(flow))\n        } else if status == StatusCode::UNAUTHORIZED\n            || status == StatusCode::FORBIDDEN\n            || status == StatusCode::NOT_FOUND\n            || status == StatusCode::GONE\n        {\n            // 401 should really redirect to login form...\n\n            let err = resp\n                .json::<ory_kratos_client::models::ErrorGeneric>()\n                .await?;\n            Err(ServerFnError::new(format!(\"{err:#?}\")))\n        } else {\n            tracing::error!(\"UHHANDLED STATUS : {status}\");\n            Err(ServerFnError::new(\"This is a helpful error message.\"))\n        }\n    } else {\n        // create a new flow\n\n        let resp = client\n            .get(\"http://127.0.0.1:4433/self-service/settings/browser\")\n            .header(\"accept\", \"application/json\")\n            .header(\"cookie\", format!(\"{}; {}\",csrf_token,session_cookie))\n            .send()\n            .await?;\n        if resp.headers().get_all(\"set-cookie\").iter().count() == 0 {\n            tracing::error!(\"init set set-cookie is empty\");\n        }\n        let cookie = resp\n            .headers()\n            .get(\"set-cookie\")\n            .ok_or(ServerFnError::new(\"Expecting a cookie\"))?\n            .to_str()?;\n        let opts = expect_context::<leptos_axum::ResponseOptions>();\n        opts.append_header(\n            axum::http::HeaderName::from_static(\"set-cookie\"),\n            axum::http::HeaderValue::from_str(cookie)?,\n        );\n        // expecting 200:settingsflow ok 400,401,403:errorGeneric\n        let status = resp.status();\n        if status == StatusCode::OK {\n            let flow = resp.json::<SettingsFlow>().await?;\n            Ok(ViewableSettingsFlow(flow))\n        } else if status == StatusCode::BAD_REQUEST\n            || status == StatusCode::UNAUTHORIZED\n            || status == StatusCode::FORBIDDEN\n        {\n            let err = resp\n                .json::<ory_kratos_client::models::ErrorGeneric>()\n                .await?;\n            Err(ServerFnError::new(format!(\"{err:#?}\")))\n        } else {\n            tracing::error!(\"UHHANDLED STATUS : {status}\");\n            Err(ServerFnError::new(\"This is a helpful error message.\"))\n        }\n    }\n}\n\n#[tracing::instrument(ret)]\n#[server]\npub async fn update_settings(\n    flow_id: String,\n    mut body: HashMap<String, String>,\n) -> Result<ViewableSettingsFlow, ServerFnError> {\n    use ory_kratos_client::models::{\n        ErrorBrowserLocationChangeRequired, ErrorGeneric, GenericError,\n    };\n    use reqwest::StatusCode;\n    let session = leptos_axum::extract::<extractors::ExtractSession>().await?.0;\n    tracing::error!(\"{session:#?}\");\n    let action = body\n        .remove(\"action\")\n        .ok_or(ServerFnError::new(\"Can't find action on body.\"))?;\n \n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    let csrf_cookie = cookie_jar\n        .iter()\n        .filter(|cookie| cookie.name().contains(\"csrf_token\"))\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting a csrf_token cookie to already be set if fetching a pre-existing flow\",\n        ))?;\n    let ory_kratos_session = cookie_jar\n        .get(\"ory_kratos_session\")\n        .ok_or(ServerFnError::new(\n            \"No `ory_kratos_session` cookie found. Logout shouldn't be visible.\",\n        ))?;\n    let client = reqwest::ClientBuilder::new()\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    let req = client\n        .post(&action)\n        .header(\"accept\", \"application/json\")\n        .header(\"cookie\",format!(\"{}={}\",csrf_cookie.name(),csrf_cookie.value()))\n        .header(\"cookie\",format!(\"{}={}\",ory_kratos_session.name(),ory_kratos_session.value()))\n        .json(&body)\n        .build()?;\n    tracing::error!(\"{req:#?}\");\n\n    let resp = client.execute(req).await?;\n\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n\n    opts.insert_header(\n        axum::http::HeaderName::from_static(\"cache-control\"),\n        axum::http::HeaderValue::from_str(\"private, no-cache, no-store, must-revalidate\")?,\n    );\n    if resp.headers().get_all(\"set-cookie\").iter().count() == 0 {\n        tracing::error!(\"update set-cookie is empty\");\n    }\n    for value in resp.headers().get_all(\"set-cookie\").iter() {\n        tracing::error!(\"update set cookie {value:#?}\");\n        opts.append_header(\n            axum::http::HeaderName::from_static(\"set-cookie\"),\n            axum::http::HeaderValue::from_str(value.to_str()?)?,\n        );\n    }\n    // https://www.ory.sh/docs/reference/api#tag/frontend/operation/updateSettingsFlow\n    // expecting  400,200:settingsflow ok 401,403,404,410:errorGeneric 422:ErrorBrowserLocationChangeRequired\n    let status = resp.status();\n    if status == StatusCode::OK || status == StatusCode::BAD_REQUEST {\n        let flow = resp.json::<SettingsFlow>().await?;\n        Ok(ViewableSettingsFlow(flow))\n    } else if status == StatusCode::UNAUTHORIZED\n        || status == StatusCode::FORBIDDEN\n        || status == StatusCode::NOT_FOUND\n        || status == StatusCode::GONE\n    {\n        /*\n        let ErrorGeneric {\n            error: box GenericError { id, message, .. },\n        } = resp.json::<ErrorGeneric>().await?;\n        if let Some(id) = id {\n            match id.as_str() {\n                \"session_refresh_required\" =>\n                    /*\n                session_refresh_required: The identity requested to change something that needs a privileged session.\n                Redirect the identity to the login init endpoint with\n                query parameters ?refresh=true&return_to=<the-current-browser-url>,\n                or initiate a refresh login flow otherwise.\n                 */\n                    {}\n                \"security_csrf_violation\" =>\n                    /*\n                Unable to fetch the flow because a CSRF violation occurred.\n                 */\n                    {}\n                \"session_inactive\" =>\n                    /*\n                No Ory Session was found - sign in a user first.\n                 */\n                    {}\n                \"security_identity_mismatch\" =>\n                    /*\n                The flow was interrupted with session_refresh_required\n                but apparently some other identity logged in instead.\n\n                or\n\n                 The requested ?return_to address is not allowed to be used.\n                 Adjust this in the configuration!\n\n                 ?\n                 */\n                    {}\n                \"browser_location_change_required\" =>\n                    /*\n                Usually sent when an AJAX request indicates that the browser\n                needs to open a specific URL. Most likely used in Social Sign In flows.\n                */\n                    {}\n                _ => {}\n            }\n        }\n        */\n        let err = resp.json::<ErrorGeneric>().await?;\n        let err = format!(\"{err:#?}\");\n        Err(ServerFnError::new(err))\n    } else if status == StatusCode::UNPROCESSABLE_ENTITY {\n        let body = resp.json::<ErrorBrowserLocationChangeRequired>().await?;\n        tracing::error!(\"{body:#?}\");\n        Err(ServerFnError::new(\"Unprocessable.\"))\n    } else {\n        tracing::error!(\"UHHANDLED STATUS : {status}\");\n        Err(ServerFnError::new(\"This is a helpful error message.\"))\n    }\n}\n\n#[component]\npub fn SettingsPage() -> impl IntoView {\n    // get flow id from url\n    // if flow id doesn't exist we create a settings flow\n    // otherwise we fetch the settings flow with the flow id\n    // we update the settings page with the ui nodes\n    // we handle update settings\n    // if we are not logged in we'll be redirect to a login page\n\n    let init_settings_flow_resource = create_local_resource(\n        // use untracked here because we don't expect the url to change after resource has been fetched.\n        || use_query_map().get_untracked().get(\"flow\").cloned(),\n        |flow_id| init_settings_flow(flow_id),\n    );\n    let update_settings_action = Action::<UpdateSettings, _>::server();\n    let flow = Signal::derive(move || {\n        if let Some(flow) = update_settings_action.value().get() {\n            Some(flow)\n        } else {\n            init_settings_flow_resource.get()\n        }\n    });\n    let body = create_rw_signal(HashMap::new());\n    view! {\n    <Suspense fallback=||\"loading settings...\".into_view()>\n        <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n            {\n                move || flow.get().map(|resp|\n                    match resp {\n                        Ok(\n                            ViewableSettingsFlow(SettingsFlow{id,ui:box UiContainer{nodes,action,messages,..},..})\n                        ) => {\n                            let form_inner_html = nodes.into_iter().map(|node|kratos_html(node,body)).collect_view();\n                        body.update(move|map|{_=map.insert(String::from(\"action\"),action);});\n                        let id = create_rw_signal(id);\n                            view!{\n                                <form id=ids::SETTINGS_FORM_ID\n                                on:submit=move|e|{\n                                    e.prevent_default();\n                                    e.stop_propagation();\n                                    update_settings_action.dispatch(UpdateSettings{flow_id:id.get_untracked(),body:body.get_untracked()});\n                                }>\n                                {form_inner_html}\n                                {messages.map(|messages|{\n                                    view!{\n                                        <For\n                                            each=move || messages.clone().into_iter()\n                                            key=|text| text.id\n                                            children=move |text: UiText| {\n                                              view! {\n                                                <p id=text.id>{text.text}</p>\n                                              }\n                                            }\n                                        />\n                                    }\n                                }).unwrap_or_default()}\n                                </form>\n                            }.into_view()\n                        },\n                        err => err.into_view()\n                    })\n                }\n            </ErrorBoundary>\n            </Suspense>\n        }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/auth/verification.rs",
    "content": "use std::collections::HashMap;\n\nuse super::*;\nuse ory_kratos_client::models::{UiContainer, UiText, VerificationFlow};\n#[cfg(feature = \"ssr\")]\nuse tracing::debug;\n\n#[derive(Clone, Debug, Serialize, Deserialize)]\npub struct ViewableVerificationFlow(VerificationFlow);\nimpl IntoView for ViewableVerificationFlow {\n    fn into_view(self) -> View {\n        format!(\"{:#?}\", self.0).into_view()\n    }\n}\n// https://{project}.projects.oryapis.com/self-service/verification/flows?id={}\n#[tracing::instrument]\n#[server]\npub async fn init_verification(\n    flow_id: String,\n) -> Result<Option<ViewableVerificationFlow>, ServerFnError> {\n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    let csrf_cookie = cookie_jar\n        .iter()\n        .filter(|cookie| cookie.name().contains(\"csrf_token\"))\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting a csrf_token cookie to already be set if fetching a pre-existing flow\",\n        ))?;\n    let client = reqwest::ClientBuilder::new()\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    // https://www.ory.sh/docs/reference/api#tag/frontend/operation/getVerificationFlow\n    let resp = client\n        .get(\"http://127.0.0.1:4433/self-service/verification/flows\")\n        .query(&[(\"id\", flow_id)])\n        //.header(\"x-csrf-token\", csrf_token)\n        //.header(\"content-type\",\"application/json\")\n        .header(\n            \"cookie\",\n            format!(\"{}={}\", csrf_cookie.name(), csrf_cookie.value()),\n        )\n        .send()\n        .await?;\n    if resp.status().as_u16() == 403 {\n        debug!(\"{:#?}\", resp.text().await?);\n        Ok(None)\n    } else {\n        let flow = resp.json::<ViewableVerificationFlow>().await?;\n        Ok(Some(flow))\n    }\n}\n// verification flow complete POST\n//http://127.0.0.1:4433/self-service/verification\n#[tracing::instrument]\n#[server]\npub async fn verify(\n    mut body: HashMap<String, String>,\n) -> Result<Option<ViewableVerificationFlow>, ServerFnError> {\n    let action = body\n        .remove(\"action\")\n        .ok_or(ServerFnError::new(\"Can't find action on body.\"))?;\n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    let csrf_cookie = cookie_jar\n        .iter()\n        .filter(|cookie| cookie.name().contains(\"csrf_token\"))\n        .next()\n        .ok_or(ServerFnError::new(\n            \"Expecting a csrf_token cookie to already be set if fetching a pre-existing flow\",\n        ))?;\n    let client = reqwest::ClientBuilder::new()\n        .redirect(reqwest::redirect::Policy::none())\n        .build()?;\n    let resp = client\n        .post(&action)\n        .header(\"accept\", \"application/json\")\n        .header(\n            \"cookie\",\n            format!(\"{}={}\", csrf_cookie.name(), csrf_cookie.value()),\n        )\n        .json(&body)\n        .send()\n        .await?;\n\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n    opts.insert_header(\n        axum::http::HeaderName::from_static(\"cache-control\"),\n        axum::http::HeaderValue::from_str(\"private, no-cache, no-store, must-revalidate\")?,\n    );\n    match resp.json::<ViewableVerificationFlow>().await {\n        Ok(flow) => Ok(Some(flow)),\n        Err(_err) => Ok(None),\n    }\n}\n\n#[component]\npub fn VerificationPage() -> impl IntoView {\n    let verify = Action::<Verify, _>::server();\n\n    let params_map = use_query_map();\n    let init_verification = create_local_resource(\n        move || params_map().get(\"flow\").cloned().unwrap_or_default(),\n        |flow_id| async move { init_verification(flow_id).await },\n    );\n    let verfication_resp =\n        create_rw_signal(None::<Result<Option<ViewableVerificationFlow>, ServerFnError>>);\n    create_effect(move |_| {\n        if let Some(resp) = verify.value().get() {\n            verfication_resp.set(Some(resp))\n        }\n    });\n    let verification_flow = Signal::derive(move || {\n        if let Some(flow) = verfication_resp.get() {\n            Some(flow)\n        } else {\n            init_verification.get()\n        }\n    });\n    let body = create_rw_signal(HashMap::new());\n    view! {\n        <Suspense fallback=||view!{Loading Verification Details}>\n        <ErrorBoundary fallback=|errors|format!(\"ERRORS: {:?}\",errors.get_untracked()).into_view()>\n        {\n          move ||\n          verification_flow.get().map(|resp|{\n                match resp {\n                    Ok(Some(ViewableVerificationFlow(VerificationFlow{ui:box UiContainer{nodes,messages,action,..},..}))) => {\n                            let form_inner_html = nodes.into_iter().map(|node|kratos_html(node,body)).collect_view();\n                            body.update(|map|{_=map.insert(String::from(\"action\"),action);});\n                            view!{\n                                <form on:submit=move|e|{\n                                    e.prevent_default();\n                                    e.stop_propagation();\n                                    verify.dispatch(Verify{body:body.get_untracked()});\n                                }\n                                id=ids::VERIFICATION_FORM_ID\n                                >\n                                {form_inner_html}\n                                {messages.map(|messages|{\n                                    view!{\n                                        <For\n                                            each=move || messages.clone().into_iter()\n                                            key=|text| text.id\n                                            children=move |text: UiText| {\n                                              view! {\n                                                <p id=text.id>{text.text}</p>\n                                              }\n                                            }\n                                        />\n                                    }\n                                }).unwrap_or_default()}\n                                </form>\n                            }.into_view()\n                    },\n                    err => err.into_view(),\n                }\n            })\n          }\n        </ErrorBoundary>\n      </Suspense>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/database_calls.rs",
    "content": "use leptos::ServerFnError;\nuse serde::{Deserialize, Serialize};\nuse sqlx::{sqlite::SqlitePool, FromRow};\n\n// This will just map into ServerFnError when we call it in our serverfunctions with ? error handling\nuse sqlx::Error;\n\nuse crate::posts_page::PostData;\n#[tracing::instrument(err)]\npub async fn create_user(\n    pool: &SqlitePool,\n    identity_id: &String,\n    email: &String,\n) -> Result<(), Error> {\n    let id = uuid::Uuid::new_v4().to_string();\n    sqlx::query!(\n        \"INSERT INTO users (user_id,identity_id,email) VALUES (?,?,?)\",\n        id,\n        identity_id,\n        email\n    )\n    .execute(pool)\n    .await?;\n    Ok(())\n}\n\n/// Returns the POST ROW\n#[tracing::instrument(ret)]\npub async fn create_post(\n    pool: &SqlitePool,\n    user_id: &String,\n    content: &String,\n) -> Result<PostData, Error> {\n    let id = uuid::Uuid::new_v4().to_string();\n    sqlx::query_as!(\n        PostData,\n        \"INSERT INTO posts (post_id,user_id,content) VALUES (?,?,?) RETURNING *\",\n        id,\n        user_id,\n        content\n    )\n    .fetch_one(pool)\n    .await\n}\n#[tracing::instrument(ret)]\n\npub async fn edit_post(\n    pool: &SqlitePool,\n    post_id: &String,\n    content: &String,\n    user_id: &String,\n) -> Result<(), Error> {\n    sqlx::query!(\n        \"\n    UPDATE posts\n    SET content = ?\n    WHERE post_id = ?\n    AND EXISTS (\n        SELECT 1\n        FROM post_permissions\n        WHERE post_permissions.post_id = posts.post_id\n        AND post_permissions.user_id = ?\n        AND post_permissions.write = TRUE\n    )\",\n        content,\n        post_id,\n        user_id\n    )\n    .execute(pool)\n    .await?;\n    Ok(())\n}\n#[tracing::instrument(ret)]\n\npub async fn delete_post(pool: &SqlitePool, post_id: &String) -> Result<(), Error> {\n    sqlx::query!(\"DELETE FROM posts where post_id = ?\", post_id)\n        .execute(pool)\n        .await?;\n    Ok(())\n}\n#[tracing::instrument(ret)]\n\npub async fn list_users(pool: &SqlitePool) -> Result<Vec<UserRow>, Error> {\n    sqlx::query_as::<_, UserRow>(\"SELECT user_id, identity_id FROM users\")\n        .fetch_all(pool)\n        .await\n}\n#[tracing::instrument(ret)]\n\npub async fn read_user(pool: &SqlitePool, user_id: &String) -> Result<UserRow, Error> {\n    sqlx::query_as::<_, UserRow>(\"SELECT * FROM users WHERE user_id = ?\")\n        .bind(user_id)\n        .fetch_one(pool)\n        .await\n}\n#[tracing::instrument(ret)]\npub async fn read_user_by_identity_id(\n    pool: &SqlitePool,\n    identity_id: &String,\n) -> Result<UserRow, Error> {\n    sqlx::query_as::<_, UserRow>(\"SELECT * FROM users WHERE identity_id = ?\")\n        .bind(identity_id)\n        .fetch_one(pool)\n        .await\n}\n#[tracing::instrument(ret)]\n\npub async fn read_user_by_email(pool: &SqlitePool, email: &String) -> Result<UserRow, Error> {\n    sqlx::query_as::<_, UserRow>(\"SELECT * FROM users WHERE email = ?\")\n        .bind(email)\n        .fetch_one(pool)\n        .await\n}\n#[tracing::instrument(ret)]\n\npub async fn list_posts(pool: &SqlitePool, user_id: &String) -> Result<Vec<PostData>, Error> {\n    sqlx::query_as::<_, PostData>(\n        \"\n    SELECT posts.*\n    FROM posts\n    JOIN post_permissions ON posts.post_id = post_permissions.post_id \n        AND post_permissions.user_id = ?\n    WHERE post_permissions.read = TRUE\n    \",\n    )\n    .bind(user_id)\n    .fetch_all(pool)\n    .await\n}\n\n#[tracing::instrument(ret)]\n\npub async fn update_post_permission(\n    pool: &SqlitePool,\n    post_id: &String,\n    user_id: &String,\n    PostPermission {\n        read,\n        write,\n        delete,\n    }: PostPermission,\n) -> Result<(), Error> {\n    sqlx::query!(\n        \"\n        INSERT INTO post_permissions (post_id, user_id, read, write, `delete`)\n        VALUES (?, ?, ?, ?, ?)\n        ON CONFLICT (post_id, user_id) DO UPDATE SET\n        read = excluded.read,\n        write = excluded.write,\n        `delete` = excluded.`delete`;\n        \",\n        post_id,\n        user_id,\n        read,\n        write,\n        delete\n    )\n    .execute(pool)\n    .await?;\n\n    Ok(())\n}\n#[tracing::instrument(ret)]\npub async fn create_post_permissions(\n    pool: &SqlitePool,\n    post_id: &String,\n    user_id: &String,\n    PostPermission {\n        read,\n        write,\n        delete,\n    }: PostPermission,\n) -> Result<(), Error> {\n    sqlx::query!(\n        \"INSERT INTO post_permissions (post_id,user_id,read,write,`delete`) VALUES (?,?,?,?,?)\",\n        post_id,\n        user_id,\n        read,\n        write,\n        delete\n    )\n    .execute(pool)\n    .await?;\n\n    Ok(())\n}\n\n#[derive(Debug, PartialEq, Clone, Copy, Default)]\npub struct PostPermission {\n    pub read: bool,\n    pub write: bool,\n    pub delete: bool,\n}\n\nimpl PostPermission {\n    #[tracing::instrument(ret)]\n    pub async fn from_db_call(\n        pool: &SqlitePool,\n        user_id: &String,\n        post_id: &String,\n    ) -> Result<Self, Error> {\n        if let Ok(row) = sqlx::query_as!(\n            PostPermissionRow,\n            \"SELECT * FROM post_permissions WHERE post_id = ? AND user_id = ?\",\n            post_id,\n            user_id\n        )\n        .fetch_one(pool)\n        .await\n        {\n            Ok(Self::from(row))\n        } else {\n            Ok(Self::default())\n        }\n    }\n\n    pub fn new_full() -> Self {\n        Self {\n            read: true,\n            write: true,\n            delete: true,\n        }\n    }\n\n    pub fn is_full(&self) -> Result<(), ServerFnError> {\n        if &Self::new_full() != self {\n            Err(ServerFnError::new(\"Unauthorized, not full permissions. \"))\n        } else {\n            Ok(())\n        }\n    }\n    pub fn can_read(&self) -> Result<(), ServerFnError> {\n        if !self.read {\n            Err(ServerFnError::new(\"Unauthorized to read\"))\n        } else {\n            Ok(())\n        }\n    }\n    pub fn can_write(&self) -> Result<(), ServerFnError> {\n        if !self.write {\n            Err(ServerFnError::new(\"Unauthorized to write\"))\n        } else {\n            Ok(())\n        }\n    }\n    pub fn can_delete(&self) -> Result<(), ServerFnError> {\n        if !self.delete {\n            Err(ServerFnError::new(\"Unauthorized to delete\"))\n        } else {\n            Ok(())\n        }\n    }\n}\n\nimpl From<PostPermissionRow> for PostPermission {\n    fn from(value: PostPermissionRow) -> Self {\n        Self {\n            read: value.read,\n            write: value.write,\n            delete: value.delete,\n        }\n    }\n}\n\n#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, FromRow)]\npub struct PostPermissionRow {\n    pub post_id: String,\n    pub user_id: String,\n    pub read: bool,\n    pub write: bool,\n    pub delete: bool,\n}\n\n#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, FromRow)]\npub struct UserRow {\n    pub user_id: String,\n    pub identity_id: String,\n    pub email: String,\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/error_template.rs",
    "content": "use cfg_if::cfg_if;\nuse http::status::StatusCode;\nuse leptos::*;\n#[cfg(feature = \"ssr\")]\nuse leptos_axum::ResponseOptions;\nuse thiserror::Error;\n\n#[derive(Clone, Debug, Error)]\npub enum AppError {\n    #[error(\"Not Found\")]\n    NotFound,\n}\n\nimpl AppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            AppError::NotFound => StatusCode::NOT_FOUND,\n        }\n    }\n}\n\n// A basic function to display errors served by the error boundaries.\n// Feel free to do more complicated things here than just displaying the error.\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional)] errors: Option<RwSignal<Errors>>,\n) -> impl IntoView {\n    let basic_err_msg = if let Some(errors_sig) = errors {\n        format!(\"{:#?}\", errors_sig.get_untracked())\n    } else {\n        \"No errors produced by signal\".to_string()\n    };\n    let errors = match outside_errors {\n        Some(e) => create_rw_signal(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n    // Get Errors from Signal\n    let errors = errors.get_untracked();\n\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors: Vec<AppError> = errors\n        .into_iter()\n        .filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())\n        .collect();\n    println!(\"Errors: {errors:#?}\");\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    cfg_if! { if #[cfg(feature=\"ssr\")] {\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            if let Some(error) = errors.get(0) {\n                response.set_status(error.status_code());\n            } else {\n                response.set_status(StatusCode::INTERNAL_SERVER_ERROR)\n            }\n        }\n    }}\n\n    view! {\n        <h1>{if errors.len() > 1 { \"Errors\" } else { \"Error\" }}</h1>\n        <For\n            // a function that returns the items we're iterating over; a signal is fine\n            each=move || { errors.clone().into_iter().enumerate() }\n            // a unique key for each item as a reference\n            key=|(index, _error)| *index\n            // renders each item to a view\n            children=move |error| {\n                let error_string = error.1.to_string();\n                let error_code = error.1.status_code();\n                view! {\n                    <h2>{error_code.to_string()}</h2>\n                    <p >\"Error: \" {error_string}</p>\n                }\n            }\n        />\n        <p id=ids::ERROR_ERROR_ID>{basic_err_msg}</p>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/lib.rs",
    "content": "#![feature(box_patterns)]\n\nuse crate::error_template::{AppError, ErrorTemplate};\n\nuse leptos::*;\nuse leptos_meta::*;\nuse leptos_router::*;\n\npub mod auth;\n#[cfg(feature = \"ssr\")]\npub mod database_calls;\npub mod error_template;\nuse auth::*;\npub mod posts;\npub use posts::*;\n#[derive(Clone, Copy, PartialEq, Debug, Default)]\npub struct IsLoggedIn(RwSignal<bool>);\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/ory-auth-example.css\"/>\n\n        // sets the document title\n        <Title text=\"Welcome to Leptos\"/>\n\n        // content for this welcome page\n        <Router fallback=|| {\n            let mut outside_errors = Errors::default();\n            outside_errors.insert_with_default_key(AppError::NotFound);\n            view! { <ErrorTemplate outside_errors/> }.into_view()\n        }>\n            <main>\n                <Routes>\n                    <Route path=\"\" view=HomePage/>\n                    <Route path=ids::REGISTER_ROUTE view=RegistrationPage/>\n                    <Route path=ids::VERIFICATION_ROUTE view=VerificationPage/>\n                    <Route path=ids::LOGIN_ROUTE view=LoginPage/>\n                    <Route path=ids::KRATOS_ERROR_ROUTE view=KratosErrorPage/>\n                    <Route path=ids::RECOVERY_ROUTE view=RecoveryPage/>\n                    <Route path=ids::SETTINGS_ROUTE view=SettingsPage/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n/// Renders the home page of your application.\n#[component]\nfn HomePage() -> impl IntoView {\n    let clear_cookies = Action::<ClearCookies, _>::server();\n    view! {\n        <h1>\"Welcome to Leptos!\"</h1>\n        <div>\n            <a href=ids::REGISTER_ROUTE id=ids::REGISTER_BUTTON_ID>Register</a>\n        </div>\n        <div>\n            <a href=ids::LOGIN_ROUTE id=ids::LOGIN_BUTTON_ID>\"Login\"</a>\n        </div>\n        <div>\n            <LogoutButton/>\n        </div>\n        <div>\n            <button id=ids::CLEAR_COOKIES_BUTTON_ID\n            on:click=move|_|clear_cookies.dispatch(ClearCookies{})>Clear cookies </button>\n        </div>\n        <div>\n            <HasSession/>\n        </div>\n        <div>\n            <PostPage/>\n        </div>\n        <div>\n            <a href=ids::RECOVERY_ROUTE id=ids::RECOVER_EMAIL_BUTTON_ID>\"Recovery Email\"</a>\n        </div>\n        <div>\n            <a href=ids::SETTINGS_ROUTE>\"Settings\"</a>\n        </div>\n    }\n}\n\n#[cfg(feature = \"ssr\")]\npub async fn clear_cookies_inner() -> Result<(), ServerFnError> {\n    let opts = expect_context::<leptos_axum::ResponseOptions>();\n\n    let cookie_jar = leptos_axum::extract::<axum_extra::extract::CookieJar>().await?;\n    for cookie in cookie_jar.iter() {\n        let mut cookie = cookie.clone();\n        cookie.set_expires(\n            time::OffsetDateTime::now_utc()\n                .checked_sub(time::Duration::hours(24 * 356 * 10))\n                .unwrap(),\n        );\n        cookie.set_max_age(time::Duration::seconds(0));\n        cookie.set_path(\"/\");\n        // To clear an http only cookie, one must set an http only cookie.\n        cookie.set_http_only(true);\n        cookie.set_secure(true);\n        let cookie = cookie.to_string();\n        opts.append_header(\n            axum::http::HeaderName::from_static(\"set-cookie\"),\n            axum::http::HeaderValue::from_str(&cookie)?,\n        );\n    }\n    Ok(())\n}\n\n#[tracing::instrument(ret)]\n#[server]\npub async fn clear_cookies() -> Result<(), ServerFnError> {\n    clear_cookies_inner().await?;\n    Ok(())\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/posts/create_posts.rs",
    "content": "use super::*;\n// An user can post a post. Technically all server functions are POST, so this is a Post Post Post.\n#[tracing::instrument(ret)]\n#[server]\npub async fn post_post(content: String) -> Result<(), ServerFnError> {\n    use crate::database_calls::{create_post, create_post_permissions, PostPermission};\n\n    let pool = leptos_axum::extract::<axum::Extension<sqlx::SqlitePool>>()\n        .await?\n        .0;\n    let user_id = leptos_axum::extract::<crate::auth::extractors::ExtractUserRow>()\n        .await?\n        .0\n        .user_id;\n    let PostData { post_id, .. } = create_post(&pool, &user_id, &content).await?;\n    create_post_permissions(&pool, &post_id, &user_id, PostPermission::new_full()).await?;\n    Ok(())\n}\n\n#[component]\npub fn CreatePost() -> impl IntoView {\n    let post_post = Action::<PostPost, _>::server();\n    view! {\n        <ActionForm action=post_post>\n            <textarea type=\"text\" name=\"content\" id=ids::POST_POST_TEXT_AREA_ID/>\n            <input type=\"submit\" value=\"Post Post\" id=ids::POST_POST_SUBMIT_ID/>\n        </ActionForm>\n        <Suspense fallback=move||view!{}>\n        <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n            { move || post_post.value().get()}\n        </ErrorBoundary>\n        </Suspense>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/posts/mod.rs",
    "content": "use super::*;\nmod post;\nuse post::Post;\npub mod posts_page;\npub use posts_page::PostPage;\nmod create_posts;\nuse crate::posts_page::PostData;\nuse create_posts::CreatePost;\n"
  },
  {
    "path": "projects/ory-kratos/app/src/posts/post.rs",
    "content": "use self::posts_page::PostData;\n\nuse super::*;\n\n// This is the post, contains all other functionality.\n#[component]\npub fn Post(post: PostData) -> impl IntoView {\n    let PostData {\n        post_id, content, ..\n    } = post;\n    view! {\n        <div>{content}</div>\n        <AddEditor post_id=post_id.clone()/>\n        <EditPost post_id=post_id.clone()/>\n    }\n}\n\n// Only the owner can add an an editor.\n#[tracing::instrument(ret)]\n#[server]\npub async fn server_add_editor(post_id: String, email: String) -> Result<(), ServerFnError> {\n    use crate::database_calls::{read_user_by_email, update_post_permission, PostPermission};\n\n    let pool: sqlx::Pool<sqlx::Sqlite> =\n        leptos_axum::extract::<axum::Extension<sqlx::SqlitePool>>()\n            .await?\n            .0;\n\n    let user_id = leptos_axum::extract::<crate::auth::extractors::ExtractUserRow>()\n        .await?\n        .0\n        .user_id;\n\n    let caller_permissions = PostPermission::from_db_call(&pool, &user_id, &post_id).await?;\n\n    caller_permissions.is_full()?;\n\n    // get other id\n    let user_id = read_user_by_email(&pool, &email).await?.user_id;\n\n    // make an idempotent update to the other users permissions;\n    let mut permissions = PostPermission::from_db_call(&pool, &post_id, &user_id).await?;\n    permissions.write = true;\n    permissions.read = true;\n\n    update_post_permission(&pool, &post_id, &user_id, permissions).await?;\n\n    Ok(())\n}\n\n#[component]\npub fn AddEditor(post_id: String) -> impl IntoView {\n    let add_editor = Action::<ServerAddEditor, _>::server();\n    view! {\n        <ActionForm action=add_editor>\n            <label value=\"Add Editor Email\">\n            <input type=\"text\"  name=\"email\" id=ids::POST_ADD_EDITOR_INPUT_ID/>\n            <input type=\"hidden\" name=\"post_id\" value=post_id/>\n            </label>\n            <input type=\"submit\" id=ids::POST_ADD_EDITOR_SUBMIT_ID/>\n        </ActionForm>\n        <Suspense fallback=||view!{}>\n            <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n            { move || add_editor.value().get()}\n            </ErrorBoundary>\n        </Suspense>\n    }\n}\n\n// Only the owner and editors can edit a post.\n#[tracing::instrument(ret)]\n#[server]\npub async fn server_edit_post(post_id: String, content: String) -> Result<(), ServerFnError> {\n    let pool: sqlx::Pool<sqlx::Sqlite> =\n        leptos_axum::extract::<axum::Extension<sqlx::SqlitePool>>()\n            .await?\n            .0;\n\n    let user_id = leptos_axum::extract::<crate::auth::extractors::ExtractUserRow>()\n        .await?\n        .0\n        .user_id;\n\n    crate::database_calls::edit_post(&pool, &post_id, &content, &user_id).await?;\n\n    Ok(())\n}\n\n#[component]\npub fn EditPost(post_id: String) -> impl IntoView {\n    let edit_post = Action::<ServerEditPost, _>::server();\n    view! {\n        <ActionForm action=edit_post>\n            <label value=\"New Content:\">\n            <textarea name=\"content\" id=ids::POST_EDIT_TEXT_AREA_ID/>\n            <input type=\"hidden\" name=\"post_id\" value=post_id/>\n            </label>\n            <input type=\"submit\" id=ids::POST_EDIT_SUBMIT_ID/>\n        </ActionForm>\n        <Suspense fallback=||view!{}>\n            <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n                { move || edit_post.value().get()}\n            </ErrorBoundary>\n        </Suspense>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/app/src/posts/posts_page.rs",
    "content": "use serde::{Deserialize, Serialize};\n\nuse super::*;\n\n#[derive(Serialize, Deserialize, Debug, Clone)]\n#[cfg_attr(feature = \"ssr\", derive(sqlx::FromRow))]\npub struct PostData {\n    pub post_id: String,\n    pub user_id: String,\n    pub content: String,\n}\nimpl IntoView for PostData {\n    fn into_view(self) -> View {\n        view! {<Post post=self/>}\n    }\n}\n\n#[tracing::instrument(ret)]\n#[server]\npub async fn get_post_list() -> Result<Vec<PostData>, ServerFnError> {\n    use crate::database_calls::list_posts;\n\n    let pool = leptos_axum::extract::<axum::Extension<sqlx::SqlitePool>>()\n        .await?\n        .0;\n\n    let user_id = leptos_axum::extract::<crate::auth::extractors::ExtractUserRow>()\n        .await?\n        .0\n        .user_id;\n\n    Ok(list_posts(&pool, &user_id).await?)\n}\n\n#[component]\npub fn PostPage() -> impl IntoView {\n    view! {\n        <PostsList/>\n        <CreatePost/>\n    }\n}\n\n#[component]\npub fn PostsList() -> impl IntoView {\n    let list_posts = Action::<GetPostList, _>::server();\n\n    view! {\n        <button on:click=move|_|list_posts.dispatch(GetPostList{}) id=ids::POST_SHOW_LIST_BUTTON_ID>Show List</button>\n        <Suspense fallback=||\"Post list loading...\".into_view()>\n        <ErrorBoundary fallback=|errors|view!{<ErrorTemplate errors/>}>\n            {\n                move || list_posts.value().get().map(|resp|\n                    match resp {\n                        Ok(list) => view!{\n                            <For\n                            each=move || list.clone()\n                            key=|_| uuid::Uuid::new_v4()\n                            children=move |post: PostData| {\n                              post.into_view()\n                            }\n                          />\n                        }.into_view(),\n                        err => err.into_view()\n                    })\n            }\n        </ErrorBoundary>\n        </Suspense>\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/docker-compose.yml",
    "content": "version: '3.7'\nservices:\n  mailcrab:\n    image: marlonb/mailcrab:latest\n    ports:\n      - \"1080:1080\"\n      - \"1025:1025\"\n    networks:\n      - mynetwork\n\n  kratos:\n    image: oryd/kratos:v1.1.0\n    command: serve --dev --config /etc/config/kratos/kratos.yaml --watch-courier\n    volumes:\n      - \"./kratos:/etc/config/kratos\"\n    ports:\n      - \"4433:4433\"\n      - \"4434:4434\"\n    networks:\n      - mynetwork\n  \nnetworks:\n  mynetwork:\n    driver: bridge\n"
  },
  {
    "path": "projects/ory-kratos/e2e/Cargo.toml",
    "content": "[package]\nname = \"e2e\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dev-dependencies]\nanyhow = \"1.0\"\nasync-trait = \"0.1.81\"\ncucumber = { version = \"0.21.1\", features = [\"tracing\", \"macros\"] }\npretty_assertions = \"1.4\"\nserde_json = \"1.0\"\ntokio = { version = \"1.39\", features = [\"macros\", \"rt-multi-thread\", \"time\"] }\nurl = \"2.5\"\nreqwest = \"0.12.5\"\ntracing = \"0.1.40\"\nchromiumoxide = { version = \"0.6.0\", default-features = false, features = [\n  \"tokio-runtime\",\n] }\nids.workspace = true\nfake = \"2.9\"\ntokio-tungstenite = \"0.23.1\"\nfutures-util = \"0.3.30\"\nuuid = { version = \"1.10\", features = [\"serde\"] }\nfutures = \"0.3.30\"\n\n[[test]]\nname = \"app_suite\"\nharness = false    # Allow Cucumber to print output instead of libtest\n\n[features]\n#vscode thing to get autocomplete\nssr = []\n\n[dependencies]\nregex = \"1.10.6\"\nserde.workspace = true\n"
  },
  {
    "path": "projects/ory-kratos/e2e/features/0_test.feature",
    "content": "@test\nFeature: Test\n\n    Scenario:pass_test_pass\n        Given I pass\n  "
  },
  {
    "path": "projects/ory-kratos/e2e/features/1_register.feature",
    "content": "@register\nFeature: Register\n\n    As a user\n    I want to register\n    So that I can login and POST CONTENT.\n    \n    Scenario:register\n        Given I am on the homepage\n        And I click register\n        And I am on the registration page\n        And I see the registration form\n        When I enter valid credentials\n        And I check my email for the verification link and code\n        And I copy the code onto the verification link page\n        Then I am on the homepage"
  },
  {
    "path": "projects/ory-kratos/e2e/features/2_login.feature",
    "content": "@login\nFeature: Login\n\n    As a user\n    I want to log in\n    So that I can get access to authorized content.\n\n\n    Scenario:login\n        Given I am on the registration page\n        And I see the registration form\n        And I enter valid credentials\n        And I check my email for the verification link and code\n        And I copy the code onto the verification link page\n        When I click login\n        And I see the login form\n        And I re-enter valid credentials\n        Then I am on the homepage\n        And I am logged in"
  },
  {
    "path": "projects/ory-kratos/e2e/features/3_logout.feature",
    "content": "@logout\nFeature: Logout\n\n    As a user\n    I want to log out after registering\n    So that I can test to see if login after registering works.\n\n    \n    Scenario:logout\n        Given I am on the registration page\n        And I see the registration form\n        And I enter valid credentials\n        And I check my email for the verification link and code\n        And I copy the code onto the verification link page\n        And I click login\n        And I re-enter valid credentials\n        When I click logout\n        Then I am logged out"
  },
  {
    "path": "projects/ory-kratos/e2e/features/4_recovery.feature",
    "content": "@recovery\nFeature: Recovery\n\n    As a user\n    I want to recovery my email\n    So that I can test to see if recovery after registering works.\n\n    \n    Scenario:recovery\n        # lol and and and and and and...\n        Given I am on the registration page\n        And I see the registration form\n        And I enter valid credentials\n        And I check my email for the verification link and code\n        And I copy the code onto the verification link page\n        And I click login\n        And I re-enter valid credentials\n        And I click logout\n        And I click recover email\n        And I submit valid recovery email\n        And I check my email for recovery link and code\n        When I copy the code onto the recovery link page\n        Then I am on the settings page"
  },
  {
    "path": "projects/ory-kratos/e2e/features/5_settings.feature",
    "content": "@settings\nFeature: Settings\n\n    As a user\n    I want to use the settings page\n    So that I can update my password.\n\n    \n    Scenario:recovery_settings\n        # lol and and and and and and...\n        Given I am on the registration page\n        And I see the registration form\n        And I enter valid credentials\n        And I check my email for the verification link and code\n        And I copy the code onto the verification link page\n        And I click login\n        And I re-enter valid credentials\n        And I click logout\n        And I click recover email\n        And I submit valid recovery email\n        And I check my email for recovery link and code\n        When I copy the code onto the recovery link page\n        And I enter recovery credentials\n        Then I don't see error"
  },
  {
    "path": "projects/ory-kratos/e2e/features/6_add_post.feature",
    "content": "@add-post\nFeature: Add-post\n\n    As a user\n    I want to add a post\n    So that I can share my EXAMPLE DATA with the world!\n\n    Background:\n        Given I am on the homepage\n        And I clear cookies\n    \n  \n\n    Scenario: add_post_logged_in\n        Given I am on the registration page\n        And I see the registration form\n        And I enter valid credentials\n        And I check my email for the verification link and code\n        And I copy the code onto the verification link page\n        And I click login\n        And I re-enter valid credentials\n        When I add example post\n        And I click show post list\n        Then I see example content posted\n\n      Scenario: add_post_logged_out\n        Given I am logged out\n        When I add example post\n        Then I see error"
  },
  {
    "path": "projects/ory-kratos/e2e/features/7_edit_post.feature",
    "content": "@edit-post\nFeature: Edit-Post\n\n    As a user\n    I want to add an editor to my post\n    So that my bestie can improve my EXAMPLE CONTENT.\n\n    Background:\n        Given I am on the registration page\n        And I see the registration form\n        And I enter valid other credentials\n        And I check my other email for the verification link and code\n        And I copy the code onto the verification link page\n        And I am on the registration page\n        And I see the registration form\n        And I enter valid credentials\n        And I check my email for the verification link and code\n        And I copy the code onto the verification link page\n\n    Scenario: add_editor_as_owner_and_edit_post\n        Given I am on the homepage\n        And I click login\n        And I re-enter valid credentials\n        And I add example post\n        And I click show post list\n        And I see example content posted\n        When I add other email as editor\n        And I logout\n        And I click login\n        And I re-enter other valid credentials\n        And I click show post list\n        And I see example content posted\n        And I edit example post\n        And I click show post list\n        Then I see my new content posted\n        And I don't see old content\n\n    Scenario: add_editor_as_other\n        Given I am on the homepage\n        And I click login\n        And I re-enter valid credentials\n        And I add example post\n        And I click show post list\n        And I see example content posted\n        When I add other email as editor\n        And I logout\n        And I click login\n        And I re-enter other valid credentials\n        And I click show post list\n        And I see example content posted\n        And I add other email as editor\n        Then I see error\n"
  },
  {
    "path": "projects/ory-kratos/e2e/tests/app_suite.rs",
    "content": "#![feature(never_type)]\nmod fixtures;\n\nuse anyhow::anyhow;\nuse anyhow::Result;\nuse chromiumoxide::cdp::browser_protocol::log::EventEntryAdded;\nuse chromiumoxide::cdp::js_protocol::runtime::EventConsoleApiCalled;\nuse chromiumoxide::{\n    browser::{Browser, BrowserConfig},\n    cdp::browser_protocol::{\n        network::{EventRequestWillBeSent, EventResponseReceived, Request, Response},\n        page::NavigateParams,\n    },\n    element::Element,\n    page::ScreenshotParams,\n    Page,\n};\nuse cucumber::World;\nuse futures::channel::mpsc::Sender;\nuse futures_util::stream::StreamExt;\nuse std::sync::LazyLock;\nuse serde::{Deserialize, Serialize};\nuse std::{collections::HashMap, sync::Arc, time::Duration};\nuse tokio::sync::RwLock;\nuse tokio_tungstenite::connect_async;\nuse uuid::Uuid;\nstatic EMAIL_ID_MAP: LazyLock<RwLock<HashMap<String, String>>> =\n    LazyLock::new(|| RwLock::new(HashMap::new()));\n\n#[derive(Clone, Debug, PartialEq)]\npub struct RequestPair {\n    req: Option<Request>,\n    redirect_resp: Option<Response>,\n    resp: Option<Response>,\n    cookies_before_request: String,\n    cookies_after_response: String,\n    ts: std::time::Instant,\n}\n\n/*\nlet screenshot =   world\n  .page\n  .screenshot(\n      ScreenshotParams::builder()\n          .capture_beyond_viewport(true)\n          .full_page(true)\n          .build(),\n  )\n  .await\n  .unwrap();\n  world.screenshots.push(screenshot);\n   */\n#[derive(Clone, Debug)]\npub enum CookieEnum {\n    BeforeReq(String),\n    AfterResp(String),\n}\nimpl RequestPair {\n    pub fn to_string(&self) -> String {\n        let (top_req, req_headers) = if let Some(req) = &self.req {\n            (\n                format!(\"{} : {} \\n\", req.method, req.url,),\n                format!(\"{} :\\n{:#?} \\n\", req.url, req.headers),\n            )\n        } else {\n            (\"NO REQ\".to_string(), \"NO REQ\".to_string())\n        };\n        let (top_redirect_resp, _redirect_resp_headers) = if let Some(resp) = &self.redirect_resp {\n            (\n                format!(\"{} : {}\", resp.status, resp.url),\n                format!(\"{} :\\n {:#?}\", resp.url, resp.headers),\n            )\n        } else {\n            (\"\".to_string(), \"\".to_string())\n        };\n        let (top_resp, resp_headers) = if let Some(resp) = &self.resp {\n            (\n                format!(\"{} : {}\", resp.status, resp.url),\n                format!(\"{} :\\n {:#?}\", resp.url, resp.headers),\n            )\n        } else {\n            (\"NO RESP\".to_string(), \"NO RESP\".to_string())\n        };\n\n        format!(\n            \"REQ: {}\\n RESP: {}\\n \\n REDIRECT {} \\n REQ_HEADERS: {} \\n REQ_COOKIES: \\n{}\\n RESP_HEADERS:{} \\n RESP_COOKIES: \\n{}\\n \",\n            top_req, top_resp,top_redirect_resp,  req_headers,  self.cookies_before_request,resp_headers,self.cookies_after_response\n        )\n    }\n}\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    // create a thread and store a\n    //  tokio-tungstenite client that connectsto http://127.0.0.1:1080/ws\n    // and then stores the recieved messages in a std::sync::LazyLock<RwLock<Vec<MailCrabMsg>>>\n    // or a custom struct that matches the body or has specific impls for verify codes, links etc.\n    let _ = tokio::spawn(async move {\n        let (mut socket, _) = connect_async(\n            url::Url::parse(\"ws://127.0.0.1:1080/ws\").expect(\"Can't connect to case count URL\"),\n        )\n        .await\n        .unwrap();\n        while let Some(msg) = socket.next().await {\n            if let Ok(tokio_tungstenite::tungstenite::Message::Text(text)) = msg {\n                let Email { id, to } = serde_json::from_str::<Email>(&text).unwrap();\n                let email = to[0].email.clone();\n                EMAIL_ID_MAP.write().await.insert(email, id.to_string());\n            }\n        }\n    });\n\n    AppWorld::cucumber()\n        .init_tracing()\n        .fail_on_skipped()\n        .max_concurrent_scenarios(1)\n        .fail_fast()\n        .before(|_feature, _rule, scenario, world| {\n            Box::pin(async move {\n                let screenshot_directory_name = format!(\"./screenshots/{}\", scenario.name);\n                if let Ok(sc_dir) = std::fs::read_dir(&screenshot_directory_name) {\n                    for file in sc_dir {\n                        if let Ok(file) = file {\n                            std::fs::remove_file(file.path()).unwrap();\n                        }\n                    }\n                } else {\n                    std::fs::create_dir(&screenshot_directory_name).unwrap();\n                }\n                // take the page from world\n                // add network event listener, tracking requests and pairing them with responses\n                // store them somewhere inside of the world.\n                let page = world.page.clone();\n                let mut req_events = page\n                    .event_listener::<EventRequestWillBeSent>()\n                    .await\n                    .unwrap();\n                let mut resp_events = page\n                    .event_listener::<EventResponseReceived>()\n                    .await\n                    .unwrap();\n                world.page.enable_log().await.unwrap();\n                // get log events generated by the browser\n                let mut log_events = page.event_listener::<EventEntryAdded>().await.unwrap();\n                // get log events generated by leptos or other console.log() calls..\n                let mut runtime_events = page\n                    .event_listener::<EventConsoleApiCalled>()\n                    .await\n                    .unwrap();\n                let console_logs = world.console_logs.clone();\n                let console_logs_2 = world.console_logs.clone();\n\n                tokio::task::spawn(async move {\n                    while let Some(event) = log_events.next().await {\n                        if let Some(EventEntryAdded { entry }) =\n                        Arc::<EventEntryAdded>::into_inner(event) {\n                            console_logs.write().await.push(format!(\" {entry:#?} \"));\n                        } else {\n                            tracing::error!(\"tried to into inner but none\")\n                        }\n                    }\n                });\n\n                tokio::task::spawn(async move {\n                    while let Some(event) = runtime_events.next().await {\n                        if let Some(event) =Arc::<EventConsoleApiCalled>::into_inner(event) {\n                            console_logs_2\n                            .write()\n                            .await\n                            .push(format!(\" CONSOLE_LOG: {:#?}\", event.args));\n                        } else {\n                            tracing::error!(\"tried to into inner but none\")\n                        }\n\n                    }\n                });\n\n                let (tx, mut rx) = futures::channel::mpsc::channel::<Option<CookieEnum>>(1000);\n                let mut tx_c = tx.clone();\n                let mut tx_c_2 = tx.clone();\n\n                world.cookie_sender = Some(tx);\n                let req_resp = world.req_resp.clone();\n                // Ideally you'd send the message for the Page to get the cookies from inside of the event stream loop,\n                // but for some reason that doesn't always work (but sometimes it does),\n                // but putting it in it's own thread makes it always work. Not sure why at the moment... ,\n                // something about async, about senders, about trying to close the browser but keeping senders around.\n                // we need to close the loop and drop the task to close the browser (I think)...\n                tokio::task::spawn(async move {\n                    while let Some(some_request_id) = rx.next().await {\n                        if let Some(cookie_enum) = some_request_id {\n                            match cookie_enum {\n                                CookieEnum::BeforeReq(req_id) => {\n                                    let cookies = page\n                                        .get_cookies()\n                                        .await\n                                        .unwrap_or_default()\n                                        .iter()\n                                        .map(|cookie| {\n                                            format!(\"name={}\\n value={}\", cookie.name, cookie.value)\n                                        })\n                                        .collect::<Vec<String>>()\n                                        .join(\"\\n\");\n                                    if let Some(thing) = req_resp\n                                    .write()\n                                    .await\n                                    .get_mut(&req_id) {\n                                        thing.cookies_before_request = cookies;\n\n                                    }\n\n                                }\n                                CookieEnum::AfterResp(req_id) => {\n                                    let cookies = page\n                                        .get_cookies()\n                                        .await\n                                        .unwrap_or_default()\n                                        .iter()\n                                        .map(|cookie| {\n                                            format!(\"name={}\\n value={}\", cookie.name, cookie.value)\n                                        })\n                                        .collect::<Vec<String>>()\n                                        .join(\"\\n\");\n                                    if let Some(thing) = req_resp\n                                    .write()\n                                    .await\n                                    .get_mut(&req_id) {\n                                        thing.cookies_after_response = cookies;\n                                    }\n                                   }\n                            }\n                        } else {\n                            break;\n                        }\n                    }\n                });\n\n                let req_resp = world.req_resp.clone();\n                tokio::task::spawn(async move {\n                    while let Some(event) = req_events.next().await {\n                        if let Some(event) = Arc::<EventRequestWillBeSent>::into_inner(event) {\n                            if event.request.url.contains(\"/pkg/\") {\n                                continue;\n                            }\n                            let req_id = event.request_id.inner().clone();\n                            req_resp.write().await.insert(\n                                req_id.clone(),\n                                RequestPair {\n                                    req: Some(event.request),\n                                    redirect_resp: event.redirect_response,\n                                    resp: None,\n                                    cookies_before_request: \"\".to_string(),\n                                    cookies_after_response: \"\".to_string(),\n                                    ts: std::time::Instant::now(),\n                                },\n                            );\n                            if let Err(msg) = tx_c.try_send(Some(CookieEnum::BeforeReq(req_id.clone()))) {\n                                tracing::error!(\" oopsies on the {msg:#?}\");\n                            }\n                        } else {\n                            tracing::error!(\"into inner err\")\n                        }\n                    }\n                });\n\n                let req_resp = world.req_resp.clone();\n                tokio::task::spawn(async move {\n                    while let Some(event) = resp_events.next().await {\n                        if let Some(event) = Arc::<EventResponseReceived>::into_inner(event){\n                            if event.response.url.contains(\"/pkg/\") {\n                                continue;\n                            }\n                            let req_id = event.request_id.inner().clone();\n                            if let Err(msg) = tx_c_2\n                                .try_send(Some(CookieEnum::AfterResp(req_id.clone()))) {\n                                tracing::error!(\"err sending {msg:#?}\");\n                            }\n                            if let Some(request_pair) = req_resp.write().await.get_mut(&req_id) {\n                                request_pair.resp = Some(event.response);\n                            } else {\n                                req_resp.write().await.insert(\n                                    req_id.clone(),\n                                    RequestPair {\n                                        req: None,\n                                        redirect_resp: None,\n                                        resp: Some(event.response),\n                                        cookies_before_request: \"No cookie?\".to_string(),\n                                        cookies_after_response: \"No cookie?\".to_string(),\n                                        ts: std::time::Instant::now(),\n                                    },\n                                );\n                            }\n                        } else {\n                            tracing::error!(\" uhh err here\")\n                        }\n\n\n                    }\n                });\n                // We don't need to join on our join handles, they will run detached and clean up whenever.\n            })\n        })\n        .after(|_feature, _rule, scenario, ev, world| {\n            Box::pin(async move {\n                let screenshot_directory_name = format!(\"./screenshots/{}\", scenario.name);\n\n                let world = world.unwrap();\n                // screenshot the last step\n                if let Ok(screenshot) = world\n                .page\n                .screenshot(\n                    ScreenshotParams::builder()\n                        .capture_beyond_viewport(true)\n                        .full_page(true)\n                        .build(),\n                )\n                .await {\n                    world.screenshots.push(screenshot);\n                }\n\n                if let cucumber::event::ScenarioFinished::StepFailed(_, _, _) = ev {\n                    // close the cookie task.\n                    if world\n                        .cookie_sender\n                        .as_mut()\n                        .unwrap()\n                        .try_send(None).is_err() {\n                            tracing::error!(\"can't close cookie sender\");\n                        }\n                    // print any applicable screenshots (just the last one of the failed step if there was none taken during the scenario)\n                    for (i, screenshot) in world.screenshots.iter().enumerate() {\n                        // i.e ./screenshots/login/1.png\n                        _ =std::fs::write(\n                            screenshot_directory_name.clone()\n                                + \"/\"\n                                + i.to_string().as_str()\n                                + \".png\",\n                            screenshot,\n                        );\n                    }\n                    // print network\n                    let mut network_output = world\n                        .req_resp\n                        .read()\n                        .await\n                        .values()\n                        .map(|val| val.clone())\n                        .collect::<Vec<RequestPair>>();\n\n                    network_output.sort_by(|a, b| a.ts.cmp(&b.ts));\n\n                    let network_output = network_output\n                        .into_iter()\n                        .map(|val| val.to_string())\n                        .collect::<Vec<String>>()\n                        .join(\"\\n\");\n\n                    _ = std::fs::write(\"./network_output\", network_output.as_bytes());\n\n                    let console_logs = world.console_logs.read().await.join(\"\\n\");\n\n                    _ =std::fs::write(\"./console_logs\", console_logs.as_bytes());\n\n                    // print html\n                    if let Ok(html) = world.page.content().await {\n                        _ = std::fs::write(\"./html\", html.as_bytes());\n                    }\n                }\n                if let Err(err) = world.browser.close().await {\n                    tracing::error!(\"{err:#?}\");\n                }\n                if let Err(err) =  world.browser.wait().await {\n                    tracing::error!(\"{err:#?}\");\n                }\n            })\n        })\n        .run_and_exit(\"./features\")\n        .await;\n    Ok(())\n}\n\n#[tracing::instrument]\nasync fn build_browser() -> Result<Browser, Box<dyn std::error::Error>> {\n    let (browser, mut handler) = Browser::launch(\n        BrowserConfig::builder()\n            //.enable_request_intercept()\n            .disable_cache()\n            .request_timeout(Duration::from_secs(1))\n            //.with_head()\n            //.arg(\"--remote-debugging-port=9222\")\n            .build()?,\n    )\n    .await?;\n\n    tokio::task::spawn(async move {\n        while let Some(h) = handler.next().await {\n            if h.is_err() {\n                tracing::info!(\"{h:?}\");\n                break;\n            }\n        }\n    });\n\n    Ok(browser)\n}\n\npub const HOST: &str = \"https://127.0.0.1:3000\";\n\n#[derive(World)]\n#[world(init = Self::new)]\npub struct AppWorld {\n    pub browser: Browser,\n    pub page: Page,\n    pub req_resp: Arc<RwLock<HashMap<String, RequestPair>>>,\n    pub clipboard: HashMap<&'static str, String>,\n    pub cookie_sender: Option<Sender<Option<CookieEnum>>>,\n    pub screenshots: Vec<Vec<u8>>,\n    pub console_logs: Arc<RwLock<Vec<String>>>,\n}\n\nimpl std::fmt::Debug for AppWorld {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AppWorld\").finish()\n    }\n}\n\nimpl AppWorld {\n    async fn new() -> Result<Self, anyhow::Error> {\n        let browser = build_browser().await.unwrap();\n\n        let page = browser.new_page(\"about:blank\").await?;\n\n        Ok(Self {\n            browser,\n            page,\n            req_resp: Arc::new(RwLock::new(HashMap::new())),\n            clipboard: HashMap::new(),\n            cookie_sender: None,\n            screenshots: Vec::new(),\n            console_logs: Arc::new(RwLock::new(Vec::new())),\n        })\n    }\n\n    pub async fn errors(&mut self) -> Result<()> {\n        if let Ok(error) = self.find(ids::ERROR_ERROR_ID).await {\n            Err(anyhow!(\"{}\", error.inner_text().await?.unwrap_or(String::from(\"no error in inner template?\"))))\n        } else {\n            Ok(())\n        }\n    }\n\n    pub async fn find(&self, id: &'static str) -> Result<Element> {\n        for _ in 0..4 {\n            if let Ok(el) = self.page.find_element(format!(\"#{id}\")).await {\n                return Ok(el);\n            }\n            crate::fixtures::wait().await;\n        }\n        Err(anyhow!(\"Can't find {id}\"))\n    }\n\n    pub async fn find_submit(&mut self) -> Result<Element> {\n        for _ in 0..4 {\n            if let Ok(el) = self.page.find_element(format!(\"input[type=submit]\")).await {\n                return Ok(el);\n            }\n            crate::fixtures::wait().await;\n        }\n        Err(anyhow!(\"Can't find input type=submit\"))\n    }\n\n    /*pub async fn find_all(&mut self, id: &'static str) -> Result<ElementList> {\n        Ok(ElementList(\n            self.page.find_elements(format!(\"#{id}\")).await?,\n        ))\n    }*/\n\n    pub async fn goto_url(&mut self, url: &str) -> Result<()> {\n        self.page\n            .goto(\n                NavigateParams::builder()\n                    .url(url)\n                    .build()\n                    .map_err(|err| anyhow!(err))?,\n            )\n            .await?\n            .wait_for_navigation()\n            .await?;\n        self.screenshot().await?;\n        Ok(())\n    }\n\n    pub async fn goto_path(&mut self, path: &str) -> Result<()> {\n        let url = format!(\"{}{}\", HOST, path);\n        self.page\n            .goto(\n                NavigateParams::builder()\n                    .url(url)\n                    .build()\n                    .map_err(|err| anyhow!(err))?,\n            )\n            .await?;\n        self.screenshot().await?;\n        Ok(())\n    }\n    pub async fn screenshot(&mut self) -> Result<()> {\n        let sc = self.page.screenshot(ScreenshotParams::default()).await?;\n        self.screenshots.push(sc);\n        Ok(())\n    }\n    pub async fn set_field<S: AsRef<str> + std::fmt::Display>(\n        &mut self,\n        id: &'static str,\n        value: S,\n    ) -> Result<()> {\n        let element = self.find(id).await?;\n        element.focus().await?.type_str(value).await?;\n        self.screenshot().await?;\n        Ok(())\n    }\n\n    pub async fn click(&mut self, id: &'static str) -> Result<()> {\n        self.find(id).await?.click().await?;\n        Ok(())\n    }\n    #[tracing::instrument(err)]\n    pub async fn submit(&mut self) -> Result<()> {\n        self.screenshot().await?;\n        self.find_submit().await?.click().await?;\n        Ok(())\n    }\n    pub async fn find_text(&self, text: String) -> Result<Element> {\n        let selector: String = format!(\"//*[contains(text(), '{text}') or @*='{text}']\");\n        let mut count = 0;\n        loop {\n            let result = self.page.find_xpath(&selector).await;\n            if result.is_err() && count < 4 {\n                count += 1;\n                crate::fixtures::wait().await;\n            } else {\n                let result = result?;\n                return Ok(result);\n            }\n        }\n    }\n    pub async fn url_contains(&self, s: &'static str) -> Result<()> {\n        if let Some(current) = self.page.url().await? {\n            if !current.contains(s) {\n                return Err(anyhow!(\"{current} does not contains {s}\"));\n            }\n        } else {\n            return Err(anyhow!(\"NO CURRENT URL FOUND\"));\n        }\n        Ok(())\n    }\n    pub async fn verify_route(&self, path: &'static str) -> Result<()> {\n        let url = format!(\"{}{}\", HOST, path);\n        if let Some(current) = self.page.url().await? {\n            if current != url {\n                return Err(anyhow!(\n                    \"EXPECTING ROUTE: {path}\\n but FOUND:\\n {current:#?}\"\n                ));\n            }\n        } else {\n            return Err(anyhow!(\n                \"EXPECTING ROUTE: {path}\\n but NO CURRENT URL FOUND\"\n            ));\n        }\n        Ok(())\n    }\n}\n\n/*\n#[derive(Debug)]\npub struct ElementList(Vec<Element>);\nimpl ElementList {\n    /// iterates over elements, finds first element whose text (as rendered) contains text given as function's argument.\n    pub async fn find_by_text(&self,text:&'static str) -> Result<Element> {\n        for element in self.0.iter() {\n            if let Ok(Some(inner_text)) = element.inner_text().await {\n                if inner_text.contains(text) {\n                    return Ok(element);\n                }\n            }\n        }\n        Err(anyhow!(format!(\"given text {} no element found\",text)))\n    }\n\n}*/\n\n#[derive(Serialize, Deserialize, Debug)]\nstruct Email {\n    id: Uuid,\n    to: Vec<Recipient>,\n}\n\n#[derive(Serialize, Deserialize, Debug)]\nstruct Recipient {\n    name: Option<String>,\n    email: String,\n}\n"
  },
  {
    "path": "projects/ory-kratos/e2e/tests/fixtures/mod.rs",
    "content": "pub mod steps;\nuse anyhow::{anyhow, Result};\n\npub async fn wait() {\n    tokio::time::sleep(tokio::time::Duration::from_millis(75)).await;\n}\n\nuse regex::Regex;\n\nfn extract_code_and_link(text: &str) -> Result<(String, String)> {\n    // Regex pattern for a six-digit number\n    let number_regex = Regex::new(r\"\\b\\d{6}\\b\").unwrap();\n    // Regex pattern for a URL\n    let url_regex = Regex::new(r\">(https?://[^<]+)<\").unwrap(); // Simplified URL pattern\n\n    // Search for a six-digit number\n    let number = number_regex\n        .find(text)\n        .map(|match_| match_.as_str().to_string())\n        .ok_or(anyhow!(\"Can't find number match\"))?;\n\n    // Search for a URL\n    let url = url_regex\n        .find(text)\n        .map(|match_| match_.as_str().to_string())\n        .ok_or(anyhow!(\"Can't find url match in \\n {text}\"))?;\n    let url = url.trim_matches(|c| c == '>' || c == '<').to_string();\n    let url = url.replace(\"amp;\", \"\");\n    Ok((number, url))\n}\n\nfn extract_code(text: &str) -> Result<String> {\n    // Regex pattern for a six-digit number\n    let number_regex = Regex::new(r\"\\b\\d{6}\\b\").unwrap();\n\n    // Search for a six-digit number\n    let number = number_regex\n        .find(text)\n        .map(|match_| match_.as_str().to_string())\n        .ok_or(anyhow!(\"Can't find number match\"))?;\n    Ok(number)\n}\n"
  },
  {
    "path": "projects/ory-kratos/e2e/tests/fixtures/steps.rs",
    "content": "use crate::{AppWorld, EMAIL_ID_MAP};\nuse anyhow::anyhow;\nuse anyhow::{Ok, Result};\nuse chromiumoxide::cdp::browser_protocol::input::TimeSinceEpoch;\nuse chromiumoxide::cdp::browser_protocol::network::{CookieParam, DeleteCookiesParams};\nuse cucumber::{given, then, when};\nuse fake::locales::EN;\nuse fake::{faker::internet::raw::FreeEmail, Fake};\n\nuse super::wait;\n#[given(\"I pass\")]\npub async fn i_pass(_world: &mut AppWorld) -> Result<()> {\n    tracing::info!(\"I pass and I trace.\");\n    Ok(())\n}\n\n#[given(\"I am on the homepage\")]\npub async fn navigate_to_homepage(world: &mut AppWorld) -> Result<()> {\n    world.goto_path(\"/\").await?;\n    Ok(())\n}\n\n#[then(\"I am on the homepage\")]\npub async fn check_url_for_homepage(world: &mut AppWorld) -> Result<()> {\n    world.verify_route(\"/\").await?;\n    Ok(())\n}\n\n#[given(\"I click register\")]\n#[when(\"I click register\")]\npub async fn click_register(world: &mut AppWorld) -> Result<()> {\n    world.click(ids::REGISTER_BUTTON_ID).await?;\n    Ok(())\n}\n\n#[given(\"I see the registration form\")]\n#[when(\"I see the registration form\")]\n#[then(\"I see the registration form\")]\npub async fn find_registration_form(world: &mut AppWorld) -> Result<()> {\n    world.find(ids::REGISTRATION_FORM_ID).await?;\n    Ok(())\n}\n\n#[given(\"I see the login form\")]\n#[when(\"I see the login form\")]\n#[then(\"I see the login form\")]\npub async fn find_login_form(world: &mut AppWorld) -> Result<()> {\n    world.find(ids::LOGIN_FORM_ID).await?;\n    Ok(())\n}\n\n#[given(\"I am on the registration page\")]\npub async fn navigate_to_register(world: &mut AppWorld) -> Result<()> {\n    world.goto_path(\"/register\").await?;\n    Ok(())\n}\n\n#[given(\"I enter valid credentials\")]\npub async fn fill_form_fields_with_credentials(world: &mut AppWorld) -> Result<()> {\n    let email = FreeEmail(EN).fake::<String>();\n    world\n        .set_field(ids::EMAIL_INPUT_ID, &email)\n        .await\n        .expect(&format!(\n            \"To find element with id {} BUT ERROR : \",\n            ids::EMAIL_INPUT_ID\n        ));\n    world.clipboard.insert(\"email\", email);\n    world\n        .set_field(ids::PASSWORD_INPUT_ID, ids::PASSWORD)\n        .await\n        .expect(&format!(\n            \"To find element with id {} BUT ERROR : \",\n            ids::PASSWORD_INPUT_ID\n        ));\n    world.submit().await?;\n    world.errors().await?;\n    wait().await;\n    Ok(())\n}\n\n#[given(\"I enter valid other credentials\")]\npub async fn fill_form_fields_with_other_credentials(world: &mut AppWorld) -> Result<()> {\n    let email = FreeEmail(EN).fake::<String>();\n    world\n        .set_field(ids::EMAIL_INPUT_ID, &email)\n        .await\n        .expect(&format!(\n            \"To find element with id {} BUT ERROR : \",\n            ids::EMAIL_INPUT_ID\n        ));\n    world.clipboard.insert(\"other_email\", email);\n    world\n        .set_field(ids::PASSWORD_INPUT_ID, ids::PASSWORD)\n        .await\n        .expect(&format!(\n            \"To find element with id {} BUT ERROR : \",\n            ids::PASSWORD_INPUT_ID\n        ));\n    world.submit().await?;\n    world.errors().await?;\n    wait().await;\n    Ok(())\n}\n#[given(\"I re-enter other valid credentials\")]\n#[when(\"I re-enter other valid credentials\")]\npub async fn fill_form_fields_with_previous_other_credentials(world: &mut AppWorld) -> Result<()> {\n    let email = world\n        .clipboard\n        .get(\"other_email\")\n        .cloned()\n        .ok_or(anyhow!(\"Can't find other credentials in clipboard\"))?;\n    world\n        .set_field(ids::EMAIL_INPUT_ID, &email)\n        .await\n        .expect(\"set email field\");\n    world\n        .set_field(ids::PASSWORD_INPUT_ID, ids::PASSWORD)\n        .await\n        .expect(\"set password field\");\n    world.submit().await?;\n    world.errors().await?;\n    Ok(())\n}\n\n#[when(\"I enter valid credentials\")]\n#[when(\"I re-enter valid credentials\")]\n#[given(\"I re-enter valid credentials\")]\npub async fn fill_form_fields_with_previous_credentials(world: &mut AppWorld) -> Result<()> {\n    let email = world.clipboard.get(\"email\").cloned();\n    let email = if let Some(email) = email {\n        email\n    } else {\n        let email = FreeEmail(EN).fake::<String>();\n        world.clipboard.insert(\"email\", email.clone());\n        email\n    };\n    world\n        .set_field(ids::EMAIL_INPUT_ID, &email)\n        .await\n        .expect(\"set email field\");\n    world\n        .set_field(ids::PASSWORD_INPUT_ID, ids::PASSWORD)\n        .await\n        .expect(\"set password field\");\n    world.submit().await?;\n    world.errors().await?;\n    Ok(())\n}\n\n#[then(\"I am on the verify email page\")]\npub async fn check_url_to_be_verify_page(world: &mut AppWorld) -> Result<()> {\n    world.find(ids::VERIFY_EMAIL_DIV_ID).await?;\n    Ok(())\n}\n#[given(\"I check my other email for the verification link and code\")]\n#[when(\"I check my other email for the verification link and code\")]\npub async fn check_email_other_for_verification_link_and_code(world: &mut AppWorld) -> Result<()> {\n    tokio::time::sleep(std::time::Duration::from_secs(2)).await;\n    // we've stored the email with the id\n    // so we get the id with our email from our clipboard\n    let email = world\n        .clipboard\n        .get(\"other_email\")\n        .ok_or(anyhow!(\"email not found in clipboard\"))?;\n    let id = EMAIL_ID_MAP\n        .read()\n        .await\n        .get(email)\n        .ok_or(anyhow!(\"{email} not found in EMAIL_ID_MAP\"))?\n        .clone();\n    // then we use the id to get the message from mailcrab\n    let body = reqwest::get(format!(\"http://127.0.0.1:1080/api/message/{}/body\", id))\n        .await\n        .unwrap()\n        .text()\n        .await\n        .unwrap();\n    let (code, link) = super::extract_code_and_link(&body)?;\n    world.clipboard.insert(\"code\", code);\n    world.clipboard.insert(\"link\", link);\n    Ok(())\n}\n\n#[given(\"I check my email for the verification link and code\")]\n#[when(\"I check my email for the verification link and code\")]\npub async fn check_email_for_verification_link_and_code(world: &mut AppWorld) -> Result<()> {\n    tokio::time::sleep(std::time::Duration::from_secs(2)).await;\n    // we've stored the email with the id\n    // so we get the id with our email from our clipboard\n    let email = world\n        .clipboard\n        .get(\"email\")\n        .ok_or(anyhow!(\"email not found in clipboard\"))?;\n    let id = EMAIL_ID_MAP\n        .read()\n        .await\n        .get(email)\n        .ok_or(anyhow!(\"{email} not found in EMAIL_ID_MAP\"))?\n        .clone();\n    // then we use the id to get the message from mailcrab\n    let body = reqwest::get(format!(\"http://127.0.0.1:1080/api/message/{}/body\", id))\n        .await\n        .unwrap()\n        .text()\n        .await\n        .unwrap();\n    let (code, link) = super::extract_code_and_link(&body)?;\n    world.clipboard.insert(\"code\", code);\n    world.clipboard.insert(\"link\", link);\n    Ok(())\n}\n\n#[given(\"I copy the code onto the verification link page\")]\n#[when(\"I copy the code onto the verification link page\")]\npub async fn copy_code_onto_verification_page(world: &mut AppWorld) -> Result<()> {\n    let link = world\n        .clipboard\n        .get(\"link\")\n        .ok_or(anyhow!(\"link not found in clipboard\"))?\n        .clone();\n    world.goto_url(&link).await?;\n    let code = world\n        .clipboard\n        .get(\"code\")\n        .ok_or(anyhow!(\"link not found in clipboard\"))?\n        .clone();\n    world\n        .set_field(ids::VERFICATION_CODE_ID, code)\n        .await\n        .expect(&format!(\"Can't find {}\", ids::VERFICATION_CODE_ID));\n    world.submit().await?;\n    world.click(\"continue\").await?;\n    wait().await;\n    Ok(())\n}\n\n#[when(\"I click login\")]\n#[given(\"I click login\")]\npub async fn click_login(world: &mut AppWorld) -> Result<()> {\n    world.click(ids::LOGIN_BUTTON_ID).await?;\n    wait().await;\n    Ok(())\n}\n\n#[given(\"I click logout\")]\n#[when(\"I click logout\")]\npub async fn click_logout(world: &mut AppWorld) -> Result<()> {\n    world.click(ids::LOGOUT_BUTTON_ID).await?;\n    wait().await;\n    world.errors().await?;\n    Ok(())\n}\n\n#[tracing::instrument]\n#[given(\"I am logged out\")]\n#[then(\"I am logged out\")]\npub async fn check_ory_kratos_cookie_doesnt_exist(world: &mut AppWorld) -> Result<()> {\n    let cookies = world.page.get_cookies().await?;\n    if !cookies\n        .iter()\n        .filter(|c| c.name.contains(\"ory_kratos_session\"))\n        .collect::<Vec<_>>()\n        .is_empty()\n    {\n        tracing::error!(\"{cookies:#?}\");\n        Err(anyhow!(\"Ory kratos cookie exists.\"))\n    } else {\n        Ok(())\n    }\n}\n\n#[then(\"I am logged in\")]\n#[given(\"I am logged in\")]\npub async fn check_ory_kratos_cookie_exists(world: &mut AppWorld) -> Result<()> {\n    if world\n        .page\n        .get_cookies()\n        .await?\n        .iter()\n        .filter(|c| c.name.contains(\"ory_kratos_session\"))\n        .collect::<Vec<_>>()\n        .is_empty()\n    {\n        Err(anyhow!(\"Ory kratos cookie doesn't exists.\"))\n    } else {\n        Ok(())\n    }\n}\n\n#[given(\"I add example post\")]\n#[when(\"I add example post\")]\npub async fn add_content_to_box(world: &mut AppWorld) -> Result<()> {\n    let content: Vec<String> = fake::faker::lorem::en::Words(0..10).fake();\n    let content = content.join(\" \");\n    world.clipboard.insert(\"content\", content.clone());\n    world\n        .set_field(ids::POST_POST_TEXT_AREA_ID, content)\n        .await?;\n    world.click(ids::POST_POST_SUBMIT_ID).await?;\n    Ok(())\n}\n\n#[given(\"I see example content posted\")]\n#[then(\"I see example content posted\")]\n#[when(\"I see example content posted\")]\npub async fn see_my_content_posted(world: &mut AppWorld) -> Result<()> {\n    world.click(ids::POST_SHOW_LIST_BUTTON_ID).await?;\n    let content = world\n        .clipboard\n        .get(\"content\")\n        .cloned()\n        .ok_or(anyhow!(\"Can't find content in clipboard\"))?;\n    world.errors().await?;\n    let _ = world.find_text(content).await?;\n    Ok(())\n}\n\n#[when(\"I see error\")]\n#[then(\"I see error\")]\npub async fn see_err(world: &mut AppWorld) -> Result<()> {\n    wait().await;\n    if world.errors().await.is_ok() {\n        return Err(anyhow!(\"Expecting an error.\"));\n    }\n    Ok(())\n}\n\n#[when(\"I don't see error\")]\n#[then(\"I don't see error\")]\npub async fn dont_see_err(world: &mut AppWorld) -> Result<()> {\n    world.errors().await?;\n    Ok(())\n}\n\n#[given(\"I add other email as editor\")]\n#[when(\"I add other email as editor\")]\npub async fn add_other_email_as_editor(world: &mut AppWorld) -> Result<()> {\n    let other_email = world\n        .clipboard\n        .get(\"other_email\")\n        .cloned()\n        .ok_or(anyhow!(\"Can't find other email.\"))?;\n    world\n        .set_field(ids::POST_ADD_EDITOR_INPUT_ID, other_email)\n        .await?;\n    world.click(ids::POST_ADD_EDITOR_SUBMIT_ID).await?;\n    Ok(())\n}\n\n#[when(\"I logout\")]\npub async fn i_logout(world: &mut AppWorld) -> Result<()> {\n    world.click(ids::LOGOUT_BUTTON_ID).await?;\n    world.errors().await?;\n    Ok(())\n}\n#[when(\"I edit example post\")]\npub async fn add_new_edit_content_to_previous(world: &mut AppWorld) -> Result<()> {\n    let edit_content: Vec<String> = fake::faker::lorem::en::Words(0..10).fake();\n    let edit_content = edit_content.join(\" \");\n    world.clipboard.insert(\"edit_content\", edit_content.clone());\n    world\n        .set_field(ids::POST_EDIT_TEXT_AREA_ID, edit_content)\n        .await?;\n    world.click(ids::POST_EDIT_SUBMIT_ID).await?;\n    Ok(())\n}\n#[then(\"I see my new content posted\")]\npub async fn new_content_boom_ba_da_boom(world: &mut AppWorld) -> Result<()> {\n    let content = world\n        .clipboard\n        .get(\"edit_content\")\n        .cloned()\n        .ok_or(anyhow!(\"Can't find content in clipboard\"))?;\n    world.find_text(content).await?;\n    Ok(())\n}\n#[then(\"I don't see old content\")]\npub async fn dont_see_old_content_posted(world: &mut AppWorld) -> Result<()> {\n    let content = world\n        .clipboard\n        .get(\"content\")\n        .cloned()\n        .ok_or(anyhow!(\"Can't find content in clipboard\"))?;\n    if world.find_text(content).await.is_ok() {\n        return Err(anyhow!(\"But I do see old content...\"));\n    }\n    Ok(())\n}\n\n#[given(\"I click show post list\")]\n#[when(\"I click show post list\")]\npub async fn i_click_show_post_list(world: &mut AppWorld) -> Result<()> {\n    world.click(ids::POST_SHOW_LIST_BUTTON_ID).await?;\n    Ok(())\n}\n\n#[given(\"I clear cookies\")]\npub async fn i_clear_cookies(world: &mut AppWorld) -> Result<()> {\n    let cookies = world\n        .page\n        .get_cookies()\n        .await?\n        .into_iter()\n        .map(|cookie| {\n            DeleteCookiesParams::from_cookie(&CookieParam {\n                name: cookie.name,\n                value: cookie.value,\n                url: None, // Since there's no direct field for URL, it's set as None\n                domain: Some(cookie.domain),\n                path: Some(cookie.path),\n                secure: Some(cookie.secure),\n                http_only: Some(cookie.http_only),\n                same_site: cookie.same_site,\n                // Assuming you have a way to convert f64 expires to TimeSinceEpoch\n                expires: None,\n                priority: Some(cookie.priority),\n                same_party: Some(cookie.same_party),\n                source_scheme: Some(cookie.source_scheme),\n                source_port: Some(cookie.source_port),\n                partition_key: cookie.partition_key,\n                // Note: `partition_key_opaque` is omitted since it doesn't have a direct mapping\n            })\n        })\n        .collect();\n    world.page.delete_cookies(cookies).await?;\n    Ok(())\n}\n\n#[given(\"I click recover email\")]\npub async fn click_recover_email(world: &mut AppWorld) -> Result<()> {\n    world.click(ids::RECOVER_EMAIL_BUTTON_ID).await?;\n    wait().await;\n    Ok(())\n}\n#[given(\"I submit valid recovery email\")]\npub async fn submit_valid_recovery_email(world: &mut AppWorld) -> Result<()> {\n    let email = world\n        .clipboard\n        .get(\"email\")\n        .cloned()\n        .ok_or(anyhow!(\"Expecting email in clipboard if recovering email.\"))?;\n    world\n        .set_field(ids::EMAIL_INPUT_ID, &email)\n        .await\n        .expect(\"set email field\");\n    world.submit().await?;\n    world.errors().await?;\n    Ok(())\n}\n#[given(\"I check my email for recovery link and code\")]\npub async fn check_email_for_recovery_link_and_code(world: &mut AppWorld) -> Result<()> {\n    tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n    // we've stored the email with the id\n    // so we get the id with our email from our clipboard\n    let email = world\n        .clipboard\n        .get(\"email\")\n        .ok_or(anyhow!(\"email not found in clipboard\"))?;\n    let id = EMAIL_ID_MAP\n        .read()\n        .await\n        .get(email)\n        .ok_or(anyhow!(\"{email} not found in EMAIL_ID_MAP\"))?\n        .clone();\n    // then we use the id to get the message from mailcrab\n    let body = reqwest::get(format!(\"http://127.0.0.1:1080/api/message/{}/body\", id))\n        .await\n        .unwrap()\n        .text()\n        .await\n        .unwrap();\n    let code = super::extract_code(&body)?;\n    world.clipboard.insert(\"recovery_code\", code);\n    Ok(())\n}\n\n#[when(\"I copy the code onto the recovery link page\")]\npub async fn copy_code_onto_recovery_page(world: &mut AppWorld) -> Result<()> {\n    // we should figure out how to be on the right page, will this just work?\n\n    let code = world\n        .clipboard\n        .get(\"recovery_code\")\n        .ok_or(anyhow!(\"link not found in clipboard\"))?\n        .clone();\n    world\n        .set_field(ids::VERFICATION_CODE_ID, code)\n        .await\n        .expect(&format!(\"Can't find {}\", ids::VERFICATION_CODE_ID));\n    world.submit().await?;\n    wait().await;\n    Ok(())\n}\n\n#[then(\"I am on the settings page\")]\npub async fn im_on_settings_page(world: &mut AppWorld) -> Result<()> {\n    wait().await;\n    world.url_contains(\"/settings\").await?;\n    Ok(())\n}\n\n#[given(\"I enter recovery credentials\")]\n#[when(\"I enter recovery credentials\")]\npub async fn i_enter_a_new_recovery_password(world: &mut AppWorld) -> Result<()> {\n    let email = world\n        .clipboard\n        .get(\"email\")\n        .cloned()\n        .ok_or(anyhow!(\"Can't find credentials in clipboard\"))?;\n    world\n        .set_field(ids::EMAIL_INPUT_ID, &email)\n        .await\n        .expect(\"set email field\");\n    world\n        .set_field(ids::PASSWORD_INPUT_ID, ids::RECOVERY_PASSWORD)\n        .await\n        .expect(\"set password field\");\n    let code = world\n        .clipboard\n        .get(\"recovery_code\")\n        .ok_or(anyhow!(\"link not found in clipboard\"))?\n        .clone();\n    world\n        .set_field(ids::VERFICATION_CODE_ID, code)\n        .await\n        .expect(&format!(\"Can't find {}\", ids::VERFICATION_CODE_ID));\n    world.submit().await?;\n    wait().await;\n    Ok(())\n}\n"
  },
  {
    "path": "projects/ory-kratos/frontend/Cargo.toml",
    "content": "[package]\nname = \"frontend\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\napp = { path = \"../app\", default-features = false, features = [\"hydrate\"] }\nleptos = { workspace = true, features = [ \"hydrate\" ] }\n\nconsole_error_panic_hook.workspace = true\nconsole_log.workspace = true\nwasm-bindgen.workspace = true\n\nids = { path=\"../ids\" }"
  },
  {
    "path": "projects/ory-kratos/frontend/src/lib.rs",
    "content": "use app::*;\nuse leptos::*;\nuse wasm_bindgen::prelude::wasm_bindgen;\n\n#[wasm_bindgen]\npub fn hydrate() {\n    // initializes logging using the `log` crate\n    // _ = console_log::init_with_level(tracing::Level::Debug);\n    console_error_panic_hook::set_once();\n\n    leptos::mount_to_body(App);\n}\n"
  },
  {
    "path": "projects/ory-kratos/ids/Cargo.toml",
    "content": "[package]\nname = \"ids\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n"
  },
  {
    "path": "projects/ory-kratos/ids/src/lib.rs",
    "content": "pub static REGISTER_BUTTON_ID: &'static str = \"register_button_id\";\npub static REGISTRATION_FORM_ID: &'static str = \"registration_form_id\";\n\npub static EMAIL_INPUT_ID: &'static str = \"email_input_id\";\npub static PASSWORD_INPUT_ID: &'static str = \"password_input_id\";\n\npub static VERIFY_EMAIL_DIV_ID: &'static str = \"verify_email_div_id\";\npub static VERIFICATION_FORM_ID: &'static str = \"verification_form_id\";\n\npub static LOGIN_FORM_ID: &'static str = \"login_form_id\";\n\npub static REGISTER_ROUTE: &'static str = \"/register\";\npub static VERIFICATION_ROUTE: &'static str = \"/verification\";\npub static LOGIN_ROUTE: &'static str = \"/login\";\npub static KRATOS_ERROR_ROUTE: &'static str = \"/kratos_error\";\npub static RECOVERY_ROUTE: &'static str = \"/recovery\";\npub static SETTINGS_ROUTE: &'static str = \"/settings\";\n\npub static ERROR_ERROR_ID: &'static str = \"error_template_id\";\npub static ERROR_COOKIES_ID: &'static str = \"error_cookies_id\";\n\npub static VERFICATION_CODE_ID: &'static str = \"verification_code_id\";\n\npub static KRATOS_FORM_SUBMIT_ID: &'static str = \"kratos_form_submit_id\";\n\npub static LOGOUT_BUTTON_ID: &'static str = \"logout_button_id\";\npub static LOGIN_BUTTON_ID: &'static str = \"login_button_id\";\n/// This function is for use in kratos_html, it takes the name of the input node and it\n/// matches it according to what we've specified in the kratos schema file. If we change the schema.\n/// I.e use a phone instead of an email, the identifier id will change and break tests that expect an email.\n/// i.e use oidc instead of password, as auth method... that will break tests too.\n/// Which is good.\npub fn match_name_to_id(name: String) -> &'static str {\n    match name.as_str() {\n        \"traits.email\" => EMAIL_INPUT_ID,\n        \"identifier\" => EMAIL_INPUT_ID,\n        \"email\" => EMAIL_INPUT_ID,\n        \"password\" => PASSWORD_INPUT_ID,\n        \"code\" => VERFICATION_CODE_ID,\n        \"totp_code\" => VERFICATION_CODE_ID,\n        _ => \"\",\n    }\n}\n\npub static POST_POST_TEXT_AREA_ID: &'static str = \"post_post_text_area_id\";\npub static POST_POST_SUBMIT_ID: &'static str = \"post_post_submit_id\";\npub static POST_ADD_EDITOR_BUTTON_ID: &'static str = \"post_add_editor_button_id\";\npub static POST_ADD_EDITOR_INPUT_ID: &'static str = \"add_editor_input_id\";\npub static POST_ADD_EDITOR_SUBMIT_ID: &'static str = \"post_add_editor_submit_id\";\npub static POST_DELETE_ID: &'static str = \"post_delete_id\";\npub static POST_EDIT_TEXT_AREA_ID: &'static str = \"post_edit_text_area_id\";\npub static POST_EDIT_SUBMIT_ID: &'static str = \"post_edit_submit_id\";\npub static POST_SHOW_LIST_BUTTON_ID: &'static str = \"post_show_list_button_id\";\n\npub static CLEAR_COOKIES_BUTTON_ID: &'static str = \"clear_cookies_button_id\";\n\npub static RECOVERY_FORM_ID: &'static str = \"recovery_form_id\";\npub static RECOVER_EMAIL_BUTTON_ID: &'static str = \"recover_email_button_id\";\n\npub static RECOVERY_PASSWORD: &'static str = \"RECOVERY_SuPeRsAfEpAsSwOrD1234!\";\npub static PASSWORD: &'static str = \"SuPeRsAfEpAsSwOrD1234!\";\n\npub static SETTINGS_FORM_ID: &'static str = \"settings_form_id\";\n"
  },
  {
    "path": "projects/ory-kratos/kratos/email.schema.json",
    "content": "{\n    \"$id\": \"email-schema\",\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n    \"title\": \"EmailPerson\",\n    \"type\": \"object\",\n    \"properties\": {\n      \"traits\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"email\": {\n            \"type\": \"string\",\n            \"format\": \"email\",\n            \"title\": \"E-Mail\",\n            \"minLength\": 3,\n            \"ory.sh/kratos\": {\n              \"credentials\": {\n                \"password\": {\n                  \"identifier\": true\n                },\n                \"webauthn\": {\n                  \"identifier\": true\n                },\n                \"totp\": {\n                  \"account_name\": true\n                }\n              },\n              \"verification\": {\n                \"via\": \"email\"\n              },\n              \"recovery\": {\n                \"via\": \"email\"\n              }\n            }\n          }\n        },\n        \"required\": [\n          \"email\"\n        ],\n        \"additionalProperties\": false\n      }\n    }\n  }"
  },
  {
    "path": "projects/ory-kratos/kratos/kratos.yaml",
    "content": "version: v1.1.0\n\ndsn: memory\n\n\nserve:\n  public:\n    base_url: http://127.0.0.1:4433/\n    cors:\n      enabled: false\n      allowed_headers:\n        - Cookie\n        - Content-Type\n        - x-csrf-token\n        - accept\n      exposed_headers:  \n        - Cookie\n        - Content-Type \n        - Set-Cookie\n        - x-csrf-token\n        - accept\n  admin:\n    base_url: http://127.0.0.1:4434/\n\nselfservice:\n  default_browser_return_url: https://127.0.0.1:3000/\n  allowed_return_urls:\n    - https://127.0.0.1:3000\n\n  methods:\n    password:\n      enabled: true\n    totp:\n      config:\n        issuer: Kratos\n      enabled: true\n    code:\n      enabled: true\n    oidc:\n      \n      \n\n  flows:\n    error:\n      ui_url: https://127.0.0.1:3000/kratos_error\n\n    settings:\n      ui_url: https://127.0.0.1:3000/settings\n      privileged_session_max_age: 15m\n      required_aal: aal1\n\n    recovery:\n      enabled: true\n      ui_url: https://127.0.0.1:3000/recovery\n      use: code\n\n    verification:\n      enabled: true\n      ui_url: https://127.0.0.1:3000/verification\n      use: code\n      after:\n        default_browser_return_url: https://127.0.0.1:3000/\n\n    logout:\n      after:\n        default_browser_return_url: https://127.0.0.1:3000/login\n\n    login:\n      ui_url: https://127.0.0.1:3000/login\n      after:\n        default_browser_return_url: https://127.0.0.1:3000\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: https://127.0.0.1:3000/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n            - hook: show_verification_ui\n\nlog:\n  level: trace\n  format: json\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n  cipher:\n    - 32-LONG-SECRET-NOT-SECURE-AT-ALL\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nhashers:\n  algorithm: bcrypt\n  bcrypt:\n    cost: 8\n\nidentity:\n  default_schema_id: email_v0\n  schemas:\n    - id: email_v0\n      url: file:///etc/config/kratos/email.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtp://user:pass@mailcrab:1025/?disable_starttls=true&skip_ssl_verify=true\n\nfeature_flags:\n  use_continue_with_transitions: true"
  },
  {
    "path": "projects/ory-kratos/migrations/01_create_users.sql",
    "content": "CREATE TABLE users (\n        user_id TEXT PRIMARY KEY,\n        identity_id TEXT NOT NULL,\n        email TEXT NOT NULL\n    );\n CREATE INDEX IF NOT EXISTS idx_identity_id ON users (identity_id);"
  },
  {
    "path": "projects/ory-kratos/migrations/02_create_posts.sql",
    "content": "CREATE TABLE IF NOT EXISTS posts (\n        post_id TEXT PRIMARY KEY NOT NULL,\n        user_id TEXT NOT NULL,\n        content TEXT NOT NULL,\n        FOREIGN KEY (user_id) REFERENCES users(user_id)\n    );"
  },
  {
    "path": "projects/ory-kratos/migrations/03_create_post_permissions.sql",
    "content": "CREATE TABLE IF NOT EXISTS post_permissions (\n        post_id TEXT NOT NULL,\n        user_id TEXT NOT NULL,\n        read BOOL NOT NULL,\n        write BOOL NOT NULL,\n        `delete` BOOL NOT NULL,\n        FOREIGN KEY (user_id) REFERENCES users(user_id),\n        FOREIGN KEY (post_id) REFERENCES posts(post_id),\n        PRIMARY KEY (post_id, user_id)\n    );"
  },
  {
    "path": "projects/ory-kratos/server/Cargo.toml",
    "content": "[package]\nname = \"server\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\napp = { path = \"../app\", default-features = false, features = [\"ssr\"] }\nleptos = { workspace = true, features = [ \"ssr\" ]}\nleptos_axum.workspace = true\n\naxum.workspace = true\naxum-server.workspace = true\nsqlx.workspace = true\ntracing.workspace = true\ntracing-subscriber.workspace = true\ntokio.workspace = true\ntower.workspace = true\ntower-http.workspace = true\nory-kratos-client.workspace = true"
  },
  {
    "path": "projects/ory-kratos/server/src/extract_session.rs",
    "content": "use axum::{async_trait, extract::FromRequestParts, RequestPartsExt};\nuse axum_extra::extract::CookieJar;\nuse http::request::Parts;\nuse ory_kratos_client::models::session::Session;\npub struct ExtractSession(pub Session);\n\n#[async_trait]\nimpl<S> FromRequestParts<S> for ExtractSession\nwhere\n    S: Send + Sync,\n{\n    type Rejection = String;\n\n    #[tracing::instrument(err(Debug),skip_all)]\n    async fn from_request_parts(parts:&mut Parts, _state: &S) -> Result<Self, Self::Rejection> {\n        let cookie_jar = parts\n            .extract::<CookieJar>()\n            .await\n            .unwrap();\n        let csrf_cookie = cookie_jar\n            .iter()\n            .filter(|cookie| cookie.name().contains(\"csrf_token\"))\n            .next()\n            .ok_or(\n                \"Expecting a csrf_token cookie to already be set if fetching a pre-existing flow\".to_string()\n            )?;\n    let session_cookie = cookie_jar\n        .get(\"ory_kratos_session\")\n        .ok_or(\"Ory Kratos Session cookie does not exist.\".to_string())?;\n    let client = reqwest::ClientBuilder::new()\n        .redirect(reqwest::redirect::Policy::none())\n        .build()\n        .unwrap();\n\n    let resp = client\n        .get(\"http://127.0.0.1:4433/sessions/whoami\")\n        .header(\"accept\",\"application/json\")\n        .header(\n            \"cookie\",\n            format!(\"{}={}\", csrf_cookie.name(), csrf_cookie.value()),\n        )\n        .header(\n            \"cookie\",\n            format!(\"{}={}\",session_cookie.name(),session_cookie.value())\n        )\n        .send()\n        .await\n        .map_err(|err|format!(\"Error sending resp to whoami err:{:#?}\",err).to_string())?;\n        let session = resp.json::<Session>().await\n            .map_err(|err|format!(\"Error getting json from body err:{:#?}\",err).to_string())?;\n        Ok(Self(session))\n    }\n}\n\n"
  },
  {
    "path": "projects/ory-kratos/server/src/fileserv.rs",
    "content": "use app::App;\nuse axum::response::Response as AxumResponse;\nuse axum::{\n    body::Body,\n    extract::State,\n    http::{Request, Response, StatusCode, Uri},\n    response::IntoResponse,\n};\nuse leptos::*;\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\n\npub async fn file_and_error_handler(\n    uri: Uri,\n    State(options): State<LeptosOptions>,\n    req: Request<Body>,\n) -> AxumResponse {\n    let root = options.site_root.clone();\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        let handler =\n            leptos_axum::render_app_to_stream(options.to_owned(), move || view! { <App/> });\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.map(Body::new)),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n"
  },
  {
    "path": "projects/ory-kratos/server/src/main.rs",
    "content": "use app::*;\nuse axum::Router;\nuse axum_server::tls_rustls::RustlsConfig;\nuse fileserv::file_and_error_handler;\nuse leptos::*;\nuse leptos_axum::{generate_route_list, LeptosRoutes};\nuse std::path::PathBuf;\nuse tracing_subscriber::EnvFilter;\npub mod fileserv;\n\n#[tokio::main]\nasync fn main() {\n    tracing_subscriber::fmt()\n        .with_env_filter(EnvFilter::new(\"debug,tower_http=trace,rustls=error,cookie_store=error,reqwest=error,sqlx=error,hyper=error,h2=error\"))\n        .pretty()\n        .init();\n\n    // we get a new db every restart.\n\n    _ = std::fs::remove_file(\"./app.db\");\n    _ = std::fs::remove_file(\"./app.db-shm\");\n    _ = std::fs::remove_file(\"./app.db-wal\");\n\n    std::process::Command::new(\"sqlx\")\n        .args([\"db\", \"create\", \"--database-url\", \"sqlite:app.db\"])\n        .status()\n        .expect(\"sqlx to exist on user machine\");\n\n    std::process::Command::new(\"sqlx\")\n        .args([\"migrate\", \"run\", \"--database-url\", \"sqlite:app.db\"])\n        .status()\n        .expect(\"sqlite3 to exist on user machine\");\n\n    let pool = sqlx::SqlitePool::connect(\"sqlite:app.db\").await.unwrap();\n\n    let config =\n        RustlsConfig::from_pem_file(PathBuf::from(\"./cert.pem\"), PathBuf::from(\"./key.pem\"))\n            .await\n            .unwrap();\n\n    // Setting get_configuration(None) means we'll be using cargo-leptos's env values\n    // For deployment these variables are:\n    // <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>\n    // Alternately a file can be specified such as Some(\"Cargo.toml\")\n    // The file would need to be included with the executable when moved to deployment\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // build our application with a route\n    let app = Router::new()\n        .leptos_routes(&leptos_options, routes, App)\n        .fallback(file_and_error_handler)\n        .layer(axum::Extension(pool))\n        .layer(tower_http::trace::TraceLayer::new_for_http())\n        .with_state(leptos_options);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    // axum_server::bind_rustl is a wrapper around that\n    // in real use case we'd want to also run a server that redirects http requests with https to the https server\n    println!(\"listening on https://{}\", &addr);\n    axum_server::bind_rustls(addr, config)\n        .serve(app.into_make_service())\n        .await\n        .unwrap();\n}\n"
  },
  {
    "path": "projects/ory-kratos/style/main.scss",
    "content": "body {\n\tfont-family: sans-serif;\n\ttext-align: center;\n}\n\n.google-sign-in-button {\n    cursor: pointer;\n    transition: background-color .3s, box-shadow .3s;\n        \n    padding: 12px 16px 12px 42px;\n    border: none;\n    border-radius: 3px;\n    box-shadow: 0 -1px 0 rgba(0, 0, 0, .04), 0 1px 1px rgba(0, 0, 0, .25);\n    \n    color: #757575;\n    font-size: 14px;\n    font-weight: 500;\n    font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen,Ubuntu,Cantarell,\"Fira Sans\",\"Droid Sans\",\"Helvetica Neue\",sans-serif;\n    \n    background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTgiIGhlaWdodD0iMTgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMTcuNiA5LjJsLS4xLTEuOEg5djMuNGg0LjhDMTMuNiAxMiAxMyAxMyAxMiAxMy42djIuMmgzYTguOCA4LjggMCAwIDAgMi42LTYuNnoiIGZpbGw9IiM0Mjg1RjQiIGZpbGwtcnVsZT0ibm9uemVybyIvPjxwYXRoIGQ9Ik05IDE4YzIuNCAwIDQuNS0uOCA2LTIuMmwtMy0yLjJhNS40IDUuNCAwIDAgMS04LTIuOUgxVjEzYTkgOSAwIDAgMCA4IDV6IiBmaWxsPSIjMzRBODUzIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNNCAxMC43YTUuNCA1LjQgMCAwIDEgMC0zLjRWNUgxYTkgOSAwIDAgMCAwIDhsMy0yLjN6IiBmaWxsPSIjRkJCQzA1IiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48cGF0aCBkPSJNOSAzLjZjMS4zIDAgMi41LjQgMy40IDEuM0wxNSAyLjNBOSA5IDAgMCAwIDEgNWwzIDIuNGE1LjQgNS40IDAgMCAxIDUtMy43eiIgZmlsbD0iI0VBNDMzNSIgZmlsbC1ydWxlPSJub256ZXJvIi8+PHBhdGggZD0iTTAgMGgxOHYxOEgweiIvPjwvZz48L3N2Zz4=);\n    background-color: white;\n    background-repeat: no-repeat;\n    background-position: 12px 11px;\n}\n\n.google-sign-in-button:hover {\n    box-shadow: 0 -1px 0 rgba(0, 0, 0, .04), 0 2px 4px rgba(0, 0, 0, .25);\n}\n\n.google-sign-in-button:active {\n    background-color: #eeeeee;\n}\n\n.google-sign-in-button:active {\n    outline: none;\n        box-shadow: \n        0 -1px 0 rgba(0, 0, 0, .04),\n        0 2px 4px rgba(0, 0, 0, .25),\n        0 0 0 3px #c8dafc;\n}\n\n.google-sign-in-button:disabled {\n    filter: grayscale(100%);\n    background-color: #ebebeb;\n    box-shadow: 0 -1px 0 rgba(0, 0, 0, .04), 0 1px 1px rgba(0, 0, 0, .25);\n    cursor: not-allowed;\n}\n"
  },
  {
    "path": "projects/session_auth_axum/Cargo.toml",
    "content": "[package]\nname = \"session_auth_axum\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nanyhow = \"1.0\"\nconsole_log = \"1.0\"\nrand = { version = \"0.8.0\", features = [\"min_const_gen\"], optional = true }\nconsole_error_panic_hook = \"0.1.0\"\nfutures = \"0.3.0\"\nleptos = { path = \"../../leptos\" }\nleptos_meta = { path = \"../../meta\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../router\" }\nlog = \"0.4.0\"\nsimple_logger = \"5.0\"\nserde = { version = \"1.0\", features = [\"derive\"] }\naxum = { version = \"0.8.0\", optional = true, features = [\"macros\"] }\ntower = { version = \"0.5.0\", optional = true }\ntower-http = { version = \"0.6.0\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.0\", features = [\"full\"], optional = true }\nhttp = { version = \"1.0\" }\nsqlx = { version = \"0.8.0\", features = [\n  \"runtime-tokio-rustls\",\n  \"sqlite\",\n], optional = true }\nthiserror = \"1.0\"\nwasm-bindgen = \"0.2.0\"\naxum_session_auth = { version = \"0.16.0\", features = [], optional = true }\naxum_session = { version = \"0.16.0\", features = [], optional = true }\naxum_session_sqlx = { version = \"0.5.0\", features = [\n  \"sqlite\",\n  \"tls-rustls\",\n], optional = true }\nbcrypt = { version = \"0.17.0\", optional = true }\nasync-trait = { version = \"0.1.0\", optional = true }\n\n[features]\ndefault = [\"ssr\"]\nhydrate = [\"leptos/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"dep:axum_session_sqlx\",\n  \"dep:axum_session_auth\",\n  \"dep:axum_session\",\n  \"dep:async-trait\",\n  \"dep:sqlx\",\n  \"dep:bcrypt\",\n  \"dep:rand\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:leptos_axum\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"ssr\", \"hydrate\"]]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"session_auth_axum\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "projects/session_auth_axum/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "projects/session_auth_axum/Makefile.toml",
    "content": "extend = [\n    { path = \"../cargo-make/main.toml\" },\n    { path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"session_auth_axum\"\n"
  },
  {
    "path": "projects/session_auth_axum/README.md",
    "content": "# Leptos Authenticated Todo App Sqlite with Axum\n\nThis example creates a basic todo app with an Axum backend that uses Leptos' server functions to call sqlx from the client and seamlessly run it on the server. It lets you login, signup, and submit todos as different users, or a guest.\n\n## Getting Started\n\nSee the [Examples README](../README.md) for setup and run instructions.\n\n## Quick Start\n\nRun `cargo leptos watch` to run this example.\n"
  },
  {
    "path": "projects/session_auth_axum/flake.nix",
    "content": "{\n  inputs.nixpkgs.url = \"github:NixOS/nixpkgs/nixos-22.05\";\n  inputs.rust-overlay.url = \"github:oxalica/rust-overlay\";\n  inputs.rust-overlay.inputs.nixpkgs.follows = \"nixpkgs\";\n  inputs.flake-utils.url = \"github:numtide/flake-utils\";\n\n  outputs = { self, nixpkgs, rust-overlay, flake-utils }:\n    flake-utils.lib.eachDefaultSystem (system:\n      let\n        pkgs = import nixpkgs {\n          inherit system;\n          overlays = [ (import rust-overlay) ];\n        };\n      in\n        with pkgs; rec {\n          devShells.default = mkShell {\n\n            shellHook = ''\n              export PKG_CONFIG_PATH=\"${pkgs.openssl.dev}/lib/pkgconfig\";\n            '';\n\n            nativeBuildInputs = [\n              pkg-config\n            ];\n            buildInputs = [\n              trunk\n              sqlite\n              sass\n              openssl\n              (rust-bin.nightly.latest.default.override {\n                extensions = [ \"rust-src\" ];\n                targets = [ \"wasm32-unknown-unknown\" ];\n              })\n            ];\n          };\n        });\n}"
  },
  {
    "path": "projects/session_auth_axum/migrations/20230226000000_create_todo_table.sql",
    "content": "CREATE TABLE IF NOT EXISTS users (\n  id         INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n  username   TEXT NOT NULL UNIQUE,\n  password   TEXT NOT NULL,\n  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS user_permissions (\n    user_id  INTEGER NOT NULL,\n    token    TEXT NOT NULL\n);\n\n-- INSERT INTO users (id, anonymous, username, password) \n-- SELECT 0, true, 'Guest', ''\n-- ON CONFLICT(id) DO UPDATE SET\n--     anonymous = EXCLUDED.anonymous,\n--     username = EXCLUDED.username;\n\n\nCREATE TABLE IF NOT EXISTS todos (\n  id         INTEGER PRIMARY KEY AUTOINCREMENT,\n  user_id    INTEGER NOT NULL,\n  title      TEXT NOT NULL,\n  completed  BOOLEAN,\n  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n  -- FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE\n);\n"
  },
  {
    "path": "projects/session_auth_axum/src/auth.rs",
    "content": "use leptos::prelude::*;\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashSet;\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct User {\n    pub id: i64,\n    pub username: String,\n    pub permissions: HashSet<String>,\n}\n\n// Explicitly is not Serialize/Deserialize!\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct UserPasshash(String);\n\nimpl Default for User {\n    fn default() -> Self {\n        let permissions = HashSet::new();\n\n        Self {\n            id: -1,\n            username: \"Guest\".into(),\n            permissions,\n        }\n    }\n}\n\n#[cfg(feature = \"ssr\")]\npub mod ssr {\n    pub use super::{User, UserPasshash};\n    pub use axum_session_auth::{Authentication, HasPermission};\n    use axum_session_sqlx::SessionSqlitePool;\n    pub use sqlx::SqlitePool;\n    pub use std::collections::HashSet;\n    pub type AuthSession = axum_session_auth::AuthSession<\n        User,\n        i64,\n        SessionSqlitePool,\n        SqlitePool,\n    >;\n    pub use crate::todo::ssr::{auth, pool};\n    pub use async_trait::async_trait;\n    pub use bcrypt::{hash, verify, DEFAULT_COST};\n\n    impl User {\n        pub async fn get_with_passhash(\n            id: i64,\n            pool: &SqlitePool,\n        ) -> Option<(Self, UserPasshash)> {\n            let sqluser = sqlx::query_as::<_, SqlUser>(\n                \"SELECT * FROM users WHERE id = ?\",\n            )\n            .bind(id)\n            .fetch_one(pool)\n            .await\n            .ok()?;\n\n            //lets just get all the tokens the user can use, we will only use the full permissions if modifying them.\n            let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(\n                \"SELECT token FROM user_permissions WHERE user_id = ?;\",\n            )\n            .bind(id)\n            .fetch_all(pool)\n            .await\n            .ok()?;\n\n            Some(sqluser.into_user(Some(sql_user_perms)))\n        }\n\n        pub async fn get(id: i64, pool: &SqlitePool) -> Option<Self> {\n            User::get_with_passhash(id, pool)\n                .await\n                .map(|(user, _)| user)\n        }\n\n        pub async fn get_from_username_with_passhash(\n            name: String,\n            pool: &SqlitePool,\n        ) -> Option<(Self, UserPasshash)> {\n            let sqluser = sqlx::query_as::<_, SqlUser>(\n                \"SELECT * FROM users WHERE username = ?\",\n            )\n            .bind(name)\n            .fetch_one(pool)\n            .await\n            .ok()?;\n\n            //lets just get all the tokens the user can use, we will only use the full permissions if modifying them.\n            let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(\n                \"SELECT token FROM user_permissions WHERE user_id = ?;\",\n            )\n            .bind(sqluser.id)\n            .fetch_all(pool)\n            .await\n            .ok()?;\n\n            Some(sqluser.into_user(Some(sql_user_perms)))\n        }\n\n        pub async fn get_from_username(\n            name: String,\n            pool: &SqlitePool,\n        ) -> Option<Self> {\n            User::get_from_username_with_passhash(name, pool)\n                .await\n                .map(|(user, _)| user)\n        }\n    }\n\n    #[derive(sqlx::FromRow, Clone)]\n    pub struct SqlPermissionTokens {\n        pub token: String,\n    }\n\n    #[async_trait]\n    impl Authentication<User, i64, SqlitePool> for User {\n        async fn load_user(\n            userid: i64,\n            pool: Option<&SqlitePool>,\n        ) -> Result<User, anyhow::Error> {\n            let pool = pool.unwrap();\n\n            User::get(userid, pool)\n                .await\n                .ok_or_else(|| anyhow::anyhow!(\"Cannot get user\"))\n        }\n\n        fn is_authenticated(&self) -> bool {\n            true\n        }\n\n        fn is_active(&self) -> bool {\n            true\n        }\n\n        fn is_anonymous(&self) -> bool {\n            false\n        }\n    }\n\n    #[async_trait]\n    impl HasPermission<SqlitePool> for User {\n        async fn has(&self, perm: &str, _pool: &Option<&SqlitePool>) -> bool {\n            self.permissions.contains(perm)\n        }\n    }\n\n    #[derive(sqlx::FromRow, Clone)]\n    pub struct SqlUser {\n        pub id: i64,\n        pub username: String,\n        pub password: String,\n    }\n\n    impl SqlUser {\n        pub fn into_user(\n            self,\n            sql_user_perms: Option<Vec<SqlPermissionTokens>>,\n        ) -> (User, UserPasshash) {\n            (\n                User {\n                    id: self.id,\n                    username: self.username,\n                    permissions: if let Some(user_perms) = sql_user_perms {\n                        user_perms\n                            .into_iter()\n                            .map(|x| x.token)\n                            .collect::<HashSet<String>>()\n                    } else {\n                        HashSet::<String>::new()\n                    },\n                },\n                UserPasshash(self.password),\n            )\n        }\n    }\n}\n\n#[server]\npub async fn foo() -> Result<String, ServerFnError> {\n    Ok(String::from(\"Bar!\"))\n}\n\n#[server]\npub async fn get_user() -> Result<Option<User>, ServerFnError> {\n    use crate::todo::ssr::auth;\n\n    let auth = auth().await?;\n\n    Ok(auth.current_user)\n}\n\n#[server(Login, \"/api\")]\npub async fn login(\n    username: String,\n    password: String,\n    remember: Option<String>,\n) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n\n    let pool = pool()?;\n    let auth = auth().await?;\n\n    let (user, UserPasshash(expected_passhash)) =\n        User::get_from_username_with_passhash(username, &pool)\n            .await\n            .ok_or_else(|| ServerFnError::new(\"User does not exist.\"))?;\n\n    match verify(password, &expected_passhash)? {\n        true => {\n            auth.login_user(user.id);\n            auth.remember_user(remember.is_some());\n            leptos_axum::redirect(\"/\");\n            Ok(())\n        }\n        false => Err(ServerFnError::ServerError(\n            \"Password does not match.\".to_string(),\n        )),\n    }\n}\n\n#[server(Signup, \"/api\")]\npub async fn signup(\n    username: String,\n    password: String,\n    password_confirmation: String,\n    remember: Option<String>,\n) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n\n    let pool = pool()?;\n    let auth = auth().await?;\n\n    if password != password_confirmation {\n        return Err(ServerFnError::ServerError(\n            \"Passwords did not match.\".to_string(),\n        ));\n    }\n\n    let password_hashed = hash(password, DEFAULT_COST).unwrap();\n\n    sqlx::query(\"INSERT INTO users (username, password) VALUES (?,?)\")\n        .bind(username.clone())\n        .bind(password_hashed)\n        .execute(&pool)\n        .await?;\n\n    let user =\n        User::get_from_username(username, &pool)\n            .await\n            .ok_or_else(|| {\n                ServerFnError::new(\"Signup failed: User does not exist.\")\n            })?;\n\n    auth.login_user(user.id);\n    auth.remember_user(remember.is_some());\n\n    leptos_axum::redirect(\"/\");\n\n    Ok(())\n}\n\n#[server(Logout, \"/api\")]\npub async fn logout() -> Result<(), ServerFnError> {\n    use self::ssr::*;\n\n    let auth = auth().await?;\n\n    auth.logout_user();\n    leptos_axum::redirect(\"/\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "projects/session_auth_axum/src/error_template.rs",
    "content": "use crate::errors::TodoAppError;\nuse leptos::prelude::*;\n#[cfg(feature = \"ssr\")]\nuse leptos_axum::ResponseOptions;\n\n// A basic function to display errors served by the error boundaries. Feel free to do more complicated things\n// here than just displaying them\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional)] errors: Option<ArcRwSignal<Errors>>,\n) -> impl IntoView {\n    let errors = match outside_errors {\n        Some(e) => ArcRwSignal::new(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n\n    // Get Errors from Signal\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors: Vec<TodoAppError> = errors\n        .get()\n        .into_iter()\n        .filter_map(|(_, v)| v.downcast_ref::<TodoAppError>().cloned())\n        .collect();\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    #[cfg(feature = \"ssr\")]\n    {\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors[0].status_code());\n        }\n    }\n\n    view! {\n        <h1>\"Errors\"</h1>\n        <For\n            // a function that returns the items we're iterating over; a signal is fine\n            each=move || { errors.clone().into_iter().enumerate() }\n            // a unique key for each item as a reference\n            key=|(index, _error)| *index\n            // renders each item to a view\n            children=move |error| {\n                let error_string = error.1.to_string();\n                let error_code = error.1.status_code();\n                view! {\n                    <h2>{error_code.to_string()}</h2>\n                    <p>\"Error: \" {error_string}</p>\n                }\n            }\n        />\n    }\n}\n"
  },
  {
    "path": "projects/session_auth_axum/src/errors.rs",
    "content": "use http::status::StatusCode;\nuse thiserror::Error;\n\n#[derive(Debug, Clone, Error)]\npub enum TodoAppError {\n    #[error(\"Not Found\")]\n    NotFound,\n    #[error(\"Internal Server Error\")]\n    InternalServerError,\n}\n\nimpl TodoAppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            TodoAppError::NotFound => StatusCode::NOT_FOUND,\n            TodoAppError::InternalServerError => {\n                StatusCode::INTERNAL_SERVER_ERROR\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "projects/session_auth_axum/src/lib.rs",
    "content": "pub mod auth;\npub mod error_template;\npub mod errors;\n#[cfg(feature = \"ssr\")]\npub mod state;\npub mod todo;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::todo::*;\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n\n    leptos::mount::hydrate_body(TodoApp);\n}\n"
  },
  {
    "path": "projects/session_auth_axum/src/main.rs",
    "content": "use axum::Router;\nuse axum_session::{SessionConfig, SessionLayer, SessionStore};\nuse axum_session_auth::{AuthConfig, AuthSessionLayer};\nuse axum_session_sqlx::SessionSqlitePool;\nuse leptos::{config::get_configuration, logging::log};\nuse leptos_axum::{generate_route_list, LeptosRoutes};\nuse session_auth_axum::{auth::User, state::AppState, todo::*};\nuse sqlx::{sqlite::SqlitePoolOptions, SqlitePool};\n\n#[tokio::main]\nasync fn main() {\n    simple_logger::init_with_level(log::Level::Info)\n        .expect(\"couldn't initialize logging\");\n\n    let pool = SqlitePoolOptions::new()\n        .connect(\"sqlite:Todos.db\")\n        .await\n        .expect(\"Could not make pool.\");\n\n    // Auth section\n    let session_config =\n        SessionConfig::default().with_table_name(\"axum_sessions\");\n    let auth_config = AuthConfig::<i64>::default();\n    let session_store = SessionStore::<SessionSqlitePool>::new(\n        Some(SessionSqlitePool::from(pool.clone())),\n        session_config,\n    )\n    .await\n    .unwrap();\n\n    if let Err(e) = sqlx::migrate!().run(&pool).await {\n        eprintln!(\"{e:?}\");\n    }\n\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(TodoApp);\n\n    let app_state = AppState {\n        leptos_options,\n        pool: pool.clone(),\n        routes: routes.clone(),\n    };\n\n    // build our application with a route\n    let app = Router::new()\n        .leptos_routes(&app_state, routes, {\n            let options = app_state.leptos_options.clone();\n            move || shell(options.clone())\n        })\n        .fallback(leptos_axum::file_and_error_handler::<AppState, _>(shell))\n        .layer(\n            AuthSessionLayer::<User, i64, SessionSqlitePool, SqlitePool>::new(\n                Some(pool.clone()),\n            )\n            .with_config(auth_config),\n        )\n        .layer(SessionLayer::new(session_store))\n        .with_state(app_state);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    log!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n"
  },
  {
    "path": "projects/session_auth_axum/src/state.rs",
    "content": "use axum::extract::FromRef;\nuse leptos::prelude::LeptosOptions;\nuse leptos_axum::AxumRouteListing;\nuse sqlx::SqlitePool;\n\n/// This takes advantage of Axum's SubStates feature by deriving FromRef. This is the only way to have more than one\n/// item in Axum's State. Leptos requires you to have leptosOptions in your State struct for the leptos route handlers\n#[derive(FromRef, Debug, Clone)]\npub struct AppState {\n    pub leptos_options: LeptosOptions,\n    pub pool: SqlitePool,\n    pub routes: Vec<AxumRouteListing>,\n}\n"
  },
  {
    "path": "projects/session_auth_axum/src/todo.rs",
    "content": "use crate::{auth::*, error_template::ErrorTemplate};\nuse leptos::prelude::*;\nuse leptos_meta::*;\nuse leptos_router::{components::*, path};\nuse serde::{Deserialize, Serialize};\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct Todo {\n    id: u32,\n    user: Option<User>,\n    title: String,\n    created_at: String,\n    completed: bool,\n}\n\n#[cfg(feature = \"ssr\")]\npub mod ssr {\n    use super::Todo;\n    use crate::{\n        auth::{ssr::AuthSession, User},\n        state::AppState,\n    };\n    use leptos::prelude::*;\n    use sqlx::SqlitePool;\n\n    pub fn pool() -> Result<SqlitePool, ServerFnError> {\n        with_context::<AppState, _>(|state| state.pool.clone())\n            .ok_or_else(|| ServerFnError::ServerError(\"Pool missing.\".into()))\n    }\n\n    pub async fn auth() -> Result<AuthSession, ServerFnError> {\n        let auth = leptos_axum::extract().await?;\n        Ok(auth)\n    }\n\n    #[derive(sqlx::FromRow, Clone)]\n    pub struct SqlTodo {\n        id: u32,\n        user_id: i64,\n        title: String,\n        created_at: String,\n        completed: bool,\n    }\n\n    impl SqlTodo {\n        pub async fn into_todo(self, pool: &SqlitePool) -> Todo {\n            Todo {\n                id: self.id,\n                user: User::get(self.user_id, pool).await,\n                title: self.title,\n                created_at: self.created_at,\n                completed: self.completed,\n            }\n        }\n    }\n}\n\n#[server(GetTodos, \"/api\")]\npub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {\n    use self::ssr::{pool, SqlTodo};\n    use futures::future::join_all;\n\n    let pool = pool()?;\n\n    Ok(join_all(\n        sqlx::query_as::<_, SqlTodo>(\"SELECT * FROM todos\")\n            .fetch_all(&pool)\n            .await?\n            .iter()\n            .map(|todo: &SqlTodo| todo.clone().into_todo(&pool)),\n    )\n    .await)\n}\n\n#[server(AddTodo, \"/api\")]\npub async fn add_todo(title: String) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n\n    let user = get_user().await?;\n    let pool = pool()?;\n\n    let id = match user {\n        Some(user) => user.id,\n        None => -1,\n    };\n\n    // fake API delay\n    std::thread::sleep(std::time::Duration::from_millis(1250));\n\n    Ok(sqlx::query(\n        \"INSERT INTO todos (title, user_id, completed) VALUES (?, ?, false)\",\n    )\n    .bind(title)\n    .bind(id)\n    .execute(&pool)\n    .await\n    .map(|_| ())?)\n}\n\n// The struct name and path prefix arguments are optional.\n#[server]\npub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {\n    use self::ssr::*;\n\n    let pool = pool()?;\n\n    Ok(sqlx::query(\"DELETE FROM todos WHERE id = $1\")\n        .bind(id)\n        .execute(&pool)\n        .await\n        .map(|_| ())?)\n}\n\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options/>\n                <link rel=\"stylesheet\" id=\"leptos\" href=\"/pkg/session_auth_axum.css\"/>\n                <link rel=\"shortcut icon\" type=\"image/ico\" href=\"/favicon.ico\"/>\n                <MetaTags/>\n            </head>\n            <body>\n                <TodoApp/>\n            </body>\n        </html>\n    }\n}\n\n#[component]\npub fn TodoApp() -> impl IntoView {\n    let login = ServerAction::<Login>::new();\n    let logout = ServerAction::<Logout>::new();\n    let signup = ServerAction::<Signup>::new();\n\n    let user = Resource::new(\n        move || {\n            (\n                login.version().get(),\n                signup.version().get(),\n                logout.version().get(),\n            )\n        },\n        move |_| get_user(),\n    );\n    provide_meta_context();\n\n    view! {\n        <Router>\n            <header>\n                <A href=\"/\">\n                    <h1>\"My Tasks\"</h1>\n                </A>\n                <Transition fallback=move || {\n                    view! { <span>\"Loading...\"</span> }\n                }>\n                    {move || {\n                        user.get()\n                            .map(|user| match user {\n                                Err(e) => {\n                                    view! {\n                                        <A href=\"/signup\">\"Signup\"</A>\n                                        \", \"\n                                        <A href=\"/login\">\"Login\"</A>\n                                        \", \"\n                                        <span>{format!(\"Login error: {e}\")}</span>\n                                    }\n                                        .into_any()\n                                }\n                                Ok(None) => {\n                                    view! {\n                                        <A href=\"/signup\">\"Signup\"</A>\n                                        \", \"\n                                        <A href=\"/login\">\"Login\"</A>\n                                        \", \"\n                                        <span>\"Logged out.\"</span>\n                                    }\n                                        .into_any()\n                                }\n                                Ok(Some(user)) => {\n                                    view! {\n                                        <A href=\"/settings\">\"Settings\"</A>\n                                        \", \"\n                                        <span>\n                                            {format!(\"Logged in as: {} ({})\", user.username, user.id)}\n                                        </span>\n                                    }\n                                        .into_any()\n                                }\n                            })\n                    }}\n\n                </Transition>\n            </header>\n            <hr/>\n            <main>\n                <FlatRoutes fallback=|| \"Not found.\">\n                    // Route\n                    <Route path=path!(\"\") view=Todos/>\n                    <Route path=path!(\"signup\") view=move || view! { <Signup action=signup/> }/>\n                    <Route path=path!(\"login\") view=move || view! { <Login action=login/> }/>\n                    <ProtectedRoute\n                        path=path!(\"settings\")\n                        condition=move || user.get().map(|r| r.ok().flatten().is_some())\n                        redirect_path=|| \"/\"\n                        view=move || {\n                            view! {\n                                <h1>\"Settings\"</h1>\n                                <Logout action=logout/>\n                            }\n                        }\n                    />\n\n                </FlatRoutes>\n            </main>\n        </Router>\n    }\n}\n\n#[component]\npub fn Todos() -> impl IntoView {\n    let add_todo = ServerMultiAction::<AddTodo>::new();\n    let delete_todo = ServerAction::<DeleteTodo>::new();\n    let submissions = add_todo.submissions();\n\n    // list of todos is loaded from the server in reaction to changes\n    let todos = Resource::new(\n        move || (add_todo.version().get(), delete_todo.version().get()),\n        move |_| get_todos(),\n    );\n\n    view! {\n        <div>\n            <MultiActionForm action=add_todo>\n                <label>\"Add a Todo\" <input type=\"text\" name=\"title\"/></label>\n                <input type=\"submit\" value=\"Add\"/>\n            </MultiActionForm>\n            <Transition fallback=move || view! { <p>\"Loading...\"</p> }>\n                <ErrorBoundary fallback=|errors| {\n                    view! { <ErrorTemplate errors=errors/> }\n                }>\n                    {move || {\n                        let existing_todos = {\n                            move || {\n                                todos\n                                    .get()\n                                    .map(move |todos| match todos {\n                                        Err(e) => {\n                                            view! {\n                                                <pre class=\"error\">\"Server Error: \" {e.to_string()}</pre>\n                                            }\n                                                .into_any()\n                                        }\n                                        Ok(todos) => {\n                                            if todos.is_empty() {\n                                                view! { <p>\"No tasks were found.\"</p> }.into_any()\n                                            } else {\n                                                todos\n                                                    .into_iter()\n                                                    .map(move |todo| {\n                                                        view! {\n                                                            <li>\n                                                                {todo.title} \": Created at \" {todo.created_at} \" by \"\n                                                                {todo.user.unwrap_or_default().username}\n                                                                <ActionForm action=delete_todo>\n                                                                    <input type=\"hidden\" name=\"id\" value=todo.id/>\n                                                                    <input type=\"submit\" value=\"X\"/>\n                                                                </ActionForm>\n                                                            </li>\n                                                        }\n                                                    })\n                                                    .collect_view()\n                                                    .into_any()\n                                            }\n                                        }\n                                    })\n                                    .unwrap_or(().into_any())\n                            }\n                        };\n                        let pending_todos = move || {\n                            submissions\n                                .get()\n                                .into_iter()\n                                .filter(|submission| submission.pending().get())\n                                .map(|submission| {\n                                    view! {\n                                        <li class=\"pending\">\n                                            {move || submission.input().get().map(|data| data.title)}\n                                        </li>\n                                    }\n                                })\n                                .collect_view()\n                        };\n                        view! { <ul>{existing_todos} {pending_todos}</ul> }\n                    }}\n\n                </ErrorBoundary>\n            </Transition>\n        </div>\n    }\n}\n\n#[component]\npub fn Login(action: ServerAction<Login>) -> impl IntoView {\n    view! {\n        <ActionForm action=action>\n            <h1>\"Log In\"</h1>\n            <label>\n                \"User ID:\"\n                <input\n                    type=\"text\"\n                    placeholder=\"User ID\"\n                    maxlength=\"32\"\n                    name=\"username\"\n                    class=\"auth-input\"\n                />\n            </label>\n            <br/>\n            <label>\n                \"Password:\"\n                <input type=\"password\" placeholder=\"Password\" name=\"password\" class=\"auth-input\"/>\n            </label>\n            <br/>\n            <label>\n                <input type=\"checkbox\" name=\"remember\" class=\"auth-input\"/>\n                \"Remember me?\"\n            </label>\n            <br/>\n            <button type=\"submit\" class=\"button\">\n                \"Log In\"\n            </button>\n        </ActionForm>\n    }\n}\n\n#[component]\npub fn Signup(action: ServerAction<Signup>) -> impl IntoView {\n    view! {\n        <ActionForm action=action>\n            <h1>\"Sign Up\"</h1>\n            <label>\n                \"User ID:\"\n                <input\n                    type=\"text\"\n                    placeholder=\"User ID\"\n                    maxlength=\"32\"\n                    name=\"username\"\n                    class=\"auth-input\"\n                />\n            </label>\n            <br/>\n            <label>\n                \"Password:\"\n                <input type=\"password\" placeholder=\"Password\" name=\"password\" class=\"auth-input\"/>\n            </label>\n            <br/>\n            <label>\n                \"Confirm Password:\"\n                <input\n                    type=\"password\"\n                    placeholder=\"Password again\"\n                    name=\"password_confirmation\"\n                    class=\"auth-input\"\n                />\n            </label>\n            <br/>\n            <label>\n                \"Remember me?\" <input type=\"checkbox\" name=\"remember\" class=\"auth-input\"/>\n            </label>\n\n            <br/>\n            <button type=\"submit\" class=\"button\">\n                \"Sign Up\"\n            </button>\n        </ActionForm>\n    }\n}\n\n#[component]\npub fn Logout(action: ServerAction<Logout>) -> impl IntoView {\n    view! {\n        <div id=\"loginbox\">\n            <ActionForm action=action>\n                <button type=\"submit\" class=\"button\">\n                    \"Log Out\"\n                </button>\n            </ActionForm>\n        </div>\n    }\n}\n"
  },
  {
    "path": "projects/session_auth_axum/style.css",
    "content": ".pending {\n\tcolor: purple;\n}\n\na {\n\tcolor: black;\n}"
  },
  {
    "path": "projects/sitemap_axum/.gitignore",
    "content": ".env\nCargo.lock\n"
  },
  {
    "path": "projects/sitemap_axum/Cargo.toml",
    "content": "[workspace]\n# The empty workspace here is to keep rust-analyzer satisfied\n\n[package]\nname = \"sitemap-axum\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\naxum = { version = \"0.7.5\", optional = true }\nconsole_error_panic_hook = \"0.1.7\"\nleptos = { version = \"0.6.13\", features = [\"nightly\"] }\nleptos_axum = { version = \"0.6.13\", optional = true }\nleptos_meta = { version = \"0.6.13\", features = [\"nightly\"] }\nleptos_router = { version = \"0.6.13\", features = [\"nightly\"] }\ntokio = { version = \"1.39\", features = [\"rt-multi-thread\"], optional = true }\ntower = { version = \"0.4\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\"], optional = true }\nwasm-bindgen = \"0.2.92\"\nthiserror = \"1.0\"\ntracing = { version = \"0.1.40\", optional = true }\nhttp = \"1.1\"\n\n# Example specific crates\nsqlx = { version = \"0.8.0\", features = [\n  \"postgres\",\n  \"runtime-tokio\",\n  \"tls-rustls\",\n  \"time\",\n], optional = true }\nxml = { version = \"0.8.20\", optional = true }\ntime = { version = \"0.3.36\", features = [\"macros\", \"serde\", \"formatting\"] }\ndotenvy = \"0.15.7\"\nanyhow = \"1.0\"\n\n[features]\nhydrate = [\"leptos/hydrate\", \"leptos_meta/hydrate\", \"leptos_router/hydrate\"]\nssr = [\n  \"dep:axum\",\n  \"dep:tokio\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:leptos_axum\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:tracing\",\n  \"dep:sqlx\",\n  \"dep:xml\",\n]\n\n# Defines a size-optimized profile for the WASM bundle in release mode\n[profile.wasm-release]\ninherits = \"release\"\nopt-level = 'z'\nlto = true\ncodegen-units = 1\npanic = \"abort\"\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"sitemap-axum\"\n\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\n# style-file = \"style/main.scss\"\n\n# Assets source dir. All files found here will be copied and synchronized to site-root.\n# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.\n#\n# Optional. Env: LEPTOS_ASSETS_DIR.\nassets-dir = \"public\"\n\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n\n# The port to use for automatic reload monitoring\nreload-port = 3001\n\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\n#   [Windows] for non-WSL use \"npx.cmd playwright test\"\n#   This binary name can be checked in Powershell with Get-Command npx\n# end2end-cmd = \"npx playwright test\"\n# end2end-dir = \"end2end\"\n\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n\n# The profile to use for the lib target when compiling for release\n#\n# Optional. Defaults to \"release\".\nlib-profile-release = \"wasm-release\"\n"
  },
  {
    "path": "projects/sitemap_axum/LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  },
  {
    "path": "projects/sitemap_axum/Makefile.toml",
    "content": "[tasks.run]\ncommand = \"cargo\"\nargs = [\"leptos\", \"watch\"]\ndependencies = [\"start-db\"]\n\n[tasks.start-db]\ncommand = \"docker\"\nargs = [\"start\", \"blog_db\"]\n\n[tasks.run-db]\ncommand = \"docker\"\nargs = [\n    \"run\",\n    \"-d\",\n    \"--name\",\n    \"blog_db\",\n    \"-p\",\n    \"5432:5432\",\n    \"--env-file\",\n    \"./.env\",\n    \"-v\",\n    \"./init:/docker-entrypoint-initdb.d\",\n    \"postgres:latest\",\n]\n\n[tasks.stop-db]\ncommand = \"docker\"\nargs = [\"stop\", \"blog_db\"]\n\n[tasks.drop-db]\ncommand = \"docker\"\nargs = [\"rm\", \"blog_db\"]\ndependencies = [\"stop-db\"]\n\n[tasks.restart-db]\ndependencies = [\"drop-db\", \"start-db\"]\n"
  },
  {
    "path": "projects/sitemap_axum/README.md",
    "content": "# Sitemaps with Axum\n\nThis project demonstrates how to serve a [sitemap](https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview) file using Axum using dynamic data (like blog posts in this case). An example Postgres database is used data source for storing blog post data that can be used to generate a dynamic site map based on blog post slugs. There's lots of [sitemap crates](https://crates.io/search?q=sitemap), though this example uses the [xml](https://crates.io/crates/xml) for example purposes.\n\n## Quick Start\n\nWe use Docker to provide a Postgres database for this sample, so make sure you have it installed.\n\n```sh\n$ docker -v\nDocker version 25.0.3, build 4debf41\n```\n\nOnce Docker has started on you local machine, run (make sure to have `cargo-make` installed):\n\n```sh\n$ cargo make run\n```\n\nThis will handle spinning up a Postgres container, initializing the example database, and launching the local dev server.\n"
  },
  {
    "path": "projects/sitemap_axum/init/schema.sql",
    "content": "-- The database initialization script is used for defining your local schema as well as postgres\n-- running within a docker container, where we'll copy this file over and run on startup\n\nDO\n$$\n    BEGIN\n        IF\n            NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'blogs') THEN\n            CREATE DATABASE blogs;\n        END IF;\n    END\n$$;\n\n\\c blogs;\n\nDROP TABLE IF EXISTS posts;\nCREATE TABLE posts\n(\n    id SERIAL PRIMARY KEY,\n    slug VARCHAR(255) UNIQUE NOT NULL,\n    title VARCHAR(255) NOT NULL,\n    content VARCHAR(255) NOT NULL,\n    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nINSERT INTO posts (slug, title, content)\nVALUES ('first-post', 'First Post', 'This is the content of the first post.'),\n       ('second-post', 'Second Post', 'Here is some more content for another post.'),\n       ('hello-world', 'Hello World', 'Yet another post to add to our collection.'),\n       ('tech-talk', 'Tech Talk', 'Discussing the latest in technology.'),\n       ('travel-diaries', 'Travel Diaries', 'Sharing my experiences traveling around the world.');"
  },
  {
    "path": "projects/sitemap_axum/sitemap-index.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\" xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\" xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\">\n  <url>\n    <loc>https://mywebsite.com/blog/first-post</loc>\n    <lastmod>2024-04-23T17:28:07Z</lastmod>\n    <changefreq>yearly</changefreq>\n    <priority>0.5</priority>\n  </url>\n  <url>\n    <loc>https://mywebsite.com/blog/second-post</loc>\n    <lastmod>2024-04-23T17:28:07Z</lastmod>\n    <changefreq>yearly</changefreq>\n    <priority>0.5</priority>\n  </url>\n  <url>\n    <loc>https://mywebsite.com/blog/hello-world</loc>\n    <lastmod>2024-04-23T17:28:07Z</lastmod>\n    <changefreq>yearly</changefreq>\n    <priority>0.5</priority>\n  </url>\n  <url>\n    <loc>https://mywebsite.com/blog/tech-talk</loc>\n    <lastmod>2024-04-23T17:28:07Z</lastmod>\n    <changefreq>yearly</changefreq>\n    <priority>0.5</priority>\n  </url>\n  <url>\n    <loc>https://mywebsite.com/blog/travel-diaries</loc>\n    <lastmod>2024-04-23T17:28:07Z</lastmod>\n    <changefreq>yearly</changefreq>\n    <priority>0.5</priority>\n  </url>\n  <url>\n    <loc>https://mywebsite.com</loc>\n    <changefreq>weekly</changefreq>\n    <priority>0.8</priority>\n  </url>\n</urlset>"
  },
  {
    "path": "projects/sitemap_axum/sitemap-static.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\" xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\" xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\">\n  <url>\n    <loc>https://mywebsite.com</loc>\n    <changefreq>weekly</changefreq>\n    <priority>0.8</priority>\n  </url>\n  <url>\n    <loc>https://mywebsite.com/about</loc>\n    <changefreq>yearly</changefreq>\n    <priority>0.5</priority>\n  </url>\n</urlset>\n"
  },
  {
    "path": "projects/sitemap_axum/src/app.rs",
    "content": "use crate::error_template::{AppError, ErrorTemplate};\nuse leptos::*;\nuse leptos_meta::*;\nuse leptos_router::*;\n\n#[component]\npub fn App() -> impl IntoView {\n    // Provides context that manages stylesheets, titles, meta tags, etc.\n    provide_meta_context();\n\n    view! {\n\n\n        // injects a stylesheet into the document <head>\n        // id=leptos means cargo-leptos will hot-reload this stylesheet\n        <Stylesheet id=\"leptos\" href=\"/pkg/sitemap-axum.css\"/>\n\n        // sets the document title\n        <Title text=\"Welcome to Leptos\"/>\n\n        // content for this welcome page\n        <Router fallback=|| {\n            let mut outside_errors = Errors::default();\n            outside_errors.insert_with_default_key(AppError::NotFound);\n            view! {\n                <ErrorTemplate outside_errors/>\n            }\n            .into_view()\n        }>\n            <main>\n                <Routes>\n                    <Route path=\"\" view=HomePage/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n/// Renders the home page of your application.\n#[component]\nfn HomePage() -> impl IntoView {\n    view! {\n        <h1>\"Welcome to Leptos!\"</h1>\n        // Typically, you won't route to these files manually - a crawler of sorts will take care of that\n        <a href=\"http://localhost:3000/sitemap-index.xml\">\"Generate dynamic sitemap\"</a>\n        <a style=\"padding-left: 1em;\" href=\"http://localhost:3000/sitemap-static.xml\">\"Go to static sitemap\"</a>\n    }\n}\n"
  },
  {
    "path": "projects/sitemap_axum/src/error_template.rs",
    "content": "use http::status::StatusCode;\nuse leptos::*;\nuse thiserror::Error;\n\n#[derive(Clone, Debug, Error)]\npub enum AppError {\n    #[error(\"Not Found\")]\n    NotFound,\n}\n\nimpl AppError {\n    pub fn status_code(&self) -> StatusCode {\n        match self {\n            AppError::NotFound => StatusCode::NOT_FOUND,\n        }\n    }\n}\n\n// A basic function to display errors served by the error boundaries.\n// Feel free to do more complicated things here than just displaying the error.\n#[component]\npub fn ErrorTemplate(\n    #[prop(optional)] outside_errors: Option<Errors>,\n    #[prop(optional)] errors: Option<RwSignal<Errors>>,\n) -> impl IntoView {\n    let errors = match outside_errors {\n        Some(e) => create_rw_signal(e),\n        None => match errors {\n            Some(e) => e,\n            None => panic!(\"No Errors found and we expected errors!\"),\n        },\n    };\n    // Get Errors from Signal\n    let errors = errors.get_untracked();\n\n    // Downcast lets us take a type that implements `std::error::Error`\n    let errors: Vec<AppError> = errors\n        .into_iter()\n        .filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())\n        .collect();\n    println!(\"Errors: {errors:#?}\");\n\n    // Only the response code for the first error is actually sent from the server\n    // this may be customized by the specific application\n    #[cfg(feature = \"ssr\")]\n    {\n        use leptos_axum::ResponseOptions;\n        let response = use_context::<ResponseOptions>();\n        if let Some(response) = response {\n            response.set_status(errors[0].status_code());\n        }\n    }\n\n    view! {\n        <h1>{if errors.len() > 1 {\"Errors\"} else {\"Error\"}}</h1>\n        <For\n            // a function that returns the items we're iterating over; a signal is fine\n            each= move || {errors.clone().into_iter().enumerate()}\n            // a unique key for each item as a reference\n            key=|(index, _error)| *index\n            // renders each item to a view\n            children=move |error| {\n                let error_string = error.1.to_string();\n                let error_code= error.1.status_code();\n                view! {\n                    <h2>{error_code.to_string()}</h2>\n                    <p>\"Error: \" {error_string}</p>\n                }\n            }\n        />\n    }\n}\n"
  },
  {
    "path": "projects/sitemap_axum/src/fileserv.rs",
    "content": "use crate::app::App;\nuse axum::{\n    body::Body,\n    extract::State,\n    http::{Request, Response, StatusCode, Uri},\n    response::{IntoResponse, Response as AxumResponse},\n};\nuse leptos::*;\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\n\npub async fn file_and_error_handler(\n    uri: Uri,\n    State(options): State<LeptosOptions>,\n    req: Request<Body>,\n) -> AxumResponse {\n    let root = options.site_root.clone();\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        let handler =\n            leptos_axum::render_app_to_stream(options.to_owned(), App);\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n"
  },
  {
    "path": "projects/sitemap_axum/src/lib.rs",
    "content": "pub mod app;\npub mod error_template;\n#[cfg(feature = \"ssr\")]\npub mod fileserv;\n#[cfg(feature = \"ssr\")]\npub mod sitemap;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    use crate::app::*;\n    console_error_panic_hook::set_once();\n    leptos::mount_to_body(App);\n}\n"
  },
  {
    "path": "projects/sitemap_axum/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::{routing::get, Router};\n    use leptos::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use sitemap_axum::{\n        app::*, fileserv::file_and_error_handler, sitemap::generate_sitemap,\n    };\n    use tower_http::services::ServeFile;\n\n    // Setting get_configuration(None) means we'll be using cargo-leptos's env values\n    // For deployment these variables are:\n    // <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>\n    // Alternately a file can be specified such as Some(\"Cargo.toml\")\n    // The file would need to be included with the executable when moved to deployment\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(App);\n\n    // Build our application with a route\n    let app = Router::new()\n        // We can use Axum to mount a route that serves a sitemap file that we can generate with dynamic data\n        .route(\"/sitemap-index.xml\", get(generate_sitemap))\n        // Using tower's serve file service, we can also serve a static sitemap file for relatively small sites too\n        .route_service(\n            \"/sitemap-static.xml\",\n            ServeFile::new(\"sitemap-static.xml\"),\n        )\n        .leptos_routes(&leptos_options, routes, App)\n        .fallback(file_and_error_handler)\n        .with_state(leptos_options);\n\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    logging::log!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(not(feature = \"ssr\"))]\npub fn main() {\n    // no client-side main function\n    // unless we want this to work with e.g., Trunk for a purely client-side app\n    // see lib.rs for hydration function instead\n}\n"
  },
  {
    "path": "projects/sitemap_axum/src/sitemap.rs",
    "content": "use axum::{\n    body::Body,\n    response::{IntoResponse, Response},\n};\nuse sqlx::{PgPool, Pool, Postgres};\nuse std::{\n    env::{self, current_dir},\n    fs::File,\n    io::{BufWriter, Read},\n    path::Path,\n};\nuse time::{format_description, PrimitiveDateTime};\nuse xml::{writer::XmlEvent, EmitterConfig, EventWriter};\n\n#[derive(Debug)]\nstruct Post {\n    slug: String,\n    updated_at: PrimitiveDateTime,\n}\n\n/// Generates a sitemap based on data stored in a database containing slugs that we can use to build URLs to the posts themselves.\npub async fn generate_sitemap() -> impl IntoResponse {\n    dotenvy::dotenv().ok();\n\n    // Depending on your preference, we can dynamically servce the sitemap file for each,\n    // or generate the file once on the first visit (probably from a bot) and write it to disk\n    // so we can simply serve the created file instead of having to query the database every time\n    let sitemap_path = format!(\n        \"{}/sitemap-index.xml\",\n        &current_dir().unwrap().to_str().unwrap()\n    );\n    let path = Path::new(&sitemap_path);\n\n    // If the doesn't exist, we've probably deployed a fresh version of our Leptos site somewhere so we'll generate it on first request\n    if !path.exists() {\n        let pool = PgPool::connect(\n            &env::var(\"DATABASE_URL\").expect(\"database URL to exist\"),\n        )\n        .await\n        .expect(\"to be able to connect to pool\");\n\n        create_sitemap_file(path, pool).await.ok();\n    }\n\n    // Once the file has been written, grab the contents of it and write it out as an XML file in the response\n    let mut file = File::open(sitemap_path).unwrap();\n    let mut contents = vec![];\n    file.read_to_end(&mut contents).ok();\n    let body = Body::from(contents);\n\n    Response::builder()\n        .header(\"Content-Type\", \"application/xml\")\n        // Cache control can be helpful for cases where your site might be deployed occassionally and the original\n        // sitemap that was generated can be cached with a header\n        .header(\"Cache-Control\", \"max-age=86400\")\n        .body(body)\n        .unwrap()\n}\n\nasync fn create_sitemap_file(\n    path: &Path,\n    pool: Pool<Postgres>,\n) -> anyhow::Result<()> {\n    let file = File::create(path).expect(\"sitemap file to be created\");\n    let file = BufWriter::new(file);\n    let mut writer = EmitterConfig::new()\n        .perform_indent(true)\n        .create_writer(file);\n\n    writer\n        .write(\n            XmlEvent::start_element(\"urlset\")\n                .attr(\"xmlns\", \"http://www.sitemaps.org/schemas/sitemap/0.9\")\n                .attr(\"xmlns:xhtml\", \"http://www.w3.org/1999/xhtml\")\n                .attr(\n                    \"xmlns:image\",\n                    \"http://www.google.com/schemas/sitemap-image/1.1\",\n                )\n                .attr(\n                    \"xmlns:video\",\n                    \"http://www.google.com/schemas/sitemap-video/1.1\",\n                )\n                .attr(\n                    \"xmlns:news\",\n                    \"http://www.google.com/schemas/sitemap-news/0.9\",\n                ),\n        )\n        .expect(\"xml header to be written\");\n\n    // We could also pull this from configuration or an environment variable\n    let app_url = \"https://mywebsite.com\";\n\n    // First, read all the blog entries so we can get the slug for building,\n    //  URLs and the updated date to determine the change frequency\n    sqlx::query_as!(\n        Post,\n        r#\"\nSELECT slug,\n       updated_at\nFROM posts\nORDER BY updated_at DESC\n    \"#\n    )\n    .fetch_all(&pool)\n    .await\n    .expect(\"\")\n    .into_iter()\n    .try_for_each(|p| write_post_entry(p, app_url, &mut writer))?;\n\n    // Next, write the static pages and close the XML stream\n    write_static_page_entry(app_url, &mut writer)?;\n\n    writer.write(XmlEvent::end_element())?;\n\n    Ok(())\n}\n\nfn write_post_entry(\n    post: Post,\n    app_url: &str,\n    writer: &mut EventWriter<BufWriter<File>>,\n) -> anyhow::Result<()> {\n    let format = format_description::parse(\n        \"[year]-[month]-[day]T[hour]:[minute]:[second]Z\",\n    )?;\n    let parsed_date = post.updated_at.format(&format)?;\n    let route = format!(\"{}/blog/{}\", app_url, post.slug);\n\n    writer.write(XmlEvent::start_element(\"url\"))?;\n    writer.write(XmlEvent::start_element(\"loc\"))?;\n    writer.write(XmlEvent::characters(&route))?;\n    writer.write(XmlEvent::end_element())?;\n    writer.write(XmlEvent::start_element(\"lastmod\"))?;\n    writer.write(XmlEvent::characters(&parsed_date))?;\n    writer.write(XmlEvent::end_element())?;\n    writer.write(XmlEvent::start_element(\"changefreq\"))?;\n    writer.write(XmlEvent::characters(\"yearly\"))?;\n    writer.write(XmlEvent::end_element())?;\n    writer.write(XmlEvent::start_element(\"priority\"))?;\n    writer.write(XmlEvent::characters(\"0.5\"))?;\n    writer.write(XmlEvent::end_element())?;\n    writer.write(XmlEvent::end_element())?;\n\n    Ok(())\n}\n\nfn write_static_page_entry(\n    route: &str,\n    writer: &mut EventWriter<BufWriter<File>>,\n) -> anyhow::Result<()> {\n    write_entry(route, \"weekly\", \"0.8\", writer)?;\n    Ok(())\n}\n\nfn write_entry(\n    route: &str,\n    change_frequency: &str,\n    priority: &str,\n    writer: &mut EventWriter<BufWriter<File>>,\n) -> anyhow::Result<()> {\n    writer.write(XmlEvent::start_element(\"url\"))?;\n    writer.write(XmlEvent::start_element(\"loc\"))?;\n    writer.write(XmlEvent::characters(route))?;\n    writer.write(XmlEvent::end_element())?;\n    writer.write(XmlEvent::start_element(\"changefreq\"))?;\n    writer.write(XmlEvent::characters(change_frequency))?;\n    writer.write(XmlEvent::end_element())?;\n    writer.write(XmlEvent::start_element(\"priority\"))?;\n    writer.write(XmlEvent::characters(priority))?;\n    writer.write(XmlEvent::end_element())?;\n    writer.write(XmlEvent::end_element())?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "projects/sso_auth_axum/Cargo.toml",
    "content": "[package]\nname = \"sso_auth_axum\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\noauth2 = { version = \"4.4\", optional = true }\nanyhow = \"1.0\"\nconsole_log = \"1.0\"\nrand = { version = \"0.8.5\", features = [\"min_const_gen\"], optional = true }\nconsole_error_panic_hook = \"0.1.7\"\nfutures = \"0.3.25\"\nleptos = { path = \"../../leptos\" }\nleptos_meta = { path = \"../../meta\" }\nleptos_axum = { path = \"../../integrations/axum\", optional = true }\nleptos_router = { path = \"../../router\" }\nlog = \"0.4.17\"\nsimple_logger = \"5.0\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = { version = \"1.0\", optional = true }\naxum = { version = \"0.7.0\", optional = true, features = [\"macros\"] }\ntower = { version = \"0.4.0\", optional = true }\ntower-http = { version = \"0.5.0\", features = [\"fs\"], optional = true }\ntokio = { version = \"1.22\", features = [\"full\"], optional = true }\nhttp = { version = \"1\" }\nsqlx = { version = \"0.8.0\", features = [\n  \"runtime-tokio-rustls\",\n  \"sqlite\",\n], optional = true }\nthiserror = \"1.0\"\nwasm-bindgen = \"0.2.0\"\naxum_session_auth = { version = \"0.14.0\", features = [\n  \"sqlite-rustls\",\n], optional = true }\naxum_session = { version = \"0.14.0\", features = [\n  \"sqlite-rustls\",\n], optional = true }\nasync-trait = { version = \"0.1.64\", optional = true }\nreqwest = { version = \"0.12.0\", optional = true, features = [\"json\"] }\n\n[features]\nhydrate = [\"leptos/hydrate\", \"leptos_meta/hydrate\", \"leptos_router/hydrate\"]\nssr = [\n  \"dep:serde_json\",\n  \"dep:axum\",\n  \"dep:tower\",\n  \"dep:tower-http\",\n  \"dep:tokio\",\n  \"dep:reqwest\",\n  \"dep:oauth2\",\n  \"dep:axum_session_auth\",\n  \"dep:axum_session\",\n  \"dep:async-trait\",\n  \"dep:sqlx\",\n  \"dep:rand\",\n  \"leptos/ssr\",\n  \"leptos_meta/ssr\",\n  \"leptos_router/ssr\",\n  \"dep:leptos_axum\",\n]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"axum\", \"tower\", \"tower-http\", \"tokio\", \"sqlx\", \"leptos_axum\"]\nskip_feature_sets = [[\"csr\", \"ssr\"], [\"csr\", \"hydrate\"], [\"ssr\", \"hydrate\"]]\n\n[package.metadata.leptos]\n# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name\noutput-name = \"sso_auth_axum\"\n# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.\nsite-root = \"target/site\"\n# The site-root relative folder where all compiled output (JS, WASM and CSS) is written\n# Defaults to pkg\nsite-pkg-dir = \"pkg\"\n# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css\nstyle-file = \"./style.css\"\n# [Optional] Files in the asset-dir will be copied to the site-root directory\nassets-dir = \"public\"\n# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.\nsite-addr = \"127.0.0.1:3000\"\n# The port to use for automatic reload monitoring\nreload-port = 3001\n# [Optional] Command to use when running end2end tests. It will run in the end2end dir.\nend2end-cmd = \"npx playwright test\"\n#  The browserlist query used for optimizing the CSS.\nbrowserquery = \"defaults\"\n# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head\nwatch = false\n# The environment Leptos will run in, usually either \"DEV\" or \"PROD\"\nenv = \"DEV\"\n# The features to use when compiling the bin target\n#\n# Optional. Can be over-ridden with the command line parameter --bin-features\nbin-features = [\"ssr\"]\n\n# If the --no-default-features flag should be used when compiling the bin target\n#\n# Optional. Defaults to false.\nbin-default-features = false\n\n# The features to use when compiling the lib target\n#\n# Optional. Can be over-ridden with the command line parameter --lib-features\nlib-features = [\"hydrate\"]\n\n# If the --no-default-features flag should be used when compiling the lib target\n#\n# Optional. Defaults to false.\nlib-default-features = false\n"
  },
  {
    "path": "projects/sso_auth_axum/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Greg Johnston\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "projects/sso_auth_axum/Makefile.toml",
    "content": "extend = [\n\t{ path = \"../cargo-make/main.toml\" },\n\t{ path = \"../cargo-make/cargo-leptos.toml\" },\n]\n\n[env]\n\nCLIENT_PROCESS_NAME = \"sso_auth_axum\"\n"
  },
  {
    "path": "projects/sso_auth_axum/README.md",
    "content": "# Leptos SSO Authenticated Email Display App with Axum\n\n## Overview\nThis project demonstrates various methods of implementing Single Sign-On (SSO) authorization using OAuth, specifically with the OAuth2 library. The primary focus is on the Authorization Code Grant flow.\n\n### Process Flow\n1. **Initiating Sign-In:** When a user clicks the 'Sign In With {THIRD PARTY SERVICE}' button, the request is sent to a server function. This function retrieves an authorization URL from the third-party service.\n\n2. **CSRF Token Handling:** During the URL fetch, a CSRF_TOKEN is generated and confirmed by the service to mitigate Cross-Site Request Forgery attacks. Learn more about CSRF [here](https://en.wikipedia.org/wiki/Cross-site_request_forgery). This token is stored on our server.\n\n3. **User Redirection:** Post-login, users are redirected to our server with a URL formatted as follows:  \n`http://your-redirect-uri.com/callback?code=AUTHORIZATION_CODE&state=CSRF_TOKEN`\nNote: Additional parameters like Scope and Client_ID may be included by the service.\n\n4. **Token Acquisition:** The 'code' parameter in the URL is not the actual service token. Instead, it's used to fetch the token. We verify the CSRF_TOKEN in the URL against our server's stored token for security.\n\n5. **Access Token Usage:** With a valid CSRF_TOKEN, we use the AUTHORIZATION_CODE in an HTTP Request to the third-party service. The response typically includes:\n- An `access token`\n- An `expires_in` value (time in seconds until token expiration)\n- A `refresh token` (used to renew the access token)\n\n6. **Email Retrieval and Display:** The access token allows us to retrieve the user's email. This email is then displayed in our Email Display App.\n\n7. **Session Management:** The `expires_in` value is sent to the client. The client uses this to set a timeout, ensuring that if the session is still active (the window hasn't been closed), it automatically triggers a token refresh when required.\n\n\n\n## Client Side Rendering\nThis example cannot be built as a trunk standalone CSR-only app. Only the server may directly connect to the database.\n\n## Server Side Rendering with cargo-leptos\ncargo-leptos is now the easiest and most featureful way to build server side rendered apps with hydration. It provides automatic recompilation of client and server code, wasm optimisation, CSS minification, and more! Check out more about it [here](https://github.com/akesson/cargo-leptos)\n\n## Env Vars\nCommands that run the program, cargo leptos watch, cargo leptos serve, cargo run etc... All need the following Environment variables\nG_AUTH_CLIENT_ID : This is the client ID given to you by google.\nG_AUTH_SECRET : This is the secret given to you by google.\nNGROK : this is the ngrok endpoint you get when you run ngrok http 3000\n\n## Ngrok Google Set Up\nAfter running your app, run\n```bash\nngrok http 3000\n```\nThen use google api's and services, go to credentials, create credentials, add your app name, and use the ngrok url as the origin\nand use the ngrok url with /g_auth as the redirect url. That will look like this `https://362b-24-34-20-189.ngrok-free.app/g_auth`\nSave you client ID and secret given to you by google. Use them as Envars when you run the program as below\n```bash\nREDIRECT_URL={ngrok_redirect_url} G_AUTH_CLIENT_ID={google_credential_client_id} G_AUTH_SECRET={google_credential_secret} {your command here...}\n```\n\n1. Install cargo-leptos\n```bash\ncargo install --locked cargo-leptos\n``` \n2. Build the site in watch mode, recompiling on file changes\n```bash\ncargo leptos watch\n```\n\nOpen browser on [http://localhost:3000/](http://localhost:3000/)\n\n3. When ready to deploy, run\n```bash\ncargo leptos build --release\n```\n\n## Server Side Rendering without cargo-leptos\nTo run it as a server side app with hydration, you'll need to have wasm-pack installed.\n\n0. Edit the `[package.metadata.leptos]` section and set `site-root` to `\".\"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.\n1. Install wasm-pack\n```bash\ncargo install wasm-pack\n```\n2. Build the Webassembly used to hydrate the HTML from the server\n```bash\nwasm-pack build --target=web --debug --no-default-features --features=hydrate\n```\n3. Run the server to serve the Webassembly, JS, and HTML \n```bash\ncargo run --no-default-features --features=ssr\n```\n"
  },
  {
    "path": "projects/sso_auth_axum/flake.nix",
    "content": "{\n  inputs.nixpkgs.url = \"github:NixOS/nixpkgs/nixos-22.05\";\n  inputs.rust-overlay.url = \"github:oxalica/rust-overlay\";\n  inputs.rust-overlay.inputs.nixpkgs.follows = \"nixpkgs\";\n  inputs.flake-utils.url = \"github:numtide/flake-utils\";\n\n  outputs = { self, nixpkgs, rust-overlay, flake-utils }:\n    flake-utils.lib.eachDefaultSystem (system:\n      let\n        pkgs = import nixpkgs {\n          inherit system;\n          overlays = [ (import rust-overlay) ];\n        };\n      in\n        with pkgs; rec {\n          devShells.default = mkShell {\n\n            shellHook = ''\n              export PKG_CONFIG_PATH=\"${pkgs.openssl.dev}/lib/pkgconfig\";\n            '';\n\n            nativeBuildInputs = [\n              pkg-config\n            ];\n            buildInputs = [\n              trunk\n              sqlite\n              sass\n              openssl\n              (rust-bin.nightly.latest.default.override {\n                extensions = [ \"rust-src\" ];\n                targets = [ \"wasm32-unknown-unknown\" ];\n              })\n            ];\n          };\n        });\n}"
  },
  {
    "path": "projects/sso_auth_axum/migrations/20231217000000_create_tables.sql",
    "content": "PRAGMA foreign_keys = ON;\n\nCREATE TABLE IF NOT EXISTS users (\n  id         INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n  email      TEXT NOT NULL UNIQUE,\n  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS user_permissions (\n    user_id  INTEGER NOT NULL,\n    token    TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS csrf_tokens (\n    csrf_token TEXT NOT NULL PRIMARY KEY UNIQUE\n);\n\nCREATE TABLE IF NOT EXISTS google_tokens (\n  id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n  user_id INTEGER NOT NULL UNIQUE,\n  access_secret TEXT NOT NULL,\n  refresh_secret TEXT NOT NULL,\n  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n  FOREIGN KEY (user_id) REFERENCES users(id)\n);\n\n"
  },
  {
    "path": "projects/sso_auth_axum/src/auth.rs",
    "content": "use serde::{Deserialize, Serialize};\nuse std::collections::HashSet;\n\n#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct User {\n    pub id: i64,\n    pub email: String,\n    pub permissions: HashSet<String>,\n}\n\nimpl Default for User {\n    fn default() -> Self {\n        let permissions = HashSet::new();\n\n        Self {\n            id: -1,\n            email: \"example@example.com\".into(),\n            permissions,\n        }\n    }\n}\n\n#[cfg(feature = \"ssr\")]\npub mod ssr_imports {\n    use super::User;\n    pub use axum_session_auth::{\n        Authentication, HasPermission, SessionSqlitePool,\n    };\n    pub use sqlx::SqlitePool;\n    use std::collections::HashSet;\n    pub type AuthSession = axum_session_auth::AuthSession<\n        User,\n        i64,\n        SessionSqlitePool,\n        SqlitePool,\n    >;\n\n    use async_trait::async_trait;\n\n    impl User {\n        pub async fn get(id: i64, pool: &SqlitePool) -> Option<Self> {\n            let sqluser = sqlx::query_as::<_, SqlUser>(\n                \"SELECT * FROM users WHERE id = ?\",\n            )\n            .bind(id)\n            .fetch_one(pool)\n            .await\n            .ok()?;\n\n            //lets just get all the tokens the user can use, we will only use the full permissions if modifying them.\n            let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(\n                \"SELECT token FROM user_permissions WHERE user_id = ?;\",\n            )\n            .bind(id)\n            .fetch_all(pool)\n            .await\n            .ok()?;\n\n            Some(sqluser.into_user(Some(sql_user_perms)))\n        }\n\n        pub async fn get_from_email(\n            email: &str,\n            pool: &SqlitePool,\n        ) -> Option<Self> {\n            let sqluser = sqlx::query_as::<_, SqlUser>(\n                \"SELECT * FROM users WHERE email = ?\",\n            )\n            .bind(email)\n            .fetch_one(pool)\n            .await\n            .ok()?;\n\n            //lets just get all the tokens the user can use, we will only use the full permissions if modifying them.\n            let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(\n                \"SELECT token FROM user_permissions WHERE user_id = ?;\",\n            )\n            .bind(sqluser.id)\n            .fetch_all(pool)\n            .await\n            .ok()?;\n\n            Some(sqluser.into_user(Some(sql_user_perms)))\n        }\n    }\n\n    #[derive(sqlx::FromRow, Clone)]\n    pub struct SqlPermissionTokens {\n        pub token: String,\n    }\n\n    #[derive(sqlx::FromRow, Clone)]\n    pub struct SqlCsrfToken {\n        pub csrf_token: String,\n    }\n\n    #[async_trait]\n    impl Authentication<User, i64, SqlitePool> for User {\n        async fn load_user(\n            userid: i64,\n            pool: Option<&SqlitePool>,\n        ) -> Result<User, anyhow::Error> {\n            let pool = pool.unwrap();\n\n            User::get(userid, pool)\n                .await\n                .ok_or_else(|| anyhow::anyhow!(\"Cannot get user\"))\n        }\n\n        fn is_authenticated(&self) -> bool {\n            true\n        }\n\n        fn is_active(&self) -> bool {\n            true\n        }\n\n        fn is_anonymous(&self) -> bool {\n            false\n        }\n    }\n\n    #[async_trait]\n    impl HasPermission<SqlitePool> for User {\n        async fn has(&self, perm: &str, _pool: &Option<&SqlitePool>) -> bool {\n            self.permissions.contains(perm)\n        }\n    }\n\n    #[derive(sqlx::FromRow, Clone)]\n    pub struct SqlUser {\n        pub id: i64,\n        pub email: String,\n    }\n\n    #[derive(sqlx::FromRow, Clone)]\n    pub struct SqlRefreshToken {\n        pub secret: String,\n    }\n\n    impl SqlUser {\n        pub fn into_user(\n            self,\n            sql_user_perms: Option<Vec<SqlPermissionTokens>>,\n        ) -> User {\n            User {\n                id: self.id,\n                email: self.email,\n                permissions: if let Some(user_perms) = sql_user_perms {\n                    user_perms\n                        .into_iter()\n                        .map(|x| x.token)\n                        .collect::<HashSet<String>>()\n                } else {\n                    HashSet::<String>::new()\n                },\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "projects/sso_auth_axum/src/error_template.rs",
    "content": "use leptos::{view, Errors, For, IntoView, RwSignal, SignalGet, View};\n\n// A basic function to display errors served by the error boundaries. Feel free to do more complicated things\n// here than just displaying them\npub fn error_template(errors: RwSignal<Errors>) -> View {\n    view! {\n      <h1>\"Errors\"</h1>\n      <For\n          // a function that returns the items we're iterating over; a signal is fine\n          each=move || errors.get()\n          // a unique key for each item as a reference\n          key=|(key, _)| key.clone()\n          // renders each item to a view\n          children= move | (_, error)| {\n          let error_string = error.to_string();\n            view! {\n              <p>\"Error: \" {error_string}</p>\n            }\n          }\n      />\n    }\n    .into_view()\n}\n"
  },
  {
    "path": "projects/sso_auth_axum/src/fallback.rs",
    "content": "use crate::error_template::error_template;\nuse axum::{\n    body::Body,\n    extract::State,\n    http::{Request, Response, StatusCode, Uri},\n    response::{IntoResponse, Response as AxumResponse},\n};\nuse leptos::prelude::*;\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\n\npub async fn file_and_error_handler(\n    uri: Uri,\n    State(options): State<LeptosOptions>,\n    req: Request<Body>,\n) -> AxumResponse {\n    let root = options.site_root.clone();\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        leptos::logging::log!(\"{:?}:{}\", res.status(), uri);\n        let handler =\n            leptos_axum::render_app_to_stream(options.to_owned(), || {\n                error_template(RwSignal::new(leptos::Errors::default()))\n            });\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`\n    // This path is relative to the cargo root\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {}\", err),\n        )),\n    }\n}\n"
  },
  {
    "path": "projects/sso_auth_axum/src/lib.rs",
    "content": "pub mod auth;\npub mod error_template;\n#[cfg(feature = \"ssr\")]\npub mod fallback;\npub mod sign_in_sign_up;\n#[cfg(feature = \"ssr\")]\npub mod state;\nuse leptos::{leptos_dom::helpers::TimeoutHandle, *};\nuse leptos_meta::*;\nuse leptos_router::*;\nuse sign_in_sign_up::*;\n\n#[cfg(feature = \"ssr\")]\nmod ssr_imports {\n    pub use crate::auth::ssr_imports::{AuthSession, SqlRefreshToken};\n    pub use leptos::{use_context, ServerFnError};\n    pub use oauth2::{reqwest::async_http_client, TokenResponse};\n    pub use sqlx::SqlitePool;\n\n    pub fn pool() -> Result<SqlitePool, ServerFnError> {\n        use_context::<SqlitePool>()\n            .ok_or_else(|| ServerFnError::new(\"Pool missing.\"))\n    }\n\n    pub fn auth() -> Result<AuthSession, ServerFnError> {\n        use_context::<AuthSession>()\n            .ok_or_else(|| ServerFnError::new(\"Auth session missing.\"))\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct Email(RwSignal<Option<String>>);\n#[derive(Clone, Debug)]\npub struct ExpiresIn(RwSignal<u64>);\n#[server]\npub async fn refresh_token(email: String) -> Result<u64, ServerFnError> {\n    use crate::{auth::User, state::AppState};\n    use ssr_imports::*;\n\n    let pool = pool()?;\n    let oauth_client = expect_context::<AppState>().client;\n    let user = User::get_from_email(&email, &pool)\n        .await\n        .ok_or(ServerFnError::new(\"User not found\"))?;\n\n    let refresh_secret = sqlx::query_as::<_, SqlRefreshToken>(\n        \"SELECT secret FROM google_refresh_tokens WHERE user_id = ?\",\n    )\n    .bind(user.id)\n    .fetch_one(&pool)\n    .await?\n    .secret;\n\n    let token_response = oauth_client\n        .exchange_refresh_token(&oauth2::RefreshToken::new(refresh_secret))\n        .request_async(async_http_client)\n        .await?;\n\n    let access_token = token_response.access_token().secret();\n    let expires_in = token_response.expires_in().unwrap().as_secs();\n    let refresh_secret = token_response.refresh_token().unwrap().secret();\n    sqlx::query(\"DELETE FROM google_tokens WHERE user_id == ?\")\n        .bind(user.id)\n        .execute(&pool)\n        .await?;\n    sqlx::query(\n        \"INSERT OR REPLACE INTO google_tokens (user_id,access_secret,refresh_secret) \\\n         VALUES (?,?,?)\",\n    )\n    .bind(user.id)\n    .bind(access_token)\n    .bind(refresh_secret)\n    .execute(&pool)\n    .await?;\n    Ok(expires_in)\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    provide_meta_context();\n    let email = RwSignal::new(None::<String>);\n    let rw_expires_in = RwSignal::new(0);\n    provide_context(Email(email));\n    provide_context(ExpiresIn(rw_expires_in));\n\n    let display_email =\n        move || email.get().unwrap_or(String::from(\"No email to display\"));\n    let refresh_token = create_server_action::<RefreshToken>();\n\n    create_effect(move |handle: Option<Option<TimeoutHandle>>| {\n        // If this effect is called, try to cancel the previous handle.\n        if let Some(prev_handle) = handle.flatten() {\n            prev_handle.clear();\n        };\n        // if expires_in isn't 0, then set a timeout that rerfresh a minute short of the refresh.\n        let expires_in = rw_expires_in.get();\n        if expires_in != 0 && email.get_untracked().is_some() {\n            let handle = set_timeout_with_handle(\n                move || {\n                    refresh_token.dispatch(RefreshToken {\n                        email: email.get_untracked().unwrap(),\n                    })\n                },\n                std::time::Duration::from_secs(\n                    // Google tokens last 3599 seconds, so we'll get a refresh token every 14 seconds.\n                    expires_in.checked_sub(3545).unwrap_or_default(),\n                ),\n            )\n            .unwrap();\n            Some(handle)\n        } else {\n            None\n        }\n    });\n\n    create_effect(move |_| {\n        if let Some(Ok(expires_in)) = refresh_token.value().get() {\n            rw_expires_in.set(expires_in);\n        }\n    });\n\n    view! {\n        <Stylesheet id=\"leptos\" href=\"/pkg/sso_auth_axum.css\"/>\n        <Link rel=\"shortcut icon\" type_=\"image/ico\" href=\"/favicon.ico\"/>\n        <Title text=\"SSO Auth Axum\"/>\n        <Router>\n            <main>\n                <Routes>\n                    <Route path=\"\" view=move || {\n                        view!{\n                            {display_email}\n                            <Show when=move || email.get().is_some() fallback=||view!{<SignIn/>}>\n                                <LogOut/>\n                            </Show>\n                            }\n                        }/>\n                    <Route path=\"g_auth\" view=||view!{<HandleGAuth/>}/>\n                </Routes>\n            </main>\n        </Router>\n    }\n}\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    _ = console_log::init_with_level(log::Level::Debug);\n    console_error_panic_hook::set_once();\n\n    leptos::mount_to_body(App);\n}\n"
  },
  {
    "path": "projects/sso_auth_axum/src/main.rs",
    "content": "use crate::ssr_imports::*;\nuse axum::{\n    body::Body as AxumBody,\n    extract::{Path, State},\n    http::Request,\n    response::IntoResponse,\n    routing::get,\n    Router,\n};\nuse axum_session::{Key, SessionConfig, SessionLayer, SessionStore};\nuse axum_session_auth::{AuthConfig, AuthSessionLayer};\nuse leptos::{get_configuration, logging::log, provide_context, view};\nuse leptos_axum::{\n    generate_route_list, handle_server_fns_with_context, LeptosRoutes,\n};\nuse sqlx::sqlite::SqlitePoolOptions;\nuse sso_auth_axum::{\n    auth::*, fallback::file_and_error_handler, state::AppState,\n};\n\nasync fn server_fn_handler(\n    State(app_state): State<AppState>,\n    auth_session: AuthSession,\n    path: Path<String>,\n    request: Request<AxumBody>,\n) -> impl IntoResponse {\n    log!(\"{:?}\", path);\n\n    handle_server_fns_with_context(\n        move || {\n            provide_context(app_state.clone());\n            provide_context(auth_session.clone());\n            provide_context(app_state.pool.clone());\n        },\n        request,\n    )\n    .await\n}\n\npub async fn leptos_routes_handler(\n    auth_session: AuthSession,\n    State(app_state): State<AppState>,\n    axum::extract::State(option): axum::extract::State<leptos::LeptosOptions>,\n    request: Request<AxumBody>,\n) -> axum::response::Response {\n    let handler = leptos_axum::render_app_async_with_context(\n        option.clone(),\n        move || {\n            provide_context(app_state.clone());\n            provide_context(auth_session.clone());\n            provide_context(app_state.pool.clone());\n        },\n        move || view! {  <sso_auth_axum::App/> },\n    );\n\n    handler(request).await.into_response()\n}\n\n#[tokio::main]\nasync fn main() {\n    simple_logger::init_with_level(log::Level::Info)\n        .expect(\"couldn't initialize logging\");\n\n    let pool = SqlitePoolOptions::new()\n        .connect(\"sqlite:sso.db\")\n        .await\n        .expect(\"Could not make pool.\");\n\n    // Auth section\n    let session_config = SessionConfig::default()\n        .with_table_name(\"sessions_table\")\n        .with_key(Key::generate())\n        .with_database_key(Key::generate());\n    // .with_security_mode(SecurityMode::PerSession); // FIXME did this disappear?\n\n    let auth_config = AuthConfig::<i64>::default();\n    let session_store = SessionStore::<SessionSqlitePool>::new(\n        Some(pool.clone().into()),\n        session_config,\n    )\n    .await\n    .unwrap();\n\n    sqlx::migrate!()\n        .run(&pool)\n        .await\n        .expect(\"could not run SQLx migrations\");\n\n    // Setting this to None means we'll be using cargo-leptos and its env vars\n    let conf = get_configuration(None).unwrap();\n    let leptos_options = conf.leptos_options;\n    let addr = leptos_options.site_addr;\n    let routes = generate_route_list(sso_auth_axum::App);\n\n    // We create our client using provided environment variables.\n    let client = oauth2::basic::BasicClient::new(\n        oauth2::ClientId::new(\n            std::env::var(\"G_AUTH_CLIENT_ID\")\n                .expect(\"G_AUTH_CLIENT_ID Env var to be set.\"),\n        ),\n        Some(oauth2::ClientSecret::new(\n            std::env::var(\"G_AUTH_SECRET\")\n                .expect(\"G_AUTH_SECRET Env var to be set\"),\n        )),\n        oauth2::AuthUrl::new(\n            \"https://accounts.google.com/o/oauth2/v2/auth\".to_string(),\n        )\n        .unwrap(),\n        Some(\n            oauth2::TokenUrl::new(\n                \"https://oauth2.googleapis.com/token\".to_string(),\n            )\n            .unwrap(),\n        ),\n    )\n    .set_redirect_uri(\n        oauth2::RedirectUrl::new(\n            std::env::var(\"REDIRECT_URL\")\n                .expect(\"REDIRECT_URL Env var to be set\"),\n        )\n        .unwrap(),\n    );\n\n    let app_state = AppState {\n        leptos_options,\n        pool: pool.clone(),\n        client,\n    };\n\n    // build our application with a route\n    let app = Router::new()\n        .route(\n            \"/api/*fn_name\",\n            get(server_fn_handler).post(server_fn_handler),\n        )\n        .leptos_routes_with_handler(routes, get(leptos_routes_handler))\n        .fallback(file_and_error_handler)\n        .layer(\n            AuthSessionLayer::<User, i64, SessionSqlitePool, SqlitePool>::new(\n                Some(pool.clone()),\n            )\n            .with_config(auth_config),\n        )\n        .layer(SessionLayer::new(session_store))\n        .with_state(app_state);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    log!(\"listening on http://{}\", &addr);\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n"
  },
  {
    "path": "projects/sso_auth_axum/src/sign_in_sign_up.rs",
    "content": "use super::*;\n\n#[cfg(feature = \"ssr\")]\npub mod ssr_imports {\n    pub use crate::{\n        auth::{ssr_imports::SqlCsrfToken, User},\n        state::AppState,\n    };\n    pub use oauth2::{\n        reqwest::async_http_client, AuthorizationCode, CsrfToken, Scope,\n        TokenResponse,\n    };\n    pub use serde_json::Value;\n}\n\n#[server]\npub async fn google_sso() -> Result<String, ServerFnError> {\n    use crate::ssr_imports::*;\n    use ssr_imports::*;\n\n    let oauth_client = expect_context::<AppState>().client;\n    let pool = pool()?;\n\n    // We get the authorization URL and CSRF_TOKEN\n    let (authorize_url, csrf_token) = oauth_client\n        .authorize_url(CsrfToken::new_random)\n        .add_scope(Scope::new(\"openid\".to_string()))\n        .add_scope(Scope::new(\"email\".to_string()))\n        // required for google auth refresh token to be part of the response.\n        .add_extra_param(\"access_type\", \"offline\")\n        .add_extra_param(\"prompt\", \"consent\")\n        .url();\n    let url = authorize_url.to_string();\n    leptos::logging::log!(\"{url:?}\");\n    // Store the CSRF_TOKEN in our sqlite db.\n    sqlx::query(\"INSERT INTO csrf_tokens (csrf_token) VALUES (?)\")\n        .bind(csrf_token.secret())\n        .execute(&pool)\n        .await\n        .map(|_| ())?;\n\n    // Send the url to the client.\n    Ok(url)\n}\n\n#[component]\npub fn SignIn() -> impl IntoView {\n    let g_auth = Action::<GoogleSso, _>::server();\n\n    create_effect(move |_| {\n        if let Some(Ok(redirect)) = g_auth.value().get() {\n            window().location().set_href(&redirect).unwrap();\n        }\n    });\n\n    view! {\n      <div style=\"\n      display:flex;\n      flex-direction: column;\n      justify-content: center;\n      align-items: center;\n      \">\n        <div> {\"Sign Up Sign In\"} </div>\n        <button style=\"display:flex;\"  on:click=move|_| g_auth.dispatch(GoogleSso{})>\n        <svg style=\"width:2rem;\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 48 48\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"display: block;\">\n          <path fill=\"#EA4335\" d=\"M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z\"></path>\n          <path fill=\"#4285F4\" d=\"M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z\"></path>\n          <path fill=\"#FBBC05\" d=\"M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z\"></path>\n          <path fill=\"#34A853\" d=\"M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z\"></path>\n          <path fill=\"none\" d=\"M0 0h48v48H0z\"></path>\n        </svg>\n        <span style=\"margin-left:0.5rem;\">\"Sign in with Google\"</span>\n        </button>\n        </div>\n    }\n}\n\n#[server]\npub async fn handle_g_auth_redirect(\n    provided_csrf: String,\n    code: String,\n) -> Result<(String, u64), ServerFnError> {\n    use crate::ssr_imports::*;\n    use ssr_imports::*;\n\n    let oauth_client = expect_context::<AppState>().client;\n    let pool = pool()?;\n    let auth_session = auth()?;\n    // If there's no match we'll return an error.\n    let _ = sqlx::query_as::<_, SqlCsrfToken>(\n        \"SELECT csrf_token FROM csrf_tokens WHERE csrf_token = ?\",\n    )\n    .bind(provided_csrf)\n    .fetch_one(&pool)\n    .await\n    .map_err(|err| ServerFnError::new(format!(\"CSRF_TOKEN error : {err:?}\")))?;\n\n    let token_response = oauth_client\n        .exchange_code(AuthorizationCode::new(code.clone()))\n        .request_async(async_http_client)\n        .await?;\n    leptos::logging::log!(\"{:?}\", &token_response);\n    let access_token = token_response.access_token().secret();\n    let expires_in = token_response.expires_in().unwrap().as_secs();\n    let refresh_secret = token_response.refresh_token().unwrap().secret();\n    let user_info_url = \"https://www.googleapis.com/oauth2/v3/userinfo\";\n    let client = reqwest::Client::new();\n    let response = client\n        .get(user_info_url)\n        .bearer_auth(access_token)\n        .send()\n        .await?;\n\n    let email = if response.status().is_success() {\n        let response_json: Value = response.json().await?;\n        leptos::logging::log!(\"{response_json:?}\");\n        response_json[\"email\"]\n            .as_str()\n            .expect(\"email to parse to string\")\n            .to_string()\n    } else {\n        return Err(ServerFnError::new(format!(\n            \"Response from google has status of {}\",\n            response.status()\n        )));\n    };\n\n    let user = if let Some(user) = User::get_from_email(&email, &pool).await {\n        user\n    } else {\n        sqlx::query(\"INSERT INTO users (email) VALUES (?)\")\n            .bind(&email)\n            .execute(&pool)\n            .await?;\n        User::get_from_email(&email, &pool).await.unwrap()\n    };\n\n    auth_session.login_user(user.id);\n\n    sqlx::query(\"DELETE FROM google_tokens WHERE user_id == ?\")\n        .bind(user.id)\n        .execute(&pool)\n        .await?;\n\n    sqlx::query(\n        \"INSERT INTO google_tokens (user_id,access_secret,refresh_secret) \\\n         VALUES (?,?,?)\",\n    )\n    .bind(user.id)\n    .bind(access_token)\n    .bind(refresh_secret)\n    .execute(&pool)\n    .await?;\n\n    Ok((user.email, expires_in as u64))\n}\n\n#[derive(Params, Debug, PartialEq, Clone)]\npub struct OAuthParams {\n    pub code: Option<String>,\n    pub state: Option<String>,\n}\n\n#[component]\npub fn HandleGAuth() -> impl IntoView {\n    let handle_g_auth_redirect = Action::<HandleGAuthRedirect, _>::server();\n\n    let query = use_query::<OAuthParams>();\n    let navigate = leptos_router::use_navigate();\n    let rw_email = expect_context::<Email>().0;\n    let rw_expires_in = expect_context::<ExpiresIn>().0;\n    create_effect(move |_| {\n        if let Some(Ok((email, expires_in))) =\n            handle_g_auth_redirect.value().get()\n        {\n            rw_email.set(Some(email));\n            rw_expires_in.set(expires_in);\n            navigate(\"/\", NavigateOptions::default());\n        }\n    });\n\n    create_effect(move |_| {\n        if let Ok(OAuthParams { code, state }) = query.get_untracked() {\n            handle_g_auth_redirect.dispatch(HandleGAuthRedirect {\n                provided_csrf: state.unwrap(),\n                code: code.unwrap(),\n            });\n        } else {\n            leptos::logging::log!(\"error parsing oauth params\");\n        }\n    });\n    view! {}\n}\n\n#[server]\npub async fn logout() -> Result<(), ServerFnError> {\n    use crate::ssr_imports::*;\n\n    let auth = auth()?;\n    auth.logout_user();\n    leptos_axum::redirect(\"/\");\n    Ok(())\n}\n\n#[component]\npub fn LogOut() -> impl IntoView {\n    let log_out = create_server_action::<Logout>();\n    view! {\n        <button on:click=move|_|log_out.dispatch(Logout{})>{\"log out\"}</button>\n    }\n}\n"
  },
  {
    "path": "projects/sso_auth_axum/src/state.rs",
    "content": "use axum::extract::FromRef;\nuse leptos::LeptosOptions;\nuse sqlx::SqlitePool;\n\n/// This takes advantage of Axum's SubStates feature by deriving FromRef. This is the only way to have more than one\n/// item in Axum's State. Leptos requires you to have leptosOptions in your State struct for the leptos route handlers\n#[derive(FromRef, Debug, Clone)]\npub struct AppState {\n    pub leptos_options: LeptosOptions,\n    pub pool: SqlitePool,\n    pub client: oauth2::basic::BasicClient,\n}\n"
  },
  {
    "path": "projects/sso_auth_axum/style.css",
    "content": ".pending {\n\tcolor: purple;\n}\n\na {\n\tcolor: black;\n}"
  },
  {
    "path": "projects/tauri-from-scratch/.gitignore",
    "content": "/target\ngen/\n"
  },
  {
    "path": "projects/tauri-from-scratch/Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\"src-tauri\", \"src-orig\"]\n\n[profile.release]\ncodegen-units = 1\nlto = true\n"
  },
  {
    "path": "projects/tauri-from-scratch/README.md",
    "content": "# Tauri from scratch\n\nThis is a guide on how to build a leptos tauri project from scratch without using a template.\n\nFirst\n\n```sh\ncargo new leptos_tauri_from_scratch\n```\n\nThen, make our two separate project folders. We need one for our actual app, _src-orig/_ and the other is required when using `cargo tauri`\n\n```sh\nmkdir src-orig && mkdir src-tauri\n```\n\nDelete the original src folder.\n\n```sh\nrm -r src\n```\n\nRewrite the `Cargo.toml` file in our crate root to the following.\n\n```toml\n[workspace]\nresolver = \"2\"\nmembers = [\"src-tauri\", \"src-orig\"]\n\n[profile.release]\ncodegen-units = 1\nlto = true\n```\n\nWe'll list our workspace members. `codegen-units = 1` and `lto = true` are good things to have for our eventual release, they make the wasm file smaller.\n\nWhat we're going to do is use `cargo leptos` for building our SSR server and we'll call trunk from `cargo tauri` for building our CSR client that we bundle into our different apps.\n\nLet's add a `Trunk.toml` file.\n\n```toml\n[build]\ntarget = \"./src-orig/index.html\"\n\n[watch]\nignore = [\"./src-tauri\"]\n```\n\nThe target of `index.html` is what trunk uses to build the wasm and js files that we'll need for the bundling process when we call `cargo tauri build`. We'll get the resulting files in a `src-orig/dist` folder.\n\nCreate the `index.html` file\n\n```sh\ntouch src-orig/index.html\n```\n\nLet's fill it with\n\n```html\n<!DOCTYPE html>\n<html>\n  <head>\n    <link\n      data-trunk\n      rel=\"rust\"\n      data-wasm-opt=\"z\"\n      data-bin=\"leptos_tauri_from_scratch_bin\"\n    />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  </head>\n  <body></body>\n</html>\n```\n\nThis line\n\n```html\n<link\n  data-trunk\n  rel=\"rust\"\n  data-wasm-opt=\"z\"\n  data-bin=\"leptos_tauri_from_scratch_bin\"\n/>\n```\n\nTells trunk we want to compile our wasm to be small with `opt=\"z\"` and that our binary will be named `\"leptos_tauri_from_scratch_bin\"`.\n\nWe need to specify that our binary will be a different name than our project name because we are also going to get a wasm file from our library and if we don't use different names then `cargo tauri` will get confused.\n\nMore specifically two wasm artifacts will be generated, one for the lib and the other for the binary and it won't know which to use.\n\nCreate a favicon that we referenced.\n\n```sh\nmkdir public\ncurl https://raw.githubusercontent.com/leptos-rs/leptos/main/examples/counter/public/favicon.ico > public/favicon.ico\n```\n\nLet's create a tauri configuration file.\n\n```sh\ntouch src-tauri/taur.conf.json\n```\n\nAnd drop this in there\n\n```json\n{\n  \"identifier\": \"leptos.chat.app\",\n  \"productName\": \"leptos_tauri_from_scratch\",\n  \"version\": \"0.1.0\",\n  \"build\": {\n    \"beforeDevCommand\": \"\",\n    \"beforeBuildCommand\": \"trunk build --no-default-features -v --features \\\"csr\\\"\",\n    \"devUrl\": \"http://127.0.0.1:3000\",\n    \"frontendDist\": \"../dist\"\n  },\n  \"bundle\": {\n    \"active\": true,\n    \"category\": \"DeveloperTool\",\n    \"copyright\": \"\",\n    \"externalBin\": [],\n    \"icon\": [\"icons/icon.png\"],\n    \"longDescription\": \"\",\n    \"macOS\": {\n      \"entitlements\": null,\n      \"exceptionDomain\": \"\",\n      \"frameworks\": [],\n      \"providerShortName\": null,\n      \"signingIdentity\": null\n    },\n    \"resources\": [],\n    \"shortDescription\": \"\",\n    \"targets\": \"all\",\n    \"windows\": {\n      \"certificateThumbprint\": null,\n      \"digestAlgorithm\": \"sha256\",\n      \"timestampUrl\": \"\"\n    }\n  },\n  \"app\": {\n    \"security\": {\n      \"csp\": null\n    },\n    \"windows\": [\n      {\n        \"fullscreen\": false,\n        \"height\": 800,\n        \"resizable\": true,\n        \"title\": \"LeptosChatApp\",\n        \"width\": 1200\n      }\n    ]\n  }\n}\n```\n\nYou can basically ignore all of this except for\n\n```json\n  \"build\": {\n    \"beforeDevCommand\": \"\",\n    \"beforeBuildCommand\": \"trunk build --no-default-features -v --features \\\"csr\\\"\",\n    \"devUrl\": \"http://127.0.0.1:3000\",\n    \"frontendDist\": \"../dist\"\n  },\n```\n\nLet's look at\n\n```json\n    \"beforeBuildCommand\": \"trunk build --no-default-features -v --features \\\"csr\\\"\",\n```\n\nWhen we `cargo tauri build` this will run before hand. Trunk will run it's build process, using the index.html file in the src-orig that we specified in `Trunk.toml`.\n\nWe'll build a binary using only the CSR feature. This is important.\n\nWe are going to build an SSR app, and serve it over the internet but we are also going to build a tauri client for desktop and mobile using CSR.\n\nIt's going to make network requests to our server that is servering our app to browsers using SSR.\n\nThis is the best of both worlds, we get the SEO of SSR and other advantages while being able to use CSR to build our app for other platforms.\n\n```json\n    \"devUrl\": \"http://127.0.0.1:3000\",\n    \"frontendDist\": \"../dist\"\n```\n\nCheck <https://tauri.app/v1/api/config/#buildconfig> for what these do, but our before build command `trunk build` will build into a folder `src-orig/dist` which we reference here.\n\nLet's add a `Cargo.toml`` to both of our packages.\n\n```sh\ntouch src-tauri/Cargo.toml && touch src-orig/Cargo.toml\n```\n\nLet's change `src-tauri/Cargo.toml` to this.\n\n```toml\n[package]\nname = \"src_tauri\"\nversion = \"0.0.1\"\nedition = \"2021\"\n\n[lib]\nname = \"app_lib\"\npath = \"src/lib.rs\"\n\n[build-dependencies]\ntauri-build = { version = \"2.2.0\", features = [] }\n\n[dependencies]\nlog = \"0.4.22\"\nserde = { version = \"1.0\", features = [\"derive\"] }\ntauri = { version = \"2.5.1\", features = [\"devtools\"] }\ntauri-plugin-http = \"2.4.4\"\n\n[features]\n#default = [\"custom-protocol\"]\ncustom-protocol = [\"tauri/custom-protocol\"]\n```\n\nTo make use of `cargo tauri build` we need `tauri-build` and we also need a `build.rs`.\n\n```sh\ntouch src-tauri/build.rs\n```\n\nAnd let's change that to\n\n```rust\nfn main() {\n    tauri_build::build();\n}\n```\n\nIn our `src-orig/Cargo.toml` let's add.\n\n```toml\n[package]\nname = \"leptos_tauri_from_scratch\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"staticlib\", \"cdylib\", \"rlib\"]\n\n[[bin]]\nname = \"leptos_tauri_from_scratch_bin\"\npath = \"./src/main.rs\"\n\n[dependencies]\naxum = { version = \"0.8.4\", optional = true }\naxum-macros = { version = \"0.5.0\", optional = true }\nconsole_error_panic_hook = { version = \"0.1.7\", optional = true }\nleptos = { git = \"https://github.com/leptos-rs/leptos.git\", rev = \"v0.8.2\" }\nleptos_axum = { git = \"https://github.com/leptos-rs/leptos.git\", rev = \"v0.8.2\", optional = true }\nleptos_meta = { git = \"https://github.com/leptos-rs/leptos.git\", rev = \"v0.8.2\", optional = true }\nserver_fn = { git = \"https://github.com/leptos-rs/leptos.git\", rev = \"v0.8.2\", optional = true }\ntokio = { version = \"1.45.1\", features = [\"rt-multi-thread\"], optional = true }\ntower = { version = \"0.5.2\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\", \"cors\"], optional = true }\nwasm-bindgen = { version = \"=0.2.100\", optional = true }\n\n[features]\ncsr = [\"leptos/csr\", \"dep:server_fn\"]\nhydrate = [\n  \"leptos/hydrate\",\n  \"dep:leptos_meta\",\n  \"dep:console_error_panic_hook\",\n  \"dep:wasm-bindgen\"\n]\nssr = [\n  \"dep:axum\",\n  \"dep:axum-macros\",\n  \"leptos/ssr\",\n  \"dep:leptos_axum\",\n  \"dep:leptos_meta\",\n  \"leptos_meta/ssr\",\n  \"dep:tower-http\",\n  \"dep:tower\",\n  \"dep:tokio\",\n]\n\n[package.metadata.leptos]\nbin-exe-name = \"leptos_tauri_from_scratch_bin\"\noutput-name = \"leptos_tauri_from_scratch\"\nassets-dir = \"../public\"\nsite-pkg-dir = \"pkg\"\nsite-root = \"target/site\"\nsite-addr = \"0.0.0.0:3000\"\nreload-port = 3001\nbrowserquery = \"defaults\"\nwatch = false\nenv = \"DEV\"\nbin-features = [\"ssr\"]\nbin-default-features = false\nlib-features = [\"hydrate\"]\nlib-default-features = false\n```\n\nSo this looks like a normal SSR leptos, except for our CSR, Hydrate, and SSR versions.\n\n```toml\ncsr = [\"leptos/csr\", \"dep:server_fn\"]\nhydrate = [\n  \"leptos/hydrate\",\n  \"dep:leptos_meta\",\n  \"dep:console_error_panic_hook\",\n  \"dep:wasm-bindgen\"\n]\nssr = [\n```\n\nalso our binary is specified and named\n\n```toml\n[[bin]]\nname=\"leptos_tauri_from_scratch_bin\"\npath=\"./src/main.rs\"\n```\n\nour lib is specified, but unnamed (it will default to the project name in cargo leptos and in cargo tauri). We need the different crate types for `cargo leptos serve` and `cargo tauri build`\n\n```toml\n[lib]\ncrate-type = [\"staticlib\", \"cdylib\", \"rlib\"]\n```\n\nWe've added the override to our cargo leptos metadata.\n\n```toml\n[package.metadata.leptos]\nbin-exe-name=\"leptos_tauri_from_scratch_bin\"\n```\n\nOur tauri app is going to send server function calls to this address, this is where we'll serve our hydratable SSR client from.\n\n```toml\nsite-addr = \"0.0.0.0:3000\"\n```\n\nNow let's create the `main.rs` that we reference in the `src-orig/Cargo.toml`\n\n```sh\nmkdir src-orig/src && touch src-orig/src/main.rs\n```\n\nand drop this in there...\n\n```rust\n#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::{\n        body::Body,\n        extract::{Request, State},\n        response::IntoResponse,\n        routing::get,\n        Router,\n    };\n    use leptos::logging::log;\n    use leptos::prelude::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use leptos_tauri_from_scratch::{\n        app::{shell, App},\n        fallback::file_and_error_handler,\n    };\n    use tower_http::cors::CorsLayer;\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let leptos_options = conf.leptos_options;\n    // Generate the list of routes in your Leptos App\n    let routes = generate_route_list(App);\n\n    #[derive(Clone, Debug, axum_macros::FromRef)]\n    pub struct ServerState {\n        pub options: LeptosOptions,\n        pub routes: Vec<leptos_axum::AxumRouteListing>,\n    }\n\n    let state = ServerState {\n        options: leptos_options,\n        routes: routes.clone(),\n    };\n\n    pub async fn server_fn_handler(\n        State(state): State<ServerState>,\n        request: Request<Body>,\n    ) -> impl IntoResponse {\n        leptos_axum::handle_server_fns_with_context(\n            move || {\n                provide_context(state.clone());\n            },\n            request,\n        )\n        .await\n        .into_response()\n    }\n\n    let cors = CorsLayer::new()\n        .allow_methods([axum::http::Method::GET, axum::http::Method::POST])\n        .allow_origin(\n            // Allow requests from the Tauri app\n            \"tauri://localhost\"\n                .parse::<axum::http::HeaderValue>()\n                .unwrap(),\n        )\n        .allow_headers(vec![\n            axum::http::header::CONTENT_TYPE,\n            axum::http::header::ACCEPT,\n        ]);\n\n    pub async fn leptos_routes_handler(\n        State(state): State<ServerState>,\n        req: Request<Body>,\n    ) -> axum::response::Response {\n        let leptos_options = state.options.clone();\n        let handler = leptos_axum::render_route_with_context(\n            state.routes.clone(),\n            move || {\n                provide_context(\"...\");\n            },\n            move || shell(leptos_options.clone()),\n        );\n        handler(axum::extract::State(state), req)\n            .await\n            .into_response()\n    }\n\n    let app = Router::new()\n        .route(\n            \"/api/{*fn_name}\",\n            get(server_fn_handler).post(server_fn_handler),\n        )\n        .layer(cors)\n        .leptos_routes_with_handler(routes, get(leptos_routes_handler))\n        .fallback(file_and_error_handler)\n        .with_state(state);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    log!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(feature = \"csr\")]\npub fn main() {\n    server_fn::client::set_server_url(\"http://127.0.0.1:3000\");\n    leptos::mount::mount_to_body(leptos_tauri_from_scratch::app::App);\n}\n```\n\nand the hydration at `src-orig/src/lib.rs`\n\n```rust\npub mod app;\n#[cfg(feature = \"ssr\")]\npub mod fallback;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(app::App);\n}\n```\n\nThis is our three pronged binary.\n\nWhen we run cargo leptos server, we're going to get a server that is what's under `#[cfg(feature=\"ssr\")]`.\n\nAnd our csr feature\n\n```rust\n#[cfg(feature = \"csr\")]\npub fn main() {\n    server_fn::client::set_server_url(\"http://127.0.0.1:3000\");\n    leptos::mount::mount_to_body(leptos_tauri_from_scratch::app::App);\n}\n```\n\nHere we're setting the server functions to use the url base that we access in our browser. I.e local host, on the port we specified in the leptos metadata.\nOtherwise our tauri app will try to route server function network requests using it's own idea of what it's url is. Which is `tauri://localhost` on macOS, and something else on windows.\n\nSince we are going to be getting API requests from different locations beside our server's domain let's set up CORS, if you don't do this your tauri apps won't be able to make server function calls because it will run into CORS erros.\n\n```rust\n        let cors = CorsLayer::new()\n            .allow_methods([axum::http::Method::GET, axum::http::Method::POST])\n            .allow_origin(\n                \"tauri://localhost\"\n                    .parse::<axum::http::HeaderValue>()\n                    .unwrap(),\n            )\n            .allow_headers(vec![axum::http::header::CONTENT_TYPE]);\n```\n\nIf you are on windows the origin of your app will be different than `tauri://localhost` and you'll need to figure that out, as well as if you deploy it to places that aren't your localhost!\n\nEverything else is standard leptos, so let's fill in the fallback and the lib really quick.\n\n```sh\ntouch src-orig/src/lib.rs && touch src-orig/src/fallback.rs\n```\n\nLet's dump this bog standard leptos code in the `src-orig/src/app.rs`\n\n```rust\nuse leptos::prelude::*;\n\n#[cfg(feature = \"ssr\")]\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    use leptos_meta::MetaTags;\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[server(endpoint = \"hello_world\")]\npub async fn hello_world_server() -> Result<String, ServerFnError> {\n    Ok(\"Hey.\".to_string())\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    let action = ServerAction::<HelloWorldServer>::new();\n    let vals = RwSignal::new(String::new());\n    Effect::new(move |_| {\n        if let Some(resp) = action.value().get() {\n            match resp {\n                Ok(val) => vals.set(val),\n                Err(err) => vals.set(format!(\"{err:?}\")),\n            }\n        }\n    });\n\n    view! {\n        <button\n            on:click=move |_| {\n                action.dispatch(HelloWorldServer{});\n            }\n        >\"Hello world.\"</button>\n        <br/><br/>\n        <span>\"Server says: \"</span>\n        {move || vals.get()}\n    }\n}\n```\n\nand add this to `src-org/src/fallback.rs`\n\n```rust\nuse axum::{\n    body::Body,\n    extract::State,\n    http::{Request, Response, StatusCode, Uri},\n    response::{IntoResponse, Response as AxumResponse},\n};\nuse leptos::{view, prelude::LeptosOptions};\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\n\npub async fn file_and_error_handler(\n    uri: Uri,\n    State(options): State<LeptosOptions>,\n    req: Request<Body>,\n) -> AxumResponse {\n    let root = options.site_root.clone();\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        let handler = leptos_axum::render_app_to_stream(\n            move || view! {404},\n        );\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n```\n\nLet's fill in our `src-tauri/src/` folder.\n\n```sh\nmkdir src-tauri/src && touch src-tauri/src/main.rs && touch src-tauri/src/lib.rs\n```\n\nand drop this in `src-tauri/src/main.rs` This is standard tauri boilerplate.\n\n```rust\n// Prevents additional console window on Windows in release, DO NOT REMOVE!!\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")]\n\nfn main() {\n    app_lib::run();\n}\n```\n\nand in `src-tauri/src/lib.rs`\n\n```rust\nuse tauri::Manager;\n\n#[cfg_attr(mobile, tauri::mobile_entry_point)]\npub fn run() {\n    tauri::Builder::default()\n        .plugin(tauri_plugin_http::init())\n        .setup(|app| {\n            {\n                let window = app.get_webview_window(\"main\").unwrap();\n                window.open_devtools();\n            }\n            Ok(())\n        })\n        .run(tauri::generate_context!())\n        .expect(\"error while running tauri application\");\n}\n```\n\nWe're gonna open devtools right away to see what is going on in our app. We need the tauri_http_plugin to make http calls, and generate_context reads our `tauri.conf.json` in the package in which its run.\n\nWe need an icon folder and an icon to build.\n\n```sh\nmkdir src-tauri/icons\ncurl https://raw.githubusercontent.com/tauri-apps/tauri/dev/examples/.icons/128x128.png > src-tauri/icons/icon.png\n```\n\nset nightly\n\n```sh\nrustup override set nightly\n```\n\nThen run\n\n```sh\ncargo leptos serve\n```\n\nYou should get something like\n\n```sh\n➜  lepto_tauri_from_scratch git:(main) ✗ cargo leptos serve\n    Finished dev [unoptimized + debuginfo] target(s) in 0.60s\n       Cargo finished cargo build --package=leptos_tauri_from_scratch --lib --target-dir=/Users/sam/Projects/lepto_tauri_from_scratch/target/front --target=wasm32-unknown-unknown --no-default-features --features=hydrate\n       Front compiling WASM\n    Finished dev [unoptimized + debuginfo] target(s) in 0.93s\n       Cargo finished cargo build --package=leptos_tauri_from_scratch --bin=leptos_tauri_from_scratch_bin --no-default-features --features=ssr\n     Serving at http://0.0.0.0:3000\nlistening on http://0.0.0.0:3000\n```\n\nNow open a new terminal and\n\n```sh\ncargo tauri build\n```\n\n> Install `tauri-cli` if you haven't already.\n\nIt'll build with csr before\n\n```sh\nRunning beforeBuildCommand `trunk build --no-default-features -v --features \"csr\"`\n```\n\nand then you should have your app, I'm on macOS so here's what I get. It's for desktop.\n\n```sh\n Compiling src_tauri v0.0.1 (/Users/sam/Projects/lepto_tauri_from_scratch/src-tauri)\n    Finished release [optimized] target(s) in 2m 26s\n    Bundling leptos_tauri_from_scratch.app (/Users/sam/Projects/lepto_tauri_from_scratch/target/release/bundle/macos/leptos_tauri_from_scratch.app)\n    Bundling leptos_tauri_from_scratch_0.1.0_x64.dmg (/Users/sam/Projects/lepto_tauri_from_scratch/target/release/bundle/dmg/leptos_tauri_from_scratch_0.1.0_x64.dmg)\n    Running bundle_dmg.sh\n```\n\nOpen run it and voilá. Click hello world button and read \"Hey\" from the server.\n\n## Thoughts, Feedback, Criticism, Comments?\n\nSend me any of the above, I'm @sjud on leptos discord. I'm always looking to improve and make these projects more helpful for the community. So please let me know how I can do that. Thanks!\n"
  },
  {
    "path": "projects/tauri-from-scratch/Trunk.toml",
    "content": "[build]\ntarget = \"./src-orig/index.html\"\n\n[watch]\nignore = [\"./src-tauri\"]"
  },
  {
    "path": "projects/tauri-from-scratch/src-orig/Cargo.toml",
    "content": "[package]\nname = \"leptos_tauri_from_scratch\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"staticlib\", \"cdylib\", \"rlib\"]\n\n[[bin]]\nname = \"leptos_tauri_from_scratch_bin\"\npath = \"./src/main.rs\"\n\n[dependencies]\naxum = { version = \"0.8.4\", optional = true }\naxum-macros = { version = \"0.5.0\", optional = true }\nconsole_error_panic_hook = { version = \"0.1.7\", optional = true }\nleptos = { git = \"https://github.com/leptos-rs/leptos.git\", rev = \"v0.8.2\" }\nleptos_axum = { git = \"https://github.com/leptos-rs/leptos.git\", rev = \"v0.8.2\", optional = true }\nleptos_meta = { git = \"https://github.com/leptos-rs/leptos.git\", rev = \"v0.8.2\", optional = true }\nserver_fn = { git = \"https://github.com/leptos-rs/leptos.git\", rev = \"v0.8.2\", optional = true }\ntokio = { version = \"1.45.1\", features = [\"rt-multi-thread\"], optional = true }\ntower = { version = \"0.5.2\", optional = true }\ntower-http = { version = \"0.5.2\", features = [\"fs\", \"cors\"], optional = true }\nwasm-bindgen = { version = \"=0.2.100\", optional = true }\n\n[features]\ncsr = [\"leptos/csr\", \"dep:server_fn\"]\nhydrate = [\n  \"leptos/hydrate\",\n  \"dep:leptos_meta\",\n  \"dep:console_error_panic_hook\",\n  \"dep:wasm-bindgen\"\n]\nssr = [\n  \"dep:axum\",\n  \"dep:axum-macros\",\n  \"leptos/ssr\",\n  \"dep:leptos_axum\",\n  \"dep:leptos_meta\",\n  \"leptos_meta/ssr\",\n  \"dep:tower-http\",\n  \"dep:tower\",\n  \"dep:tokio\",\n]\n\n[package.metadata.leptos]\nbin-exe-name = \"leptos_tauri_from_scratch_bin\"\noutput-name = \"leptos_tauri_from_scratch\"\nassets-dir = \"../public\"\nsite-pkg-dir = \"pkg\"\nsite-root = \"target/site\"\nsite-addr = \"0.0.0.0:3000\"\nreload-port = 3001\nbrowserquery = \"defaults\"\nwatch = false\nenv = \"DEV\"\nbin-features = [\"ssr\"]\nbin-default-features = false\nlib-features = [\"hydrate\"]\nlib-default-features = false\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-orig/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link data-trunk rel=\"rust\" data-wasm-opt=\"z\" data-bin=\"leptos_tauri_from_scratch_bin\"/>\n\t\t<link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\">\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t</head>\n\t<body></body>\n</html>"
  },
  {
    "path": "projects/tauri-from-scratch/src-orig/src/app.rs",
    "content": "use leptos::prelude::*;\n\n#[cfg(feature = \"ssr\")]\npub fn shell(options: LeptosOptions) -> impl IntoView {\n    use leptos_meta::MetaTags;\n    view! {\n        <!DOCTYPE html>\n        <html lang=\"en\">\n            <head>\n                <meta charset=\"utf-8\"/>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n                <AutoReload options=options.clone() />\n                <HydrationScripts options/>\n                <MetaTags/>\n            </head>\n            <body>\n                <App/>\n            </body>\n        </html>\n    }\n}\n\n#[server(endpoint = \"hello_world\")]\npub async fn hello_world_server() -> Result<String, ServerFnError> {\n    Ok(\"Hey.\".to_string())\n}\n\n#[component]\npub fn App() -> impl IntoView {\n    let action = ServerAction::<HelloWorldServer>::new();\n    let vals = RwSignal::new(String::new());\n    Effect::new(move |_| {\n        if let Some(resp) = action.value().get() {\n            match resp {\n                Ok(val) => vals.set(val),\n                Err(err) => vals.set(format!(\"{err:?}\")),\n            }\n        }\n    });\n\n    view! {\n        <button\n            on:click=move |_| {\n                action.dispatch(HelloWorldServer{});\n            }\n        >\"Hello world.\"</button>\n        <br/><br/>\n        <span>\"Server says: \"</span>\n        {move || vals.get()}\n    }\n}\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-orig/src/fallback.rs",
    "content": "use axum::{\n    body::Body,\n    extract::State,\n    http::{Request, Response, StatusCode, Uri},\n    response::{IntoResponse, Response as AxumResponse},\n};\nuse leptos::{prelude::LeptosOptions, view};\nuse tower::ServiceExt;\nuse tower_http::services::ServeDir;\n\npub async fn file_and_error_handler(\n    uri: Uri,\n    State(options): State<LeptosOptions>,\n    req: Request<Body>,\n) -> AxumResponse {\n    let root = options.site_root.clone();\n    let res = get_static_file(uri.clone(), &root).await.unwrap();\n\n    if res.status() == StatusCode::OK {\n        res.into_response()\n    } else {\n        let handler = leptos_axum::render_app_to_stream(move || view! {404});\n        handler(req).await.into_response()\n    }\n}\n\nasync fn get_static_file(\n    uri: Uri,\n    root: &str,\n) -> Result<Response<Body>, (StatusCode, String)> {\n    let req = Request::builder()\n        .uri(uri.clone())\n        .body(Body::empty())\n        .unwrap();\n    match ServeDir::new(root).oneshot(req).await {\n        Ok(res) => Ok(res.into_response()),\n        Err(err) => Err((\n            StatusCode::INTERNAL_SERVER_ERROR,\n            format!(\"Something went wrong: {err}\"),\n        )),\n    }\n}\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-orig/src/lib.rs",
    "content": "pub mod app;\n#[cfg(feature = \"ssr\")]\npub mod fallback;\n\n#[cfg(feature = \"hydrate\")]\n#[wasm_bindgen::prelude::wasm_bindgen]\npub fn hydrate() {\n    console_error_panic_hook::set_once();\n    leptos::mount::hydrate_body(app::App);\n}\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-orig/src/main.rs",
    "content": "#[cfg(feature = \"ssr\")]\n#[tokio::main]\nasync fn main() {\n    use axum::{\n        body::Body,\n        extract::{Request, State},\n        response::IntoResponse,\n        routing::get,\n        Router,\n    };\n    use leptos::logging::log;\n    use leptos::prelude::*;\n    use leptos_axum::{generate_route_list, LeptosRoutes};\n    use leptos_tauri_from_scratch::{\n        app::{shell, App},\n        fallback::file_and_error_handler,\n    };\n    use tower_http::cors::CorsLayer;\n\n    let conf = get_configuration(None).unwrap();\n    let addr = conf.leptos_options.site_addr;\n    let leptos_options = conf.leptos_options;\n    // Generate the list of routes in your Leptos App\n    let routes = generate_route_list(App);\n\n    #[derive(Clone, Debug, axum_macros::FromRef)]\n    pub struct ServerState {\n        pub options: LeptosOptions,\n        pub routes: Vec<leptos_axum::AxumRouteListing>,\n    }\n\n    let state = ServerState {\n        options: leptos_options,\n        routes: routes.clone(),\n    };\n\n    pub async fn server_fn_handler(\n        State(state): State<ServerState>,\n        request: Request<Body>,\n    ) -> impl IntoResponse {\n        leptos_axum::handle_server_fns_with_context(\n            move || {\n                provide_context(state.clone());\n            },\n            request,\n        )\n        .await\n        .into_response()\n    }\n\n    let cors = CorsLayer::new()\n        .allow_methods([axum::http::Method::GET, axum::http::Method::POST])\n        .allow_origin(\n            \"tauri://localhost\"\n                .parse::<axum::http::HeaderValue>()\n                .unwrap(),\n        )\n        .allow_headers(vec![axum::http::header::CONTENT_TYPE]);\n\n    pub async fn leptos_routes_handler(\n        State(state): State<ServerState>,\n        req: Request<Body>,\n    ) -> axum::response::Response {\n        let leptos_options = state.options.clone();\n        let handler = leptos_axum::render_route_with_context(\n            state.routes.clone(),\n            move || {\n                provide_context(\"...\");\n            },\n            move || shell(leptos_options.clone()),\n        );\n        handler(axum::extract::State(state), req)\n            .await\n            .into_response()\n    }\n\n    let app = Router::new()\n        .route(\n            \"/api/{*fn_name}\",\n            get(server_fn_handler).post(server_fn_handler),\n        )\n        .layer(cors)\n        .leptos_routes_with_handler(routes, get(leptos_routes_handler))\n        .fallback(file_and_error_handler)\n        .with_state(state);\n\n    // run our app with hyper\n    // `axum::Server` is a re-export of `hyper::Server`\n    log!(\"listening on http://{}\", &addr);\n    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();\n    axum::serve(listener, app.into_make_service())\n        .await\n        .unwrap();\n}\n\n#[cfg(feature = \"csr\")]\npub fn main() {\n    server_fn::client::set_server_url(\"http://127.0.0.1:3000\");\n    leptos::mount::mount_to_body(leptos_tauri_from_scratch::app::App);\n}\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-tauri/Cargo.toml",
    "content": "[package]\nname = \"src_tauri\"\nversion = \"0.0.1\"\nedition = \"2021\"\n\n[lib]\nname = \"app_lib\"\npath = \"src/lib.rs\"\n\n[build-dependencies]\ntauri-build = { version = \"2.2.0\", features = [] }\n\n[dependencies]\nlog = \"0.4.22\"\nserde = { version = \"1.0\", features = [\"derive\"] }\ntauri = { version = \"2.5.1\", features = [\"devtools\"] }\ntauri-plugin-http = \"2.4.4\"\n\n[features]\n#default = [\"custom-protocol\"]\ncustom-protocol = [\"tauri/custom-protocol\"]\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-tauri/build.rs",
    "content": "fn main() {\n    tauri_build::build();\n}\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-tauri/src/lib.rs",
    "content": "use tauri::Manager;\n\n#[cfg_attr(mobile, tauri::mobile_entry_point)]\npub fn run() {\n    tauri::Builder::default()\n        .plugin(tauri_plugin_http::init())\n        .setup(|app| {\n            {\n                let window = app.get_webview_window(\"main\").unwrap();\n                window.open_devtools();\n            }\n            Ok(())\n        })\n        .run(tauri::generate_context!())\n        .expect(\"error while running tauri application\");\n}\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-tauri/src/main.rs",
    "content": "// Prevents additional console window on Windows in release, DO NOT REMOVE!!\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")]\n\nfn main() {\n    app_lib::run();\n}\n"
  },
  {
    "path": "projects/tauri-from-scratch/src-tauri/tauri.conf.json",
    "content": "{\n    \"identifier\": \"leptos.chat.app\",\n    \"productName\": \"leptos_tauri_from_scratch\",\n    \"version\": \"0.1.0\",\n    \"build\": {\n      \"beforeDevCommand\": \"\",\n      \"beforeBuildCommand\": \"trunk build --no-default-features -v --features \\\"csr\\\"\",\n      \"devUrl\": \"http://127.0.0.1:3000\",\n      \"frontendDist\": \"../dist\"\n    },\n    \"bundle\": {\n      \"active\": true,\n      \"category\": \"DeveloperTool\",\n      \"copyright\": \"\",\n      \"externalBin\": [],\n      \"icon\": [\"icons/icon.png\"],\n      \"longDescription\": \"\",\n      \"macOS\": {\n        \"entitlements\": null,\n        \"exceptionDomain\": \"\",\n        \"frameworks\": [],\n        \"providerShortName\": null,\n        \"signingIdentity\": null\n      },\n      \"resources\": [],\n      \"shortDescription\": \"\",\n      \"targets\": \"all\",\n      \"windows\": {\n        \"certificateThumbprint\": null,\n        \"digestAlgorithm\": \"sha256\",\n        \"timestampUrl\": \"\"\n      }\n    },\n    \"app\": {\n      \"security\": {\n        \"csp\": null\n      },\n      \"windows\": [\n        {\n          \"fullscreen\": false,\n          \"height\": 800,\n          \"resizable\": true,\n          \"title\": \"LeptosChatApp\",\n          \"width\": 1200\n        }\n      ]\n    }\n  }\n  "
  },
  {
    "path": "reactive_graph/Cargo.toml",
    "content": "[package]\nname = \"reactive_graph\"\nversion = \"0.2.13\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"A fine-grained reactive graph for building user interfaces.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nany_spawner = { workspace = true }\nor_poisoned = { workspace = true }\nfutures = { workspace = true, default-features = true }\nhydration_context = { workspace = true, optional = true }\npin-project-lite = { workspace = true, default-features = true }\nrustc-hash = { workspace = true, default-features = true }\nserde = { features = [\n  \"derive\",\n], optional = true, workspace = true, default-features = true }\nslotmap = { workspace = true, default-features = true }\nthiserror = { workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\nguardian = { workspace = true, default-features = true }\nasync-lock = { workspace = true, default-features = true }\nsend_wrapper = { features = [\n  \"futures\",\n], workspace = true, default-features = true }\nsubsecond = { workspace = true, default-features = true, optional = true }\nindexmap = { workspace = true, default-features = true }\npaste = { workspace = true, default-features = true }\n\n[target.'cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))'.dependencies]\nweb-sys = { workspace = true, features = [\"console\"] }\n\n[dev-dependencies]\ntokio = { features = [\n  \"rt-multi-thread\",\n  \"macros\",\n], workspace = true, default-features = true }\ntokio-test = { workspace = true, default-features = true }\nany_spawner = { workspace = true, features = [\"futures-executor\", \"tokio\"] }\ntyped-builder.workspace = true\n\n[build-dependencies]\nrustc_version = { workspace = true, default-features = true }\n\n[features]\nnightly = []\nserde = [\"dep:serde\"]\ntracing = [\"dep:tracing\"]\nhydration = [\"dep:hydration_context\"]\neffects = [\n] # whether to run effects: should be disabled for something like server rendering\nsandboxed-arenas = []\nsubsecond = [\"dep:subsecond\"]\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\"]\nmax_combination_size = 2\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = [\n  'cfg(leptos_debuginfo)',\n  'cfg(rustc_nightly)',\n] }\n"
  },
  {
    "path": "reactive_graph/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "reactive_graph/README.md",
    "content": "An implementation of a fine-grained reactive system.\n\nFine-grained reactivity is an approach to modeling the flow of data through an interactive\napplication by composing together three categories of reactive primitives:\n\n1. **Signals**: atomic units of state, which can be directly mutated.\n2. **Computations**: derived values, which cannot be mutated directly but update whenever the signals\n   they depend on change. These include both synchronous and asynchronous derived values.\n3. **Effects**: side effects that synchronize the reactive system with the non-reactive world\n   outside it.\n\nSignals and computations are \"source\" nodes in the reactive graph, because an observer can\nsubscribe to them to respond to changes in their values. Effects and computations are \"subscriber\"\nnodes, because they can listen to changes in other values.\n\n```rust\nuse reactive_graph::{\n    computed::ArcMemo,\n    effect::Effect,\n    prelude::{Read, Set},\n    signal::ArcRwSignal,\n};\n\nlet count = ArcRwSignal::new(1);\nlet double_count = ArcMemo::new({\n    let count = count.clone();\n    move |_| *count.read() * 2\n});\n\n// the effect will run once initially\nEffect::new(move |_| {\n    println!(\"double_count = {}\", *double_count.read());\n});\n\n// updating `count` will propagate changes to the dependencies,\n// causing the effect to run again\ncount.set(2);\n```\n\nThis reactivity is called \"fine grained\" because updating the value of a signal only affects\nthe effects and computations that depend on its value, without requiring any diffing or update\ncalculations for other values.\n\nThis model is especially suitable for building user interfaces, i.e., long-lived systems in\nwhich changes can begin from many different entry points. It is not particularly useful in\n\"run-once\" programs like a CLI.\n\n## Design Principles and Assumptions\n\n- **Effects are expensive.** The library is built on the assumption that the side effects\n  (making a network request, rendering something to the DOM, writing to disk) are orders of\n  magnitude more expensive than propagating signal updates. As a result, the algorithm is\n  designed to avoid re-running side effects unnecessarily, and is willing to sacrifice a small\n  amount of raw update speed to that goal.\n- **Automatic dependency tracking.** Dependencies are not specified as a compile-time list, but\n  tracked at runtime. This in turn enables **dynamic dependency tracking**: subscribers\n  unsubscribe from their sources between runs, which means that a subscriber that contains a\n  condition branch will not re-run when dependencies update that are only used in the inactive\n  branch.\n- **Asynchronous effect scheduling.** Effects are spawned as asynchronous tasks. This means\n  that while updating a signal will immediately update its value, effects that depend on it\n  will not run until the next \"tick\" of the async runtime. (This in turn means that the\n  reactive system is _async runtime agnostic_: it can be used in the browser with\n  `wasm-bindgen-futures`, in a native binary with `tokio`, in a GTK application with `glib`,\n  etc.)\n\nThe reactive-graph algorithm used in this crate is based on that of\n[Reactively](https://github.com/modderme123/reactively), as described\n[in this article](https://dev.to/modderme123/super-charging-fine-grained-reactive-performance-47ph).\n"
  },
  {
    "path": "reactive_graph/build.rs",
    "content": "use rustc_version::{version_meta, Channel};\n\nfn main() {\n    // Set cfg flags depending on release channel\n    if matches!(version_meta().unwrap().channel, Channel::Nightly) {\n        println!(\"cargo:rustc-cfg=rustc_nightly\");\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/actions/action.rs",
    "content": "use crate::{\n    computed::{ArcMemo, Memo, ScopedFuture},\n    diagnostics::is_suppressing_resource_load,\n    graph::untrack,\n    owner::{ArcStoredValue, ArenaItem, Owner},\n    send_wrapper_ext::SendOption,\n    signal::{ArcMappedSignal, ArcRwSignal, MappedSignal, RwSignal},\n    traits::{DefinedAt, Dispose, Get, GetUntracked, GetValue, Update, Write},\n    unwrap_signal,\n};\nuse any_spawner::Executor;\nuse futures::{channel::oneshot, select, FutureExt};\nuse send_wrapper::SendWrapper;\nuse std::{\n    future::Future,\n    ops::{Deref, DerefMut},\n    panic::Location,\n    pin::Pin,\n    sync::Arc,\n};\n\n/// An action runs some asynchronous code when you dispatch a new value to it, and gives you\n/// reactive access to the result.\n///\n/// Actions are intended for mutating or updating data, not for loading data. If you find yourself\n/// creating an action and immediately dispatching a value to it, this is probably the wrong\n/// primitive.\n///\n/// The arena-allocated, `Copy` version of an `ArcAction` is an [`Action`].\n///\n/// ```rust\n/// # use reactive_graph::actions::*;\n/// # use reactive_graph::prelude::*;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n/// async fn send_new_todo_to_api(task: String) -> usize {\n///     // do something...\n///     // return a task id\n///     42\n/// }\n/// let save_data = ArcAction::new(|task: &String| {\n///   // `task` is given as `&String` because its value is available in `input`\n///   send_new_todo_to_api(task.clone())\n/// });\n///\n/// // the argument currently running\n/// let input = save_data.input();\n/// // the most recent returned result\n/// let result_of_call = save_data.value();\n/// // whether the call is pending\n/// let pending = save_data.pending();\n/// // how many times the action has run\n/// // useful for reactively updating something else in response to a `dispatch` and response\n/// let version = save_data.version();\n///\n/// // before we do anything\n/// assert_eq!(input.get(), None); // no argument yet\n/// assert_eq!(pending.get(), false); // isn't pending a response\n/// assert_eq!(result_of_call.get(), None); // there's no \"last value\"\n/// assert_eq!(version.get(), 0);\n///\n/// // dispatch the action\n/// save_data.dispatch(\"My todo\".to_string());\n///\n/// // when we're making the call\n/// assert_eq!(input.get(), Some(\"My todo\".to_string()));\n/// assert_eq!(pending.get(), true); // is pending\n/// assert_eq!(result_of_call.get(), None); // has not yet gotten a response\n///\n/// # any_spawner::Executor::tick().await;\n///\n/// // after call has resolved\n/// assert_eq!(input.get(), None); // input clears out after resolved\n/// assert_eq!(pending.get(), false); // no longer pending\n/// assert_eq!(result_of_call.get(), Some(42));\n/// assert_eq!(version.get(), 1);\n/// # });\n/// ```\n///\n/// The input to the `async` function should always be a single value,\n/// but it can be of any type. The argument is always passed by reference to the\n/// function, because it is stored in [Action::input] as well.\n///\n/// ```rust\n/// # use reactive_graph::actions::*;\n/// // if there's a single argument, just use that\n/// let action1 = ArcAction::new(|input: &String| {\n///     let input = input.clone();\n///     async move { todo!() }\n/// });\n///\n/// // if there are no arguments, use the unit type `()`\n/// let action2 = ArcAction::new(|input: &()| async { todo!() });\n///\n/// // if there are multiple arguments, use a tuple\n/// let action3 = ArcAction::new(|input: &(usize, String)| async { todo!() });\n/// ```\npub struct ArcAction<I, O> {\n    in_flight: ArcRwSignal<usize>,\n    input: ArcRwSignal<SendOption<I>>,\n    value: ArcRwSignal<SendOption<O>>,\n    version: ArcRwSignal<usize>,\n    dispatched: ArcStoredValue<usize>,\n    #[allow(clippy::complexity)]\n    action_fn: Arc<\n        dyn Fn(&I) -> Pin<Box<dyn Future<Output = O> + Send>> + Send + Sync,\n    >,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<I, O> Clone for ArcAction<I, O> {\n    fn clone(&self) -> Self {\n        Self {\n            in_flight: self.in_flight.clone(),\n            input: self.input.clone(),\n            value: self.value.clone(),\n            version: self.version.clone(),\n            dispatched: self.dispatched.clone(),\n            action_fn: self.action_fn.clone(),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n        }\n    }\n}\n\nimpl<I, O> ArcAction<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// Creates a new action. This is lazy: it does not run the action function until some value\n    /// is dispatched.\n    ///\n    /// The constructor takes a function which will create a new `Future` from some input data.\n    /// When the action is dispatched, this `action_fn` will run, and the `Future` it returns will\n    /// be spawned.\n    ///\n    /// The `action_fn` must be `Send + Sync` so that the `ArcAction` is `Send + Sync`. The\n    /// `Future` must be `Send` so that it can be moved across threads by the async executor as\n    /// needed.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = ArcAction::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// act.dispatch(3);\n    /// assert_eq!(act.input().get(), Some(3));\n    ///\n    /// // Remember that async functions already return a future if they are\n    /// // not `await`ed. You can save keystrokes by leaving out the `async move`\n    ///\n    /// let act2 = Action::new(|n: &String| yell(n.to_owned()));\n    /// act2.dispatch(String::from(\"i'm in a doctest\"));\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    ///\n    /// // after it resolves\n    /// assert_eq!(act2.value().get(), Some(\"I'M IN A DOCTEST\".to_string()));\n    ///\n    /// async fn yell(n: String) -> String {\n    ///     n.to_uppercase()\n    /// }\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn new<F, Fu>(action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + Send + Sync + 'static,\n        Fu: Future<Output = O> + Send + 'static,\n        I: Send + Sync,\n        O: Send + Sync,\n    {\n        Self::new_with_value(None, action_fn)\n    }\n\n    /// Creates a new action, initializing it with the given value.\n    ///\n    /// This is lazy: it does not run the action function until some value is dispatched.\n    ///\n    /// The constructor takes a function which will create a new `Future` from some input data.\n    /// When the action is dispatched, this `action_fn` will run, and the `Future` it returns will\n    /// be spawned.\n    ///\n    /// The `action_fn` must be `Send + Sync` so that the `ArcAction` is `Send + Sync`. The\n    /// `Future` must be `Send` so that it can be moved across threads by the async executor as\n    /// needed.\n    #[track_caller]\n    pub fn new_with_value<F, Fu>(value: Option<O>, action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + Send + Sync + 'static,\n        Fu: Future<Output = O> + Send + 'static,\n        I: Send + Sync,\n        O: Send + Sync,\n    {\n        let weak_owner = Owner::current().map(|o| o.downgrade());\n        ArcAction {\n            in_flight: ArcRwSignal::new(0),\n            input: ArcRwSignal::new(SendOption::new(None)),\n            value: ArcRwSignal::new(SendOption::new(value)),\n            version: Default::default(),\n            dispatched: Default::default(),\n            action_fn: Arc::new(move |input| {\n                let fut = untrack(|| action_fn(input));\n                match weak_owner.as_ref().and_then(|w| w.upgrade()) {\n                    Some(owner) => Box::pin(\n                        owner.with(|| ScopedFuture::new_untracked(fut)),\n                    )\n                        as Pin<Box<dyn Future<Output = O> + Send>>,\n                    None => Box::pin(ScopedFuture::new_untracked(fut)),\n                }\n            }),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Clears the value of the action, setting its current value to `None`.\n    ///\n    /// This has no other effect: i.e., it will not cancel in-flight actions, set the\n    /// input, etc.\n    #[track_caller]\n    pub fn clear(&self) {\n        if let Some(mut guard) = self.value.try_write() {\n            **guard = None;\n        }\n    }\n}\n\n/// A handle that allows aborting an in-flight action. It is returned from [`Action::dispatch`] or\n/// [`ArcAction::dispatch`].\n#[derive(Debug)]\npub struct ActionAbortHandle(oneshot::Sender<()>);\n\nimpl ActionAbortHandle {\n    /// Aborts the action.\n    ///\n    /// This will cause the dispatched task to complete, without updating the action's value. The\n    /// dispatched action's `Future` will no longer be polled. This does not guarantee that side\n    /// effects created by that `Future` no longer run: for example, if the action dispatches an\n    /// HTTP request, whether that request is actually canceled or not depends on whether the\n    /// request library actually cancels a request when its `Future` is dropped.\n    pub fn abort(self) {\n        let _ = self.0.send(());\n    }\n}\n\nimpl<I, O> ArcAction<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n{\n    /// Calls the `async` function with a reference to the input type as its argument.\n    #[track_caller]\n    pub fn dispatch(&self, input: I) -> ActionAbortHandle {\n        let (abort_tx, mut abort_rx) = oneshot::channel();\n        if !is_suppressing_resource_load() {\n            let mut fut = (self.action_fn)(&input).fuse();\n\n            // Update the state before loading\n            self.in_flight.update(|n| *n += 1);\n            let current_version = self.dispatched.get_value();\n            self.input.try_update(|inp| **inp = Some(input));\n\n            // Spawn the task\n            crate::spawn({\n                let input = self.input.clone();\n                let version = self.version.clone();\n                let dispatched = self.dispatched.clone();\n                let value = self.value.clone();\n                let in_flight = self.in_flight.clone();\n                async move {\n                    select! {\n                        // if the abort message has been sent, bail and do nothing\n                        _ = abort_rx => {\n                            in_flight.update(|n| *n = n.saturating_sub(1));\n                        },\n                        // otherwise, update the value\n                        result = fut => {\n                            in_flight.update(|n| *n = n.saturating_sub(1));\n                            let is_latest = dispatched.get_value() <= current_version;\n                            if is_latest {\n                                version.update(|n| *n += 1);\n                                value.update(|n| **n = Some(result));\n                            }\n                        }\n                    }\n                    if in_flight.get_untracked() == 0 {\n                        input.update(|inp| **inp = None);\n                    }\n                }\n            });\n        }\n\n        ActionAbortHandle(abort_tx)\n    }\n}\n\nimpl<I, O> ArcAction<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// Calls the `async` function with a reference to the input type as its argument,\n    /// ensuring that it is spawned on the current thread.\n    #[track_caller]\n    pub fn dispatch_local(&self, input: I) -> ActionAbortHandle {\n        let (abort_tx, mut abort_rx) = oneshot::channel();\n        if !is_suppressing_resource_load() {\n            let mut fut = (self.action_fn)(&input).fuse();\n\n            // Update the state before loading\n            self.in_flight.update(|n| *n += 1);\n            let current_version = self.dispatched.get_value();\n            self.input.try_update(|inp| **inp = Some(input));\n\n            // Spawn the task\n            Executor::spawn_local({\n                let input = self.input.clone();\n                let version = self.version.clone();\n                let value = self.value.clone();\n                let dispatched = self.dispatched.clone();\n                let in_flight = self.in_flight.clone();\n                async move {\n                    select! {\n                        // if the abort message has been sent, bail and do nothing\n                        _ = abort_rx => {\n                            in_flight.update(|n| *n = n.saturating_sub(1));\n                        },\n                        // otherwise, update the value\n                        result = fut => {\n                            in_flight.update(|n| *n = n.saturating_sub(1));\n                            let is_latest = dispatched.get_value() <= current_version;\n                            if is_latest {\n                                version.update(|n| *n += 1);\n                                value.update(|n| **n = Some(result));\n                            }\n                        }\n                    }\n                    if in_flight.get_untracked() == 0 {\n                        input.update(|inp| **inp = None);\n                    }\n                }\n            });\n        }\n        ActionAbortHandle(abort_tx)\n    }\n}\n\nimpl<I, O> ArcAction<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// Creates a new action, which will only be run on the thread in which it is created.\n    ///\n    /// In all other ways, this is identical to [`ArcAction::new`].\n    #[track_caller]\n    pub fn new_unsync<F, Fu>(action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + 'static,\n        Fu: Future<Output = O> + 'static,\n    {\n        let action_fn = move |inp: &I| SendWrapper::new(action_fn(inp));\n        Self::new_unsync_with_value(None, action_fn)\n    }\n\n    /// Creates a new action that will only run on the current thread, initializing it with the given value.\n    ///\n    /// In all other ways, this is identical to [`ArcAction::new_with_value`].\n    #[track_caller]\n    pub fn new_unsync_with_value<F, Fu>(value: Option<O>, action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + 'static,\n        Fu: Future<Output = O> + 'static,\n    {\n        let weak_owner = Owner::current().map(|o| o.downgrade());\n        let action_fn = SendWrapper::new(action_fn);\n        ArcAction {\n            in_flight: ArcRwSignal::new(0),\n            input: ArcRwSignal::new(SendOption::new_local(None)),\n            value: ArcRwSignal::new(SendOption::new_local(value)),\n            version: Default::default(),\n            dispatched: Default::default(),\n            action_fn: Arc::new(move |input| {\n                let fut = untrack(|| action_fn(input));\n                match weak_owner.as_ref().and_then(|w| w.upgrade()) {\n                    Some(owner) => Box::pin(SendWrapper::new(\n                        owner.with(|| ScopedFuture::new_untracked(fut)),\n                    )),\n                    None => Box::pin(SendWrapper::new(\n                        ScopedFuture::new_untracked(fut),\n                    )),\n                }\n            }),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<I, O> ArcAction<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// The number of times the action has successfully completed.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = ArcAction::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// let version = act.version();\n    /// act.dispatch(3);\n    /// assert_eq!(version.get(), 0);\n    ///\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    /// // after it resolves\n    /// assert_eq!(version.get(), 1);\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn version(&self) -> ArcRwSignal<usize> {\n        self.version.clone()\n    }\n\n    /// The current argument that was dispatched to the async function. This value will\n    /// be `Some` while we are waiting for it to resolve, and `None` after it has resolved.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = ArcAction::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// let input = act.input();\n    /// assert_eq!(input.get(), None);\n    /// act.dispatch(3);\n    /// assert_eq!(input.get(), Some(3));\n    ///\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    /// // after it resolves\n    /// assert_eq!(input.get(), None);\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn input(&self) -> ArcMappedSignal<Option<I>> {\n        ArcMappedSignal::new(\n            self.input.clone(),\n            |n| n.deref(),\n            |n| n.deref_mut(),\n        )\n    }\n\n    /// The most recent return value of the `async` function. This will be `None` before\n    /// the action has ever run successfully, and subsequently will always be `Some(_)`,\n    /// holding the old value until a new value has been received.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = ArcAction::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// let value = act.value();\n    /// assert_eq!(value.get(), None);\n    /// act.dispatch(3);\n    /// assert_eq!(value.get(), None);\n    ///\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    /// // after it resolves\n    /// assert_eq!(value.get(), Some(6));\n    /// // dispatch another value, and it still holds the old value\n    /// act.dispatch(3);\n    /// assert_eq!(value.get(), Some(6));\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn value(&self) -> ArcMappedSignal<Option<O>> {\n        ArcMappedSignal::new(\n            self.value.clone(),\n            |n| n.deref(),\n            |n| n.deref_mut(),\n        )\n    }\n\n    /// Whether the action has been dispatched and is currently waiting to resolve.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = ArcAction::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// let pending = act.pending();\n    /// assert_eq!(pending.get(), false);\n    /// act.dispatch(3);\n    /// assert_eq!(pending.get(), true);\n    ///\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    /// // after it resolves\n    /// assert_eq!(pending.get(), false);\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn pending(&self) -> ArcMemo<bool> {\n        let in_flight = self.in_flight.clone();\n        ArcMemo::new(move |_| in_flight.get() > 0)\n    }\n}\n\nimpl<I, O> DefinedAt for ArcAction<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\n/// An action runs some asynchronous code when you dispatch a new value to it, and gives you\n/// reactive access to the result.\n///\n/// Actions are intended for mutating or updating data, not for loading data. If you find yourself\n/// creating an action and immediately dispatching a value to it, this is probably the wrong\n/// primitive.\n///\n/// The reference-counted, `Clone` (but not `Copy` version of an `Action` is an [`ArcAction`].\n///\n/// ```rust\n/// # use reactive_graph::actions::*;\n/// # use reactive_graph::prelude::*;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n/// async fn send_new_todo_to_api(task: String) -> usize {\n///     // do something...\n///     // return a task id\n///     42\n/// }\n/// let save_data = Action::new(|task: &String| {\n///   // `task` is given as `&String` because its value is available in `input`\n///   send_new_todo_to_api(task.clone())\n/// });\n///\n/// // the argument currently running\n/// let input = save_data.input();\n/// // the most recent returned result\n/// let result_of_call = save_data.value();\n/// // whether the call is pending\n/// let pending = save_data.pending();\n/// // how many times the action has run\n/// // useful for reactively updating something else in response to a `dispatch` and response\n/// let version = save_data.version();\n///\n/// // before we do anything\n/// assert_eq!(input.get(), None); // no argument yet\n/// assert_eq!(pending.get(), false); // isn't pending a response\n/// assert_eq!(result_of_call.get(), None); // there's no \"last value\"\n/// assert_eq!(version.get(), 0);\n///\n/// // dispatch the action\n/// save_data.dispatch(\"My todo\".to_string());\n///\n/// // when we're making the call\n/// assert_eq!(input.get(), Some(\"My todo\".to_string()));\n/// assert_eq!(pending.get(), true); // is pending\n/// assert_eq!(result_of_call.get(), None); // has not yet gotten a response\n///\n/// # any_spawner::Executor::tick().await;\n///\n/// // after call has resolved\n/// assert_eq!(input.get(), None); // input clears out after resolved\n/// assert_eq!(pending.get(), false); // no longer pending\n/// assert_eq!(result_of_call.get(), Some(42));\n/// assert_eq!(version.get(), 1);\n/// # });\n/// ```\n///\n/// The input to the `async` function should always be a single value,\n/// but it can be of any type. The argument is always passed by reference to the\n/// function, because it is stored in [Action::input] as well.\n///\n/// ```rust\n/// # use reactive_graph::actions::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// // if there's a single argument, just use that\n/// let action1 = Action::new(|input: &String| {\n///     let input = input.clone();\n///     async move { todo!() }\n/// });\n///\n/// // if there are no arguments, use the unit type `()`\n/// let action2 = Action::new(|input: &()| async { todo!() });\n///\n/// // if there are multiple arguments, use a tuple\n/// let action3 = Action::new(|input: &(usize, String)| async { todo!() });\n/// ```\npub struct Action<I, O> {\n    inner: ArenaItem<ArcAction<I, O>>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<I, O> Dispose for Action<I, O> {\n    fn dispose(self) {\n        self.inner.dispose()\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n{\n    /// Creates a new action. This is lazy: it does not run the action function until some value\n    /// is dispatched.\n    ///\n    /// The constructor takes a function which will create a new `Future` from some input data.\n    /// When the action is dispatched, this `action_fn` will run, and the `Future` it returns will\n    /// be spawned.\n    ///\n    /// The `action_fn` must be `Send + Sync` so that the `ArcAction` is `Send + Sync`. The\n    /// `Future` must be `Send` so that it can be moved across threads by the async executor as\n    /// needed. In order to be stored in the `Copy` arena, the input and output types should also\n    /// be `Send + Sync`.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = Action::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// act.dispatch(3);\n    /// assert_eq!(act.input().get(), Some(3));\n    ///\n    /// // Remember that async functions already return a future if they are\n    /// // not `await`ed. You can save keystrokes by leaving out the `async move`\n    ///\n    /// let act2 = Action::new(|n: &String| yell(n.to_owned()));\n    /// act2.dispatch(String::from(\"i'm in a doctest\"));\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    ///\n    /// // after it resolves\n    /// assert_eq!(act2.value().get(), Some(\"I'M IN A DOCTEST\".to_string()));\n    ///\n    /// async fn yell(n: String) -> String {\n    ///     n.to_uppercase()\n    /// }\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn new<F, Fu>(action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + Send + Sync + 'static,\n        Fu: Future<Output = O> + Send + 'static,\n    {\n        Self {\n            inner: ArenaItem::new(ArcAction::new(action_fn)),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Creates a new action, initializing it with the given value.\n    ///\n    /// This is lazy: it does not run the action function until some value is dispatched.\n    ///\n    /// The constructor takes a function which will create a new `Future` from some input data.\n    /// When the action is dispatched, this `action_fn` will run, and the `Future` it returns will\n    /// be spawned.\n    ///\n    /// The `action_fn` must be `Send + Sync` so that the `ArcAction` is `Send + Sync`. The\n    /// `Future` must be `Send` so that it can be moved across threads by the async executor as\n    /// needed. In order to be stored in the `Copy` arena, the input and output types should also\n    /// be `Send + Sync`.\n    #[track_caller]\n    pub fn new_with_value<F, Fu>(value: Option<O>, action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + Send + Sync + 'static,\n        Fu: Future<Output = O> + Send + 'static,\n    {\n        Self {\n            inner: ArenaItem::new(ArcAction::new_with_value(value, action_fn)),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// Clears the value of the action, setting its current value to `None`.\n    ///\n    /// This has no other effect: i.e., it will not cancel in-flight actions, set the\n    /// input, etc.\n    #[track_caller]\n    pub fn clear(&self) {\n        self.inner.try_with_value(|inner| inner.clear());\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// Creates a new action, which does not require its inputs or outputs to be `Send`. In all other\n    /// ways, this is the same as [`Action::new`]. If this action is accessed from outside the\n    /// thread on which it was created, it panics.\n    #[track_caller]\n    pub fn new_local<F, Fu>(action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + 'static,\n        Fu: Future<Output = O> + 'static,\n    {\n        Self {\n            inner: ArenaItem::new(ArcAction::new_unsync(action_fn)),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Creates a new action with the initial value, which does not require its inputs or outputs to be `Send`. In all other\n    /// ways, this is the same as [`Action::new_with_value`]. If this action is accessed from outside the\n    /// thread on which it was created, it panics.\n    #[track_caller]\n    pub fn new_local_with_value<F, Fu>(value: Option<O>, action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + 'static,\n        Fu: Future<Output = O> + Send + 'static,\n    {\n        Self {\n            inner: ArenaItem::new(ArcAction::new_unsync_with_value(\n                value, action_fn,\n            )),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// The number of times the action has successfully completed.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = Action::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// let version = act.version();\n    /// act.dispatch(3);\n    /// assert_eq!(version.get(), 0);\n    ///\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    /// // after it resolves\n    /// assert_eq!(version.get(), 1);\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn version(&self) -> RwSignal<usize> {\n        let inner = self\n            .inner\n            .try_with_value(|inner| inner.version())\n            .unwrap_or_else(unwrap_signal!(self));\n        inner.into()\n    }\n\n    /// Whether the action has been dispatched and is currently waiting to resolve.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = Action::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// let pending = act.pending();\n    /// assert_eq!(pending.get(), false);\n    /// act.dispatch(3);\n    /// assert_eq!(pending.get(), true);\n    ///\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    /// // after it resolves\n    /// assert_eq!(pending.get(), false);\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn pending(&self) -> Memo<bool> {\n        let inner = self\n            .inner\n            .try_with_value(|inner| inner.pending())\n            .unwrap_or_else(unwrap_signal!(self));\n        inner.into()\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// The current argument that was dispatched to the async function. This value will\n    /// be `Some` while we are waiting for it to resolve, and `None` after it has resolved.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = Action::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// let input = act.input();\n    /// assert_eq!(input.get(), None);\n    /// act.dispatch(3);\n    /// assert_eq!(input.get(), Some(3));\n    ///\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    /// // after it resolves\n    /// assert_eq!(input.get(), None);\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn input(&self) -> MappedSignal<Option<I>> {\n        self.inner\n            .try_with_value(|inner| inner.input())\n            .unwrap_or_else(unwrap_signal!(self))\n            .into()\n    }\n\n    /// The current argument that was dispatched to the async function. This value will\n    /// be `Some` while we are waiting for it to resolve, and `None` after it has resolved.\n    ///\n    /// Returns a thread-local signal using [`LocalStorage`].\n    #[track_caller]\n    #[deprecated = \"You can now use .input() for any value, whether it's \\\n                    thread-safe or not.\"]\n    pub fn input_local(&self) -> MappedSignal<Option<I>> {\n        self.inner\n            .try_with_value(|inner| inner.input())\n            .unwrap_or_else(unwrap_signal!(self))\n            .into()\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// The most recent return value of the `async` function. This will be `None` before\n    /// the action has ever run successfully, and subsequently will always be `Some(_)`,\n    /// holding the old value until a new value has been received.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// let act = Action::new(|n: &u8| {\n    ///     let n = n.to_owned();\n    ///     async move { n * 2 }\n    /// });\n    ///\n    /// let value = act.value();\n    /// assert_eq!(value.get(), None);\n    /// act.dispatch(3);\n    /// assert_eq!(value.get(), None);\n    ///\n    /// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n    /// // after it resolves\n    /// assert_eq!(value.get(), Some(6));\n    /// // dispatch another value, and it still holds the old value\n    /// act.dispatch(3);\n    /// assert_eq!(value.get(), Some(6));\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn value(&self) -> MappedSignal<Option<O>> {\n        self.inner\n            .try_with_value(|inner| inner.value())\n            .unwrap_or_else(unwrap_signal!(self))\n            .into()\n    }\n\n    /// The most recent return value of the `async` function. This will be `None` before\n    /// the action has ever run successfully, and subsequently will always be `Some(_)`,\n    /// holding the old value until a new value has been received.\n    ///\n    /// Returns a thread-local signal using [`LocalStorage`].\n    #[deprecated = \"You can now use .value() for any value, whether it's \\\n                    thread-safe or not.\"]\n    #[track_caller]\n    pub fn value_local(&self) -> MappedSignal<Option<O>>\n    where\n        O: Send + Sync,\n    {\n        self.inner\n            .try_with_value(|inner| inner.value())\n            .unwrap_or_else(unwrap_signal!(self))\n            .into()\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n{\n    /// Calls the `async` function with a reference to the input type as its argument.\n    #[track_caller]\n    pub fn dispatch(&self, input: I) -> ActionAbortHandle {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.dispatch(input))\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// Calls the `async` function with a reference to the input type as its argument.\n    #[track_caller]\n    pub fn dispatch_local(&self, input: I) -> ActionAbortHandle {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.dispatch_local(input))\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n{\n    /// Creates a new action, which does not require the action itself to be `Send`, but will run\n    /// it on the same thread it was created on.\n    ///\n    /// In all other ways, this is identical to [`Action::new`].\n    #[track_caller]\n    pub fn new_unsync<F, Fu>(action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + 'static,\n        Fu: Future<Output = O> + 'static,\n    {\n        Self {\n            inner: ArenaItem::new_with_storage(ArcAction::new_unsync(\n                action_fn,\n            )),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Creates a new action, which does not require the action itself to be `Send`, but will run\n    /// it on the same thread it was created on, and gives an initial value.\n    ///\n    /// In all other ways, this is identical to [`Action::new`].\n    #[track_caller]\n    pub fn new_unsync_with_value<F, Fu>(value: Option<O>, action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + 'static,\n        Fu: Future<Output = O> + 'static,\n    {\n        Self {\n            inner: ArenaItem::new_with_storage(\n                ArcAction::new_unsync_with_value(value, action_fn),\n            ),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<I, O> Action<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// Creates a new action, which neither requires the action itself nor the\n    /// value it returns to be `Send`. If this action is accessed from outside the\n    /// thread on which it was created, it panics.\n    ///\n    /// This combines the features of [`Action::new_local`] and [`Action::new_unsync`].\n    #[track_caller]\n    pub fn new_unsync_local<F, Fu>(action_fn: F) -> Self\n    where\n        F: Fn(&I) -> Fu + 'static,\n        Fu: Future<Output = O> + 'static,\n    {\n        Self {\n            inner: ArenaItem::new_with_storage(ArcAction::new_unsync(\n                action_fn,\n            )),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n\n    /// Creates a new action, which neither requires the action itself nor the\n    /// value it returns to be `Send`, and provides it with an initial value.\n    /// If this action is accessed from outside the thread on which it was created, it panics.\n    ///\n    /// This combines the features of [`Action::new_local_with_value`] and\n    /// [`Action::new_unsync_with_value`].\n    #[track_caller]\n    pub fn new_unsync_local_with_value<F, Fu>(\n        value: Option<O>,\n        action_fn: F,\n    ) -> Self\n    where\n        F: Fn(&I) -> Fu + 'static,\n        Fu: Future<Output = O> + 'static,\n    {\n        Self {\n            inner: ArenaItem::new_with_storage(\n                ArcAction::new_unsync_with_value(value, action_fn),\n            ),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<I, O> DefinedAt for Action<I, O> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<I, O> Clone for Action<I, O> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<I, O> Copy for Action<I, O> {}\n\n/// Creates a new action. This is lazy: it does not run the action function until some value\n/// is dispatched.\n///\n/// The constructor takes a function which will create a new `Future` from some input data.\n/// When the action is dispatched, this `action_fn` will run, and the `Future` it returns will\n/// be spawned.\n///\n/// The `action_fn` must be `Send + Sync` so that the `ArcAction` is `Send + Sync`. The\n/// `Future` must be `Send` so that it can be moved across threads by the async executor as\n/// needed. In order to be stored in the `Copy` arena, the input and output types should also\n/// be `Send + Sync`.\n///\n/// ```rust\n/// # use reactive_graph::actions::*;\n/// # use reactive_graph::prelude::*;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n/// let act = Action::new(|n: &u8| {\n///     let n = n.to_owned();\n///     async move { n * 2 }\n/// });\n///\n/// act.dispatch(3);\n/// assert_eq!(act.input().get(), Some(3));\n///\n/// // Remember that async functions already return a future if they are\n/// // not `await`ed. You can save keystrokes by leaving out the `async move`\n///\n/// let act2 = Action::new(|n: &String| yell(n.to_owned()));\n/// act2.dispatch(String::from(\"i'm in a doctest\"));\n/// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;\n///\n/// // after it resolves\n/// assert_eq!(act2.value().get(), Some(\"I'M IN A DOCTEST\".to_string()));\n///\n/// async fn yell(n: String) -> String {\n///     n.to_uppercase()\n/// }\n/// # });\n/// ```\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `Action::new()` instead.\"]\npub fn create_action<I, O, F, Fu>(action_fn: F) -> Action<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n    F: Fn(&I) -> Fu + Send + Sync + 'static,\n    Fu: Future<Output = O> + Send + 'static,\n{\n    Action::new(action_fn)\n}\n"
  },
  {
    "path": "reactive_graph/src/actions/mod.rs",
    "content": "//! Reactive primitives to asynchronously update some value.\n\nmod action;\nmod multi_action;\npub use action::*;\npub use multi_action::*;\n"
  },
  {
    "path": "reactive_graph/src/actions/multi_action.rs",
    "content": "use crate::{\n    diagnostics::is_suppressing_resource_load,\n    owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},\n    signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},\n    traits::{DefinedAt, Dispose, GetUntracked, Set, Update},\n    unwrap_signal,\n};\nuse std::{fmt::Debug, future::Future, panic::Location, pin::Pin, sync::Arc};\n\n/// An action that synchronizes multiple imperative `async` calls to the reactive system,\n/// tracking the progress of each one.\n///\n/// Where an [`Action`](super::Action) fires a single call, a `MultiAction` allows you to\n/// keep track of multiple in-flight actions.\n///\n/// If you’re trying to load data by running an `async` function reactively, you probably\n/// want to use an [`AsyncDerived`](crate::computed::AsyncDerived) instead.\n/// If you’re trying to occasionally run an `async` function in response to something\n/// like a user adding a task to a todo list, you’re in the right place.\n///\n/// The reference-counted, `Clone` (but not `Copy` version of a `MultiAction` is an [`ArcMultiAction`].\n///\n/// ```rust\n/// # use reactive_graph::actions::*;\n/// # use reactive_graph::prelude::*;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n/// async fn send_new_todo_to_api(task: String) -> usize {\n///   // do something...\n///   // return a task id\n///   42\n/// }\n/// let add_todo = MultiAction::new(|task: &String| {\n///   // `task` is given as `&String` because its value is available in `input`\n///   send_new_todo_to_api(task.clone())\n/// });\n///\n/// add_todo.dispatch(\"Buy milk\".to_string());\n/// add_todo.dispatch(\"???\".to_string());\n/// add_todo.dispatch(\"Profit!!!\".to_string());\n///\n/// let submissions = add_todo.submissions();\n/// assert_eq!(submissions.with(Vec::len), 3);\n/// # });\n/// ```\npub struct MultiAction<I, O, S = SyncStorage> {\n    inner: ArenaItem<ArcMultiAction<I, O>, S>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<I, O, S> Dispose for MultiAction<I, O, S> {\n    fn dispose(self) {\n        self.inner.dispose()\n    }\n}\n\nimpl<I, O, S> DefinedAt for MultiAction<I, O, S>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<I, O, S> Copy for MultiAction<I, O, S>\nwhere\n    I: 'static,\n    O: 'static,\n{\n}\n\nimpl<I, O, S> Clone for MultiAction<I, O, S>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<I, O> MultiAction<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n{\n    /// Creates a new multi-action.\n    ///\n    /// The input to the `async` function should always be a single value,\n    /// but it can be of any type. The argument is always passed by reference to the\n    /// function, because it is stored in [Submission::input] as well.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// // if there's a single argument, just use that\n    /// let action1 = MultiAction::new(|input: &String| {\n    ///     let input = input.clone();\n    ///     async move { todo!() }\n    /// });\n    ///\n    /// // if there are no arguments, use the unit type `()`\n    /// let action2 = MultiAction::new(|input: &()| async { todo!() });\n    ///\n    /// // if there are multiple arguments, use a tuple\n    /// let action3 =\n    ///     MultiAction::new(|input: &(usize, String)| async { todo!() });\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn new<Fut>(\n        action_fn: impl Fn(&I) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        Fut: Future<Output = O> + Send + 'static,\n    {\n        Self {\n            inner: ArenaItem::new_with_storage(ArcMultiAction::new(action_fn)),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<I, O, S> MultiAction<I, O, S>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n    S: Storage<ArcMultiAction<I, O>>,\n{\n    /// Calls the `async` function with a reference to the input type as its argument.\n    ///\n    /// This can be called any number of times: each submission will be dispatched, running\n    /// concurrently, and its status can be checked via the\n    /// [`submissions()`](MultiAction::submissions) signal.\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// async fn send_new_todo_to_api(task: String) -> usize {\n    ///   // do something...\n    ///   // return a task id\n    ///   42\n    /// }\n    /// let add_todo = MultiAction::new(|task: &String| {\n    ///   // `task` is given as `&String` because its value is available in `input`\n    ///   send_new_todo_to_api(task.clone())\n    /// });\n    ///\n    /// let submissions = add_todo.submissions();\n    /// let pending_submissions = move || {\n    ///   submissions.with(|subs| subs.iter().filter(|sub| sub.pending().get()).count())\n    /// };\n    ///\n    /// add_todo.dispatch(\"Buy milk\".to_string());\n    /// assert_eq!(submissions.with(Vec::len), 1);\n    /// assert_eq!(pending_submissions(), 1);\n    ///\n    /// add_todo.dispatch(\"???\".to_string());\n    /// add_todo.dispatch(\"Profit!!!\".to_string());\n    ///\n    /// assert_eq!(submissions.with(Vec::len), 3);\n    /// assert_eq!(pending_submissions(), 3);\n    ///\n    /// // when submissions resolve, they are not removed from the set\n    /// // however, their `pending` signal is now `false`, and this can be used to filter them\n    /// # any_spawner::Executor::tick().await;\n    /// assert_eq!(submissions.with(Vec::len), 3);\n    /// assert_eq!(pending_submissions(), 0);\n    /// # });\n    /// ```\n    pub fn dispatch(&self, input: I) {\n        if !is_suppressing_resource_load() {\n            self.inner.try_with_value(|inner| inner.dispatch(input));\n        }\n    }\n\n    /// Synchronously adds a submission with the given value.\n    ///\n    /// This takes the output value, rather than the input, because it is adding a result, not an\n    /// input.\n    ///\n    /// This can be useful for use cases like handling errors, where the error can already be known\n    /// on the client side.\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// async fn send_new_todo_to_api(task: String) -> usize {\n    ///   // do something...\n    ///   // return a task id\n    ///   42\n    /// }\n    /// let add_todo = MultiAction::new(|task: &String| {\n    ///   // `task` is given as `&String` because its value is available in `input`\n    ///   send_new_todo_to_api(task.clone())\n    /// });\n    ///\n    /// let submissions = add_todo.submissions();\n    /// let pending_submissions = move || {\n    ///   submissions.with(|subs| subs.iter().filter(|sub| sub.pending().get()).count())\n    /// };\n    ///\n    /// add_todo.dispatch(\"Buy milk\".to_string());\n    /// assert_eq!(submissions.with(Vec::len), 1);\n    /// assert_eq!(pending_submissions(), 1);\n    ///\n    /// add_todo.dispatch_sync(42);\n    ///\n    /// assert_eq!(submissions.with(Vec::len), 2);\n    /// assert_eq!(pending_submissions(), 1);\n    /// # });\n    /// ```\n    pub fn dispatch_sync(&self, value: O) {\n        self.inner\n            .try_with_value(|inner| inner.dispatch_sync(value));\n    }\n}\n\nimpl<I, O> MultiAction<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n{\n    /// The set of all submissions to this multi-action.\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// async fn send_new_todo_to_api(task: String) -> usize {\n    ///   // do something...\n    ///   // return a task id\n    ///   42\n    /// }\n    /// let add_todo = MultiAction::new(|task: &String| {\n    ///   // `task` is given as `&String` because its value is available in `input`\n    ///   send_new_todo_to_api(task.clone())\n    /// });\n    ///\n    /// let submissions = add_todo.submissions();\n    ///\n    /// add_todo.dispatch(\"Buy milk\".to_string());\n    /// add_todo.dispatch(\"???\".to_string());\n    /// add_todo.dispatch(\"Profit!!!\".to_string());\n    ///\n    /// assert_eq!(submissions.with(Vec::len), 3);\n    /// # });\n    /// ```\n    pub fn submissions(&self) -> ReadSignal<Vec<ArcSubmission<I, O>>> {\n        self.inner\n            .try_with_value(|inner| inner.submissions())\n            .unwrap_or_else(unwrap_signal!(self))\n            .into()\n    }\n}\n\nimpl<I, O, S> MultiAction<I, O, S>\nwhere\n    I: 'static,\n    O: 'static,\n    S: Storage<ArcMultiAction<I, O>>\n        + Storage<ArcReadSignal<Vec<ArcSubmission<I, O>>>>,\n{\n    /// How many times an action has successfully resolved.\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// async fn send_new_todo_to_api(task: String) -> usize {\n    ///   // do something...\n    ///   // return a task id\n    ///   42\n    /// }\n    /// let add_todo = MultiAction::new(|task: &String| {\n    ///   // `task` is given as `&String` because its value is available in `input`\n    ///   send_new_todo_to_api(task.clone())\n    /// });\n    ///\n    /// let version = add_todo.version();\n    ///\n    /// add_todo.dispatch(\"Buy milk\".to_string());\n    /// add_todo.dispatch(\"???\".to_string());\n    /// add_todo.dispatch(\"Profit!!!\".to_string());\n    ///\n    /// assert_eq!(version.get(), 0);\n    /// # any_spawner::Executor::tick().await;\n    ///\n    /// // when they've all resolved\n    /// assert_eq!(version.get(), 3);\n    /// # });\n    /// ```\n    pub fn version(&self) -> RwSignal<usize> {\n        self.inner\n            .try_with_value(|inner| inner.version())\n            .unwrap_or_else(unwrap_signal!(self))\n            .into()\n    }\n}\n\n/// An action that synchronizes multiple imperative `async` calls to the reactive system,\n/// tracking the progress of each one.\n///\n/// Where an [`Action`](super::Action) fires a single call, a `MultiAction` allows you to\n/// keep track of multiple in-flight actions.\n///\n/// If you’re trying to load data by running an `async` function reactively, you probably\n/// want to use an [`AsyncDerived`](crate::computed::AsyncDerived) instead.\n/// If you’re trying to occasionally run an `async` function in response to something\n/// like a user adding a task to a todo list, you’re in the right place.\n///\n/// The arena-allocated, `Copy` version of an `ArcMultiAction` is a [`MultiAction`].\n///\n/// ```rust\n/// # use reactive_graph::actions::*;\n/// # use reactive_graph::prelude::*;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n/// async fn send_new_todo_to_api(task: String) -> usize {\n///   // do something...\n///   // return a task id\n///   42\n/// }\n/// let add_todo = ArcMultiAction::new(|task: &String| {\n///   // `task` is given as `&String` because its value is available in `input`\n///   send_new_todo_to_api(task.clone())\n/// });\n///\n/// add_todo.dispatch(\"Buy milk\".to_string());\n/// add_todo.dispatch(\"???\".to_string());\n/// add_todo.dispatch(\"Profit!!!\".to_string());\n///\n/// let submissions = add_todo.submissions();\n/// assert_eq!(submissions.with(Vec::len), 3);\n/// # });\n/// ```\npub struct ArcMultiAction<I, O> {\n    version: ArcRwSignal<usize>,\n    submissions: ArcRwSignal<Vec<ArcSubmission<I, O>>>,\n    #[allow(clippy::complexity)]\n    action_fn: Arc<\n        dyn Fn(&I) -> Pin<Box<dyn Future<Output = O> + Send>> + Send + Sync,\n    >,\n}\n\nimpl<I, O> Debug for ArcMultiAction<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ArcMultiAction\")\n            .field(\"version\", &self.version)\n            .field(\"submissions\", &self.submissions)\n            .finish()\n    }\n}\n\nimpl<I, O> Clone for ArcMultiAction<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    fn clone(&self) -> Self {\n        Self {\n            version: self.version.clone(),\n            submissions: self.submissions.clone(),\n            action_fn: Arc::clone(&self.action_fn),\n        }\n    }\n}\n\nimpl<I, O> ArcMultiAction<I, O> {\n    /// Creates a new multi-action.\n    ///\n    /// The input to the `async` function should always be a single value,\n    /// but it can be of any type. The argument is always passed by reference to the\n    /// function, because it is stored in [Submission::input] as well.\n    ///\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// // if there's a single argument, just use that\n    /// let action1 = ArcMultiAction::new(|input: &String| {\n    ///     let input = input.clone();\n    ///     async move { todo!() }\n    /// });\n    ///\n    /// // if there are no arguments, use the unit type `()`\n    /// let action2 = ArcMultiAction::new(|input: &()| async { todo!() });\n    ///\n    /// // if there are multiple arguments, use a tuple\n    /// let action3 =\n    ///     ArcMultiAction::new(|input: &(usize, String)| async { todo!() });\n    /// # });\n    /// ```\n    #[track_caller]\n    pub fn new<Fut>(\n        action_fn: impl Fn(&I) -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        Fut: Future<Output = O> + Send + 'static,\n    {\n        let action_fn = Arc::new(move |input: &I| {\n            let fut = action_fn(input);\n            Box::pin(fut) as Pin<Box<dyn Future<Output = O> + Send>>\n        });\n        Self {\n            version: ArcRwSignal::new(0),\n            submissions: ArcRwSignal::new(Vec::new()),\n            action_fn,\n        }\n    }\n}\n\nimpl<I, O> ArcMultiAction<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n{\n    /// Calls the `async` function with a reference to the input type as its argument.\n    ///\n    /// This can be called any number of times: each submission will be dispatched, running\n    /// concurrently, and its status can be checked via the\n    /// [`submissions()`](MultiAction::submissions) signal.\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// async fn send_new_todo_to_api(task: String) -> usize {\n    ///   // do something...\n    ///   // return a task id\n    ///   42\n    /// }\n    /// let add_todo = ArcMultiAction::new(|task: &String| {\n    ///   // `task` is given as `&String` because its value is available in `input`\n    ///   send_new_todo_to_api(task.clone())\n    /// });\n    ///\n    /// let submissions = add_todo.submissions();\n    /// let pending_submissions = {\n    ///     let submissions = submissions.clone();\n    ///     move || {\n    ///         submissions.with(|subs| subs.iter().filter(|sub| sub.pending().get()).count())\n    ///     }\n    /// };\n    ///\n    /// add_todo.dispatch(\"Buy milk\".to_string());\n    /// assert_eq!(submissions.with(Vec::len), 1);\n    /// assert_eq!(pending_submissions(), 1);\n    ///\n    /// add_todo.dispatch(\"???\".to_string());\n    /// add_todo.dispatch(\"Profit!!!\".to_string());\n    ///\n    /// assert_eq!(submissions.with(Vec::len), 3);\n    /// assert_eq!(pending_submissions(), 3);\n    ///\n    /// // when submissions resolve, they are not removed from the set\n    /// // however, their `pending` signal is now `false`, and this can be used to filter them\n    /// # any_spawner::Executor::tick().await;\n    /// assert_eq!(submissions.with(Vec::len), 3);\n    /// assert_eq!(pending_submissions(), 0);\n    /// # });\n    /// ```\n    pub fn dispatch(&self, input: I) {\n        if !is_suppressing_resource_load() {\n            let fut = (self.action_fn)(&input);\n\n            let submission = ArcSubmission {\n                input: ArcRwSignal::new(Some(input)),\n                value: ArcRwSignal::new(None),\n                pending: ArcRwSignal::new(true),\n                canceled: ArcRwSignal::new(false),\n            };\n\n            self.submissions\n                .try_update(|subs| subs.push(submission.clone()));\n\n            let version = self.version.clone();\n\n            crate::spawn(async move {\n                let new_value = fut.await;\n                let canceled = submission.canceled.get_untracked();\n                if !canceled {\n                    submission.value.try_set(Some(new_value));\n                }\n                submission.input.try_set(None);\n                submission.pending.try_set(false);\n                version.try_update(|n| *n += 1);\n            })\n        }\n    }\n\n    /// Synchronously adds a submission with the given value.\n    ///\n    /// This takes the output value, rather than the input, because it is adding a result, not an\n    /// input.\n    ///\n    /// This can be useful for use cases like handling errors, where the error can already be known\n    /// on the client side.\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// async fn send_new_todo_to_api(task: String) -> usize {\n    ///   // do something...\n    ///   // return a task id\n    ///   42\n    /// }\n    /// let add_todo = ArcMultiAction::new(|task: &String| {\n    ///   // `task` is given as `&String` because its value is available in `input`\n    ///   send_new_todo_to_api(task.clone())\n    /// });\n    ///\n    /// let submissions = add_todo.submissions();\n    /// let pending_submissions = {\n    ///     let submissions = submissions.clone();\n    ///     move || {\n    ///         submissions.with(|subs| subs.iter().filter(|sub| sub.pending().get()).count())\n    ///     }\n    /// };\n    ///\n    /// add_todo.dispatch(\"Buy milk\".to_string());\n    /// assert_eq!(submissions.with(Vec::len), 1);\n    /// assert_eq!(pending_submissions(), 1);\n    ///\n    /// add_todo.dispatch_sync(42);\n    ///\n    /// assert_eq!(submissions.with(Vec::len), 2);\n    /// assert_eq!(pending_submissions(), 1);\n    /// # });\n    /// ```\n    pub fn dispatch_sync(&self, value: O) {\n        let submission = ArcSubmission {\n            input: ArcRwSignal::new(None),\n            value: ArcRwSignal::new(Some(value)),\n            pending: ArcRwSignal::new(false),\n            canceled: ArcRwSignal::new(false),\n        };\n\n        self.submissions\n            .try_update(|subs| subs.push(submission.clone()));\n        self.version.try_update(|n| *n += 1);\n    }\n}\n\nimpl<I, O> ArcMultiAction<I, O> {\n    /// The set of all submissions to this multi-action.\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// async fn send_new_todo_to_api(task: String) -> usize {\n    ///   // do something...\n    ///   // return a task id\n    ///   42\n    /// }\n    /// let add_todo = ArcMultiAction::new(|task: &String| {\n    ///   // `task` is given as `&String` because its value is available in `input`\n    ///   send_new_todo_to_api(task.clone())\n    /// });\n    ///\n    /// let submissions = add_todo.submissions();\n    ///\n    /// add_todo.dispatch(\"Buy milk\".to_string());\n    /// add_todo.dispatch(\"???\".to_string());\n    /// add_todo.dispatch(\"Profit!!!\".to_string());\n    ///\n    /// assert_eq!(submissions.with(Vec::len), 3);\n    /// # });\n    /// ```\n    pub fn submissions(&self) -> ArcReadSignal<Vec<ArcSubmission<I, O>>> {\n        self.submissions.read_only()\n    }\n\n    /// How many times an action has successfully resolved.\n    /// ```rust\n    /// # use reactive_graph::actions::*;\n    /// # use reactive_graph::prelude::*;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n    /// async fn send_new_todo_to_api(task: String) -> usize {\n    ///   // do something...\n    ///   // return a task id\n    ///   42\n    /// }\n    /// let add_todo = ArcMultiAction::new(|task: &String| {\n    ///   // `task` is given as `&String` because its value is available in `input`\n    ///   send_new_todo_to_api(task.clone())\n    /// });\n    ///\n    /// let version = add_todo.version();\n    ///\n    /// add_todo.dispatch(\"Buy milk\".to_string());\n    /// add_todo.dispatch(\"???\".to_string());\n    /// add_todo.dispatch(\"Profit!!!\".to_string());\n    ///\n    /// assert_eq!(version.get(), 0);\n    /// # any_spawner::Executor::tick().await;\n    ///\n    /// // when they've all resolved\n    /// assert_eq!(version.get(), 3);\n    /// # });\n    /// ```\n    pub fn version(&self) -> ArcRwSignal<usize> {\n        self.version.clone()\n    }\n}\n\n/// An action that has been submitted by dispatching it to a [`MultiAction`].\n#[derive(Debug, PartialEq, Eq, Hash)]\npub struct ArcSubmission<I, O> {\n    /// The current argument that was dispatched to the `async` function.\n    /// `Some` while we are waiting for it to resolve, `None` if it has resolved.\n    input: ArcRwSignal<Option<I>>,\n    /// The most recent return value of the `async` function.\n    value: ArcRwSignal<Option<O>>,\n    pending: ArcRwSignal<bool>,\n    /// Controls this submission has been canceled.\n    canceled: ArcRwSignal<bool>,\n}\n\nimpl<I, O> ArcSubmission<I, O>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// The current argument that was dispatched to the `async` function.\n    /// `Some` while we are waiting for it to resolve, `None` if it has resolved.\n    #[track_caller]\n    pub fn input(&self) -> ArcReadSignal<Option<I>> {\n        self.input.read_only()\n    }\n\n    /// The most recent return value of the `async` function.\n    #[track_caller]\n    pub fn value(&self) -> ArcReadSignal<Option<O>> {\n        self.value.read_only()\n    }\n\n    /// Whether this submision is still waiting to resolve.\n    #[track_caller]\n    pub fn pending(&self) -> ArcReadSignal<bool> {\n        self.pending.read_only()\n    }\n\n    /// Whether this submission has been canceled.\n    #[track_caller]\n    pub fn canceled(&self) -> ArcReadSignal<bool> {\n        self.canceled.read_only()\n    }\n\n    /// Cancels the submission. This will not necessarily prevent the `Future`\n    /// from continuing to run, but it will update the returned value.\n    #[track_caller]\n    pub fn cancel(&self) {\n        // TODO if we set these up to race against a cancel signal, we could actually drop the\n        // futures\n        self.canceled.try_set(true);\n    }\n}\n\nimpl<I, O> Clone for ArcSubmission<I, O> {\n    fn clone(&self) -> Self {\n        Self {\n            input: self.input.clone(),\n            value: self.value.clone(),\n            pending: self.pending.clone(),\n            canceled: self.canceled.clone(),\n        }\n    }\n}\n\n/// An action that has been submitted by dispatching it to a [`MultiAction`].\n#[derive(Debug, PartialEq, Eq, Hash)]\npub struct Submission<I, O, S = SyncStorage>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    /// The current argument that was dispatched to the `async` function.\n    /// `Some` while we are waiting for it to resolve, `None` if it has resolved.\n    input: RwSignal<Option<I>, S>,\n    /// The most recent return value of the `async` function.\n    value: RwSignal<Option<O>, S>,\n    pending: RwSignal<bool>,\n    /// Controls this submission has been canceled.\n    canceled: RwSignal<bool>,\n}\n\nimpl<I, O> From<ArcSubmission<I, O>> for Submission<I, O>\nwhere\n    I: Send + Sync + 'static,\n    O: Send + Sync + 'static,\n{\n    fn from(value: ArcSubmission<I, O>) -> Self {\n        let ArcSubmission {\n            input,\n            value,\n            pending,\n            canceled,\n        } = value;\n        Self {\n            input: input.into(),\n            value: value.into(),\n            pending: pending.into(),\n            canceled: canceled.into(),\n        }\n    }\n}\n\nimpl<I, O> FromLocal<ArcSubmission<I, O>> for Submission<I, O, LocalStorage>\nwhere\n    I: 'static,\n    O: 'static,\n{\n    fn from_local(value: ArcSubmission<I, O>) -> Self {\n        let ArcSubmission {\n            input,\n            value,\n            pending,\n            canceled,\n        } = value;\n        Self {\n            input: RwSignal::from_local(input),\n            value: RwSignal::from_local(value),\n            pending: pending.into(),\n            canceled: canceled.into(),\n        }\n    }\n}\n\nimpl<I, O, S> Submission<I, O, S>\nwhere\n    S: Storage<ArcRwSignal<Option<I>>> + Storage<ArcReadSignal<Option<I>>>,\n{\n    /// The current argument that was dispatched to the `async` function.\n    /// `Some` while we are waiting for it to resolve, `None` if it has resolved.\n    #[track_caller]\n    pub fn input(&self) -> ReadSignal<Option<I>, S> {\n        self.input.read_only()\n    }\n}\n\nimpl<I, O, S> Submission<I, O, S>\nwhere\n    S: Storage<ArcRwSignal<Option<O>>> + Storage<ArcReadSignal<Option<O>>>,\n{\n    /// The most recent return value of the `async` function.\n    #[track_caller]\n    pub fn value(&self) -> ReadSignal<Option<O>, S> {\n        self.value.read_only()\n    }\n}\n\nimpl<I, O, S> Submission<I, O, S> {\n    /// Whether this submision is still waiting to resolve.\n    #[track_caller]\n    pub fn pending(&self) -> ReadSignal<bool> {\n        self.pending.read_only()\n    }\n\n    /// Whether this submission has been canceled.\n    #[track_caller]\n    pub fn canceled(&self) -> ReadSignal<bool> {\n        self.canceled.read_only()\n    }\n\n    /// Cancels the submission. This will not necessarily prevent the `Future`\n    /// from continuing to run, but it will update the returned value.\n    #[track_caller]\n    pub fn cancel(&self) {\n        self.canceled.try_set(true);\n    }\n}\n\nimpl<I, O, S> Clone for Submission<I, O, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<I, O, S> Copy for Submission<I, O, S> {}\n"
  },
  {
    "path": "reactive_graph/src/callback.rs",
    "content": "//! Callbacks define a standard way to store functions and closures. They are useful\n//! for component properties, because they can be used to define optional callback functions,\n//! which generic props don’t support.\n//!\n//! The callback types implement [`Copy`], so they can easily be moved into and out of other closures, just like signals.\n//!\n//! # Types\n//! This modules implements 2 callback types:\n//! - [`Callback`](reactive_graph::callback::Callback)\n//! - [`UnsyncCallback`](reactive_graph::callback::UnsyncCallback)\n//!\n//! Use `SyncCallback` if the function is not `Sync` and `Send`.\n\nuse crate::{\n    owner::{LocalStorage, StoredValue},\n    traits::{Dispose, WithValue},\n    IntoReactiveValue,\n};\nuse std::{fmt, rc::Rc, sync::Arc};\n\n/// A wrapper trait for calling callbacks.\npub trait Callable<In: 'static, Out: 'static = ()> {\n    /// calls the callback with the specified argument.\n    ///\n    /// Returns None if the callback has been disposed\n    fn try_run(&self, input: In) -> Option<Out>;\n    /// calls the callback with the specified argument.\n    ///\n    /// # Panics\n    /// Panics if you try to run a callback that has been disposed\n    fn run(&self, input: In) -> Out;\n}\n\n/// A callback type that is not required to be [`Send`] or [`Sync`].\n///\n/// # Example\n/// ```\n/// # use reactive_graph::prelude::*; use reactive_graph::callback::*;  let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let _: UnsyncCallback<()> = UnsyncCallback::new(|_| {});\n/// let _: UnsyncCallback<(i32, i32)> = (|_x: i32, _y: i32| {}).into();\n/// let cb: UnsyncCallback<i32, String> = UnsyncCallback::new(|x: i32| x.to_string());\n/// assert_eq!(cb.run(42), \"42\".to_string());\n/// ```\npub struct UnsyncCallback<In: 'static, Out: 'static = ()>(\n    StoredValue<Rc<dyn Fn(In) -> Out>, LocalStorage>,\n);\n\nimpl<In> fmt::Debug for UnsyncCallback<In> {\n    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        fmt.write_str(\"Callback\")\n    }\n}\n\nimpl<In, Out> Copy for UnsyncCallback<In, Out> {}\n\nimpl<In, Out> Clone for UnsyncCallback<In, Out> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<In, Out> Dispose for UnsyncCallback<In, Out> {\n    fn dispose(self) {\n        self.0.dispose();\n    }\n}\n\nimpl<In, Out> UnsyncCallback<In, Out> {\n    /// Creates a new callback from the given function.\n    pub fn new<F>(f: F) -> UnsyncCallback<In, Out>\n    where\n        F: Fn(In) -> Out + 'static,\n    {\n        Self(StoredValue::new_local(Rc::new(f)))\n    }\n\n    /// Returns `true` if both callbacks wrap the same underlying function pointer.\n    #[inline]\n    pub fn matches(&self, other: &Self) -> bool {\n        self.0.with_value(|self_value| {\n            other\n                .0\n                .with_value(|other_value| Rc::ptr_eq(self_value, other_value))\n        })\n    }\n}\n\nimpl<In: 'static, Out: 'static> Callable<In, Out> for UnsyncCallback<In, Out> {\n    fn try_run(&self, input: In) -> Option<Out> {\n        self.0.try_with_value(|fun| fun(input))\n    }\n\n    fn run(&self, input: In) -> Out {\n        self.0.with_value(|fun| fun(input))\n    }\n}\n\nmacro_rules! impl_unsync_callable_from_fn {\n    ($($arg:ident),*) => {\n        impl<F, $($arg,)* T, Out> From<F> for UnsyncCallback<($($arg,)*), Out>\n        where\n            F: Fn($($arg),*) -> T + 'static,\n            T: Into<Out> + 'static,\n            $($arg: 'static,)*\n        {\n            fn from(f: F) -> Self {\n                paste::paste!(\n                    Self::new(move |($([<$arg:lower>],)*)| f($([<$arg:lower>]),*).into())\n                )\n            }\n        }\n    };\n}\n\nimpl_unsync_callable_from_fn!();\nimpl_unsync_callable_from_fn!(P1);\nimpl_unsync_callable_from_fn!(P1, P2);\nimpl_unsync_callable_from_fn!(P1, P2, P3);\nimpl_unsync_callable_from_fn!(P1, P2, P3, P4);\nimpl_unsync_callable_from_fn!(P1, P2, P3, P4, P5);\nimpl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6);\nimpl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7);\nimpl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8);\nimpl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9);\nimpl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);\nimpl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11);\nimpl_unsync_callable_from_fn!(\n    P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12\n);\n\n/// A callback type that is [`Send`] + [`Sync`].\n///\n/// # Example\n/// ```\n/// # use reactive_graph::prelude::*; use reactive_graph::callback::*;  let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let _: Callback<()> = Callback::new(|_| {});\n/// let _: Callback<(i32, i32)> = (|_x: i32, _y: i32| {}).into();\n/// let cb: Callback<i32, String> = Callback::new(|x: i32| x.to_string());\n/// assert_eq!(cb.run(42), \"42\".to_string());\n/// ```\npub struct Callback<In, Out = ()>(\n    StoredValue<Arc<dyn Fn(In) -> Out + Send + Sync>>,\n)\nwhere\n    In: 'static,\n    Out: 'static;\n\nimpl<In, Out> fmt::Debug for Callback<In, Out> {\n    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        fmt.write_str(\"SyncCallback\")\n    }\n}\n\nimpl<In, Out> Callable<In, Out> for Callback<In, Out> {\n    fn try_run(&self, input: In) -> Option<Out> {\n        self.0.try_with_value(|fun| fun(input))\n    }\n\n    fn run(&self, input: In) -> Out {\n        self.0.with_value(|f| f(input))\n    }\n}\n\nimpl<In, Out> Clone for Callback<In, Out> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<In, Out> Dispose for Callback<In, Out> {\n    fn dispose(self) {\n        self.0.dispose();\n    }\n}\n\nimpl<In, Out> Copy for Callback<In, Out> {}\n\nmacro_rules! impl_callable_from_fn {\n    ($($arg:ident),*) => {\n        impl<F, $($arg,)* T, Out> From<F> for Callback<($($arg,)*), Out>\n        where\n            F: Fn($($arg),*) -> T + Send + Sync + 'static,\n            T: Into<Out> + 'static,\n            $($arg: Send + Sync + 'static,)*\n        {\n            fn from(f: F) -> Self {\n                paste::paste!(\n                    Self::new(move |($([<$arg:lower>],)*)| f($([<$arg:lower>]),*).into())\n                )\n            }\n        }\n    };\n}\n\nimpl_callable_from_fn!();\nimpl_callable_from_fn!(P1);\nimpl_callable_from_fn!(P1, P2);\nimpl_callable_from_fn!(P1, P2, P3);\nimpl_callable_from_fn!(P1, P2, P3, P4);\nimpl_callable_from_fn!(P1, P2, P3, P4, P5);\nimpl_callable_from_fn!(P1, P2, P3, P4, P5, P6);\nimpl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7);\nimpl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8);\nimpl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9);\nimpl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);\nimpl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11);\nimpl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12);\n\nimpl<In: 'static, Out: 'static> Callback<In, Out> {\n    /// Creates a new callback from the given function.\n    #[track_caller]\n    pub fn new<F>(fun: F) -> Self\n    where\n        F: Fn(In) -> Out + Send + Sync + 'static,\n    {\n        Self(StoredValue::new(Arc::new(fun)))\n    }\n\n    /// Returns `true` if both callbacks wrap the same underlying function pointer.\n    #[inline]\n    pub fn matches(&self, other: &Self) -> bool {\n        self.0\n            .try_with_value(|self_value| {\n                other.0.try_with_value(|other_value| {\n                    Arc::ptr_eq(self_value, other_value)\n                })\n            })\n            .flatten()\n            .unwrap_or(false)\n    }\n}\n\n#[doc(hidden)]\npub struct __IntoReactiveValueMarkerCallbackSingleParam;\n\n#[doc(hidden)]\npub struct __IntoReactiveValueMarkerCallbackStrOutputToString;\n\nimpl<I, O, F>\n    IntoReactiveValue<\n        Callback<I, O>,\n        __IntoReactiveValueMarkerCallbackSingleParam,\n    > for F\nwhere\n    F: Fn(I) -> O + Send + Sync + 'static,\n{\n    #[track_caller]\n    fn into_reactive_value(self) -> Callback<I, O> {\n        Callback::new(self)\n    }\n}\n\nimpl<I, O, F>\n    IntoReactiveValue<\n        UnsyncCallback<I, O>,\n        __IntoReactiveValueMarkerCallbackSingleParam,\n    > for F\nwhere\n    F: Fn(I) -> O + 'static,\n{\n    #[track_caller]\n    fn into_reactive_value(self) -> UnsyncCallback<I, O> {\n        UnsyncCallback::new(self)\n    }\n}\n\nimpl<I, F>\n    IntoReactiveValue<\n        Callback<I, String>,\n        __IntoReactiveValueMarkerCallbackStrOutputToString,\n    > for F\nwhere\n    F: Fn(I) -> &'static str + Send + Sync + 'static,\n{\n    #[track_caller]\n    fn into_reactive_value(self) -> Callback<I, String> {\n        Callback::new(move |i| self(i).to_string())\n    }\n}\n\nimpl<I, F>\n    IntoReactiveValue<\n        UnsyncCallback<I, String>,\n        __IntoReactiveValueMarkerCallbackStrOutputToString,\n    > for F\nwhere\n    F: Fn(I) -> &'static str + 'static,\n{\n    #[track_caller]\n    fn into_reactive_value(self) -> UnsyncCallback<I, String> {\n        UnsyncCallback::new(move |i| self(i).to_string())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::Callable;\n    use crate::{\n        callback::{Callback, UnsyncCallback},\n        owner::Owner,\n        traits::Dispose,\n        IntoReactiveValue,\n    };\n\n    struct NoClone {}\n\n    #[test]\n    fn clone_callback() {\n        let owner = Owner::new();\n        owner.set();\n\n        let callback = Callback::new(move |_no_clone: NoClone| NoClone {});\n        let _cloned = callback;\n    }\n\n    #[test]\n    fn clone_unsync_callback() {\n        let owner = Owner::new();\n        owner.set();\n\n        let callback =\n            UnsyncCallback::new(move |_no_clone: NoClone| NoClone {});\n        let _cloned = callback;\n    }\n\n    #[test]\n    fn runback_from() {\n        let owner = Owner::new();\n        owner.set();\n\n        let _callback: Callback<(), String> = (|| \"test\").into();\n        let _callback: Callback<(i32, String), String> =\n            (|num, s| format!(\"{num} {s}\")).into();\n        // Single params should work without needing the (foo,) tuple using IntoReactiveValue:\n        let _callback: Callback<usize, &'static str> =\n            (|_usize| \"test\").into_reactive_value();\n        let _callback: Callback<usize, String> =\n            (|_usize| \"test\").into_reactive_value();\n    }\n\n    #[test]\n    fn sync_callback_from() {\n        let owner = Owner::new();\n        owner.set();\n\n        let _callback: UnsyncCallback<(), String> = (|| \"test\").into();\n        let _callback: UnsyncCallback<(i32, String), String> =\n            (|num, s| format!(\"{num} {s}\")).into();\n        // Single params should work without needing the (foo,) tuple using IntoReactiveValue:\n        let _callback: UnsyncCallback<usize, &'static str> =\n            (|_usize| \"test\").into_reactive_value();\n        let _callback: UnsyncCallback<usize, String> =\n            (|_usize| \"test\").into_reactive_value();\n    }\n\n    #[test]\n    fn sync_callback_try_run() {\n        let owner = Owner::new();\n        owner.set();\n\n        let callback = Callback::new(move |arg| arg);\n        assert_eq!(callback.try_run((0,)), Some((0,)));\n        callback.dispose();\n        assert_eq!(callback.try_run((0,)), None);\n    }\n\n    #[test]\n    fn unsync_callback_try_run() {\n        let owner = Owner::new();\n        owner.set();\n\n        let callback = UnsyncCallback::new(move |arg| arg);\n        assert_eq!(callback.try_run((0,)), Some((0,)));\n        callback.dispose();\n        assert_eq!(callback.try_run((0,)), None);\n    }\n\n    #[test]\n    fn callback_matches_same() {\n        let owner = Owner::new();\n        owner.set();\n\n        let callback1 = Callback::new(|x: i32| x * 2);\n        let callback2 = callback1;\n        assert!(callback1.matches(&callback2));\n    }\n\n    #[test]\n    fn callback_matches_different() {\n        let owner = Owner::new();\n        owner.set();\n\n        let callback1 = Callback::new(|x: i32| x * 2);\n        let callback2 = Callback::new(|x: i32| x + 1);\n        assert!(!callback1.matches(&callback2));\n    }\n\n    #[test]\n    fn unsync_callback_matches_same() {\n        let owner = Owner::new();\n        owner.set();\n\n        let callback1 = UnsyncCallback::new(|x: i32| x * 2);\n        let callback2 = callback1;\n        assert!(callback1.matches(&callback2));\n    }\n\n    #[test]\n    fn unsync_callback_matches_different() {\n        let owner = Owner::new();\n        owner.set();\n\n        let callback1 = UnsyncCallback::new(|x: i32| x * 2);\n        let callback2 = UnsyncCallback::new(|x: i32| x + 1);\n        assert!(!callback1.matches(&callback2));\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/channel.rs",
    "content": "use core::sync::atomic::Ordering::Relaxed;\nuse futures::{task::AtomicWaker, Stream};\nuse std::{\n    fmt::Debug,\n    hash::Hash,\n    pin::Pin,\n    sync::{atomic::AtomicBool, Arc, Weak},\n    task::{Context, Poll},\n};\n\n#[derive(Debug)]\npub(crate) struct Sender(Arc<Inner>);\n\n#[derive(Debug)]\npub(crate) struct Receiver(Weak<Inner>);\n\n#[derive(Debug, Default)]\nstruct Inner {\n    waker: AtomicWaker,\n    set: AtomicBool,\n}\n\nimpl Drop for Inner {\n    fn drop(&mut self) {\n        // Sender holds a strong reference to Inner, and Receiver holds a weak reference to Inner,\n        // so this will run when the Sender is dropped.\n        //\n        // The Receiver is usually owned by a spawned async task that is always waiting on the next\n        // value from its Stream. While it's waiting, it continues owning all the data it has\n        // captured. That data will not be dropped until the the stream ends.\n        //\n        // If we don't wake the waker a final time here, the spawned task will continue waiting for\n        // a final message from the Receiver that never arrives, because the waker never wakes it\n        // up again. So we wake the waker a final time, which tries to upgrade the Receiver, which\n        // fails, which causes the stream to yield Poll::Ready(None), ending the stream, and\n        // therefore ending the task, and therefore dropping all data that the stream has\n        // captured, avoiding a memory leak.\n        self.waker.wake();\n    }\n}\n\npub fn channel() -> (Sender, Receiver) {\n    let inner = Arc::new(Inner {\n        waker: AtomicWaker::new(),\n        set: AtomicBool::new(false),\n    });\n    let rx = Arc::downgrade(&inner);\n    (Sender(inner), Receiver(rx))\n}\n\nimpl Sender {\n    pub fn notify(&mut self) {\n        self.0.set.store(true, Relaxed);\n        self.0.waker.wake();\n    }\n}\n\nimpl Stream for Receiver {\n    type Item = ();\n\n    fn poll_next(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Self::Item>> {\n        if let Some(inner) = self.0.upgrade() {\n            inner.waker.register(cx.waker());\n\n            if inner.set.swap(false, Relaxed) {\n                Poll::Ready(Some(()))\n            } else {\n                Poll::Pending\n            }\n        } else {\n            Poll::Ready(None)\n        }\n    }\n}\n\nimpl Hash for Sender {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        Arc::as_ptr(&self.0).hash(state)\n    }\n}\n\nimpl PartialEq for Sender {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.0, &other.0)\n    }\n}\n\nimpl Eq for Sender {}\n\nimpl Hash for Receiver {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        Weak::as_ptr(&self.0).hash(state)\n    }\n}\n\nimpl PartialEq for Receiver {\n    fn eq(&self, other: &Self) -> bool {\n        Weak::ptr_eq(&self.0, &other.0)\n    }\n}\n\nimpl Eq for Receiver {}\n"
  },
  {
    "path": "reactive_graph/src/computed/arc_memo.rs",
    "content": "use super::inner::MemoInner;\nuse crate::{\n    graph::{\n        AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,\n        ToAnySource, ToAnySubscriber,\n    },\n    owner::{Storage, StorageAccess, SyncStorage},\n    signal::{\n        guards::{Mapped, Plain, ReadGuard},\n        ArcReadSignal, ArcRwSignal,\n    },\n    traits::{DefinedAt, Get, IsDisposed, ReadUntracked},\n};\nuse core::fmt::Debug;\nuse std::{\n    hash::Hash,\n    panic::Location,\n    sync::{Arc, Weak},\n};\n\n/// An efficient derived reactive value based on other reactive values.\n///\n/// This is a reference-counted memo, which is `Clone` but not `Copy`.\n/// For arena-allocated `Copy` memos, use [`Memo`](super::Memo).\n///\n/// Unlike a \"derived signal,\" a memo comes with two guarantees:\n/// 1. The memo will only run *once* per change, no matter how many times you\n///    access its value.\n/// 2. The memo will only notify its dependents if the value of the computation changes.\n///\n/// This makes a memo the perfect tool for expensive computations.\n///\n/// Memos have a certain overhead compared to derived signals. In most cases, you should\n/// create a derived signal. But if the derivation calculation is expensive, you should\n/// create a memo.\n///\n/// As with an [`Effect`](crate::effect::Effect), the argument to the memo function is the previous value,\n/// i.e., the current value of the memo, which will be `None` for the initial calculation.\n///\n/// ## Examples\n/// ```\n/// # use reactive_graph::prelude::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # use reactive_graph::computed::*;\n/// # use reactive_graph::signal::signal;\n/// # fn really_expensive_computation(value: i32) -> i32 { value };\n/// let (value, set_value) = signal(0);\n///\n/// // 🆗 we could create a derived signal with a simple function\n/// let double_value = move || value.get() * 2;\n/// set_value.set(2);\n/// assert_eq!(double_value(), 4);\n///\n/// // but imagine the computation is really expensive\n/// let expensive = move || really_expensive_computation(value.get()); // lazy: doesn't run until called\n/// // 🆗 run #1: calls `really_expensive_computation` the first time\n/// println!(\"expensive = {}\", expensive());\n/// // ❌ run #2: this calls `really_expensive_computation` a second time!\n/// let some_value = expensive();\n///\n/// // instead, we create a memo\n/// // 🆗 run #1: the calculation runs once immediately\n/// let memoized = ArcMemo::new(move |_| really_expensive_computation(value.get()));\n/// // 🆗 reads the current value of the memo\n/// //    can be `memoized()` on nightly\n/// println!(\"memoized = {}\", memoized.get());\n/// // ✅ reads the current value **without re-running the calculation**\n/// let some_value = memoized.get();\n/// ```\n///\n/// ## Core Trait Implementations\n/// - [`.get()`](crate::traits::Get) clones the current value of the memo.\n///   If you call it within an effect, it will cause that effect to subscribe\n///   to the memo, and to re-run whenever the value of the memo changes.\n///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of\n///     the memo without reactively tracking it.\n/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the\n///   value of the memo by reference. If you call it within an effect, it will\n///   cause that effect to subscribe to the memo, and to re-run whenever the\n///   value of the memo changes.\n///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the\n///     current value of the memo without reactively tracking it.\n/// - [`.with()`](crate::traits::With) allows you to reactively access the memo’s\n///   value without cloning by applying a callback function.\n///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access\n///     the memo’s value by applying a callback function without reactively\n///     tracking it.\n/// - [`.to_stream()`](crate::traits::ToStream) converts the memo to an `async`\n///   stream of values.\n/// - [`::from_stream()`](crate::traits::FromStream) converts an `async` stream\n///   of values into a memo containing the latest value.\npub struct ArcMemo<T, S = SyncStorage>\nwhere\n    S: Storage<T>,\n{\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    inner: Arc<MemoInner<T, S>>,\n}\n\nimpl<T: 'static> ArcMemo<T, SyncStorage>\nwhere\n    SyncStorage: Storage<T>,\n{\n    /// Creates a new memo by passing a function that computes the value.\n    ///\n    /// This is lazy: the function will not be called until the memo's value is read for the first\n    /// time.\n    #[track_caller]\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    pub fn new(fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static) -> Self\n    where\n        T: PartialEq,\n    {\n        Self::new_with_compare(fun, |lhs, rhs| lhs.as_ref() != rhs.as_ref())\n    }\n\n    /// Creates a new memo by passing a function that computes the value, and a comparison function\n    /// that takes the previous value and the new value and returns `true` if the value has\n    /// changed.\n    ///\n    /// This is lazy: the function will not be called until the memo's value is read for the first\n    /// time.\n    #[track_caller]\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    pub fn new_with_compare(\n        fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static,\n        changed: fn(Option<&T>, Option<&T>) -> bool,\n    ) -> Self {\n        Self::new_owning(move |prev: Option<T>| {\n            let new_value = fun(prev.as_ref());\n            let changed = changed(prev.as_ref(), Some(&new_value));\n            (new_value, changed)\n        })\n    }\n\n    /// Creates a new memo by passing a function that computes the value.\n    ///\n    /// Unlike [`ArcMemo::new`](), this receives ownership of the previous value. As a result, it\n    /// must return both the new value and a `bool` that is `true` if the value has changed.\n    ///\n    /// This is lazy: the function will not be called until the memo's value is read for the first\n    /// time.\n    #[track_caller]\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    pub fn new_owning(\n        fun: impl Fn(Option<T>) -> (T, bool) + Send + Sync + 'static,\n    ) -> Self {\n        let inner = Arc::new_cyclic(|weak| {\n            let subscriber = AnySubscriber(\n                weak.as_ptr() as usize,\n                Weak::clone(weak) as Weak<dyn Subscriber + Send + Sync>,\n            );\n\n            MemoInner::new(Arc::new(fun), subscriber)\n        });\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner,\n        }\n    }\n}\n\nimpl<T, S> Clone for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            inner: Arc::clone(&self.inner),\n        }\n    }\n}\n\nimpl<T, S> DefinedAt for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    #[inline(always)]\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T, S> Debug for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ArcMemo\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"data\", &Arc::as_ptr(&self.inner))\n            .finish()\n    }\n}\n\nimpl<T, S> PartialEq for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.inner, &other.inner)\n    }\n}\n\nimpl<T, S> Eq for ArcMemo<T, S> where S: Storage<T> {}\n\nimpl<T, S> Hash for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        std::ptr::hash(&Arc::as_ptr(&self.inner), state);\n    }\n}\n\nimpl<T: 'static, S> ReactiveNode for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn mark_dirty(&self) {\n        self.inner.mark_dirty();\n    }\n\n    fn mark_check(&self) {\n        self.inner.mark_check();\n    }\n\n    fn mark_subscribers_check(&self) {\n        self.inner.mark_subscribers_check();\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        self.inner.update_if_necessary()\n    }\n}\n\nimpl<T: 'static, S> IsDisposed for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T: 'static, S> ToAnySource for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn to_any_source(&self) -> AnySource {\n        AnySource(\n            Arc::as_ptr(&self.inner) as usize,\n            Arc::downgrade(&self.inner) as Weak<dyn Source + Send + Sync>,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            self.defined_at,\n        )\n    }\n}\n\nimpl<T: 'static, S> Source for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        self.inner.add_subscriber(subscriber);\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        self.inner.remove_subscriber(subscriber);\n    }\n\n    fn clear_subscribers(&self) {\n        self.inner.clear_subscribers();\n    }\n}\n\nimpl<T: 'static, S> ToAnySubscriber for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        AnySubscriber(\n            Arc::as_ptr(&self.inner) as usize,\n            Arc::downgrade(&self.inner) as Weak<dyn Subscriber + Send + Sync>,\n        )\n    }\n}\n\nimpl<T: 'static, S> Subscriber for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn add_source(&self, source: AnySource) {\n        self.inner.add_source(source);\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        self.inner.clear_sources(subscriber);\n    }\n}\n\nimpl<T: 'static, S> ReadUntracked for ArcMemo<T, S>\nwhere\n    S: Storage<T>,\n{\n    type Value = ReadGuard<T, Mapped<Plain<Option<S::Wrapped>>, T>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.update_if_necessary();\n\n        Mapped::try_new(Arc::clone(&self.inner.value), |t| {\n            // safe to unwrap here because update_if_necessary\n            // guarantees the value is Some\n            t.as_ref().unwrap().as_borrowed()\n        })\n        .map(ReadGuard::new)\n    }\n}\n\nimpl<T> From<ArcReadSignal<T>> for ArcMemo<T, SyncStorage>\nwhere\n    T: Clone + PartialEq + Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcReadSignal<T>) -> Self {\n        ArcMemo::new(move |_| value.get())\n    }\n}\n\nimpl<T> From<ArcRwSignal<T>> for ArcMemo<T, SyncStorage>\nwhere\n    T: Clone + PartialEq + Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcRwSignal<T>) -> Self {\n        ArcMemo::new(move |_| value.get())\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed/async_derived/arc_async_derived.rs",
    "content": "use super::{\n    inner::{ArcAsyncDerivedInner, AsyncDerivedState},\n    AsyncDerivedReadyFuture, ScopedFuture,\n};\n#[cfg(feature = \"sandboxed-arenas\")]\nuse crate::owner::Sandboxed;\nuse crate::{\n    channel::channel,\n    computed::suspense::SuspenseContext,\n    diagnostics::SpecialNonReactiveFuture,\n    graph::{\n        AnySource, AnySubscriber, ReactiveNode, Source, SourceSet, Subscriber,\n        SubscriberSet, ToAnySource, ToAnySubscriber, WithObserver,\n    },\n    owner::{use_context, Owner},\n    send_wrapper_ext::SendOption,\n    signal::{\n        guards::{AsyncPlain, Mapped, MappedMut, ReadGuard, WriteGuard},\n        ArcTrigger,\n    },\n    traits::{\n        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,\n        Write,\n    },\n    transition::AsyncTransition,\n};\nuse async_lock::RwLock as AsyncRwLock;\nuse core::fmt::Debug;\nuse futures::{channel::oneshot, FutureExt, StreamExt};\nuse or_poisoned::OrPoisoned;\nuse std::{\n    future::Future,\n    mem,\n    ops::{Deref, DerefMut},\n    panic::Location,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc, RwLock, Weak,\n    },\n    task::Waker,\n};\n\n/// A reactive value that is derived by running an asynchronous computation in response to changes\n/// in its sources.\n///\n/// When one of its dependencies changes, this will re-run its async computation, then notify other\n/// values that depend on it that it has changed.\n///\n/// This is a reference-counted type, which is `Clone` but not `Copy`.\n/// For arena-allocated `Copy` memos, use [`AsyncDerived`](super::AsyncDerived).\n///\n/// ## Examples\n/// ```rust\n/// # use reactive_graph::computed::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # use reactive_graph::prelude::*;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n///\n/// let signal1 = RwSignal::new(0);\n/// let signal2 = RwSignal::new(0);\n/// let derived = ArcAsyncDerived::new(move || async move {\n///   // reactive values can be tracked anywhere in the `async` block\n///   let value1 = signal1.get();\n///   tokio::time::sleep(std::time::Duration::from_millis(25)).await;\n///   let value2 = signal2.get();\n///\n///   value1 + value2\n/// });\n///\n/// // the value can be accessed synchronously as `Option<T>`\n/// assert_eq!(derived.get(), None);\n/// // we can also .await the value, i.e., convert it into a Future\n/// assert_eq!(derived.clone().await, 0);\n/// assert_eq!(derived.get(), Some(0));\n///\n/// signal1.set(1);\n/// // while the new value is still pending, the signal holds the old value\n/// tokio::time::sleep(std::time::Duration::from_millis(5)).await;\n/// assert_eq!(derived.get(), Some(0));\n///\n/// // setting multiple dependencies will hold until the latest change is ready\n/// signal2.set(1);\n/// assert_eq!(derived.await, 2);\n/// # });\n/// ```\n///\n/// ## Core Trait Implementations\n/// - [`.get()`](crate::traits::Get) clones the current value as an `Option<T>`.\n///   If you call it within an effect, it will cause that effect to subscribe\n///   to the memo, and to re-run whenever the value of the memo changes.\n///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of\n///     without reactively tracking it.\n/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the\n///   value by reference. If you call it within an effect, it will\n///   cause that effect to subscribe to the memo, and to re-run whenever the\n///   value changes.\n///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the\n///     current value without reactively tracking it.\n/// - [`.with()`](crate::traits::With) allows you to reactively access the\n///   value without cloning by applying a callback function.\n///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access\n///     the value by applying a callback function without reactively\n///     tracking it.\n/// - [`IntoFuture`](std::future::Future) allows you to create a [`Future`] that resolves\n///   when this resource is done loading.\npub struct ArcAsyncDerived<T> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    // the current state of this signal\n    pub(crate) value: Arc<AsyncRwLock<SendOption<T>>>,\n    // holds wakers generated when you .await this\n    pub(crate) wakers: Arc<RwLock<Vec<Waker>>>,\n    pub(crate) inner: Arc<RwLock<ArcAsyncDerivedInner>>,\n    pub(crate) loading: Arc<AtomicBool>,\n}\n\n#[allow(dead_code)]\npub(crate) trait BlockingLock<T> {\n    fn blocking_read_arc(self: &Arc<Self>)\n        -> async_lock::RwLockReadGuardArc<T>;\n\n    fn blocking_write_arc(\n        self: &Arc<Self>,\n    ) -> async_lock::RwLockWriteGuardArc<T>;\n\n    fn blocking_read(&self) -> async_lock::RwLockReadGuard<'_, T>;\n\n    fn blocking_write(&self) -> async_lock::RwLockWriteGuard<'_, T>;\n}\n\nimpl<T> BlockingLock<T> for AsyncRwLock<T> {\n    fn blocking_read_arc(\n        self: &Arc<Self>,\n    ) -> async_lock::RwLockReadGuardArc<T> {\n        #[cfg(not(target_family = \"wasm\"))]\n        {\n            self.read_arc_blocking()\n        }\n        #[cfg(target_family = \"wasm\")]\n        {\n            self.read_arc().now_or_never().unwrap()\n        }\n    }\n\n    fn blocking_write_arc(\n        self: &Arc<Self>,\n    ) -> async_lock::RwLockWriteGuardArc<T> {\n        #[cfg(not(target_family = \"wasm\"))]\n        {\n            self.write_arc_blocking()\n        }\n        #[cfg(target_family = \"wasm\")]\n        {\n            self.write_arc().now_or_never().unwrap()\n        }\n    }\n\n    fn blocking_read(&self) -> async_lock::RwLockReadGuard<'_, T> {\n        #[cfg(not(target_family = \"wasm\"))]\n        {\n            self.read_blocking()\n        }\n        #[cfg(target_family = \"wasm\")]\n        {\n            self.read().now_or_never().unwrap()\n        }\n    }\n\n    fn blocking_write(&self) -> async_lock::RwLockWriteGuard<'_, T> {\n        #[cfg(not(target_family = \"wasm\"))]\n        {\n            self.write_blocking()\n        }\n        #[cfg(target_family = \"wasm\")]\n        {\n            self.write().now_or_never().unwrap()\n        }\n    }\n}\n\nimpl<T> Clone for ArcAsyncDerived<T> {\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            value: Arc::clone(&self.value),\n            wakers: Arc::clone(&self.wakers),\n            inner: Arc::clone(&self.inner),\n            loading: Arc::clone(&self.loading),\n        }\n    }\n}\n\nimpl<T> Debug for ArcAsyncDerived<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut f = f.debug_struct(\"ArcAsyncDerived\");\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        f.field(\"defined_at\", &self.defined_at);\n        f.finish_non_exhaustive()\n    }\n}\n\nimpl<T> DefinedAt for ArcAsyncDerived<T> {\n    #[inline(always)]\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\n// This helps create a derived async signal.\n// It needs to be implemented as a macro because it needs to be flexible over\n// whether `fun` returns a `Future` that is `Send`. Doing it as a function would,\n// as far as I can tell, require repeating most of the function body.\nmacro_rules! spawn_derived {\n    ($spawner:expr, $initial:ident, $fun:ident, $should_spawn:literal, $force_spawn:literal, $should_track:literal, $source:expr) => {{\n        let (notifier, mut rx) = channel();\n\n        let is_ready = $initial.is_some() && !$force_spawn;\n\n        let owner = Owner::new();\n        let inner = Arc::new(RwLock::new(ArcAsyncDerivedInner {\n            owner: owner.clone(),\n            notifier,\n            sources: SourceSet::new(),\n            subscribers: SubscriberSet::new(),\n            state: AsyncDerivedState::Clean,\n            version: 0,\n            suspenses: Vec::new(),\n            pending_suspenses: Vec::new()\n        }));\n        let value = Arc::new(AsyncRwLock::new($initial));\n        let wakers = Arc::new(RwLock::new(Vec::new()));\n\n        let this = ArcAsyncDerived {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: Arc::clone(&value),\n            wakers,\n            inner: Arc::clone(&inner),\n            loading: Arc::new(AtomicBool::new(!is_ready)),\n        };\n        let any_subscriber = this.to_any_subscriber();\n        let initial_fut = if $should_track {\n            owner.with_cleanup(|| {\n                any_subscriber\n                    .with_observer(|| ScopedFuture::new($fun()))\n            })\n        } else {\n            owner.with_cleanup(|| {\n                any_subscriber\n                    .with_observer_untracked(|| ScopedFuture::new($fun()))\n            })\n        };\n        #[cfg(feature = \"sandboxed-arenas\")]\n        let initial_fut = Sandboxed::new(initial_fut);\n        let mut initial_fut = Box::pin(initial_fut);\n\n        let (was_ready, mut initial_fut) = {\n            if is_ready {\n                (true, None)\n            } else {\n                // if we don't already know that it's ready, we need to poll once, initially\n                // so that the correct value is set synchronously\n                let initial = initial_fut.as_mut().now_or_never();\n                match initial {\n                    None => {\n                        inner.write().or_poisoned().notifier.notify();\n                        (false, Some(initial_fut))\n                    }\n                    Some(orig_value) => {\n                        let mut guard = this.inner.write().or_poisoned();\n\n                        guard.state = AsyncDerivedState::Clean;\n                        *value.blocking_write() = orig_value;\n                        this.loading.store(false, Ordering::Relaxed);\n                        (true, None)\n                    }\n                }\n            }\n        };\n\n        let mut first_run = {\n            let (ready_tx, ready_rx) = oneshot::channel();\n            if !was_ready {\n                AsyncTransition::register(ready_rx);\n            }\n            Some(ready_tx)\n        };\n\n        if was_ready {\n            first_run.take();\n        }\n\n        if let Some(source) = $source {\n            any_subscriber.with_observer(|| source.track());\n        }\n\n        if $should_spawn {\n            $spawner({\n                let value = Arc::downgrade(&this.value);\n                let inner = Arc::downgrade(&this.inner);\n                let wakers = Arc::downgrade(&this.wakers);\n                let loading = Arc::downgrade(&this.loading);\n                let fut = async move {\n                    // if the AsyncDerived has *already* been marked dirty (i.e., one of its\n                    // sources has changed after creation), we should throw out the Future\n                    // we already created, because its values might be stale\n                    let already_dirty = inner.upgrade()\n                        .as_ref()\n                        .and_then(|inner| inner.read().ok())\n                        .map(|inner| inner.state == AsyncDerivedState::Dirty)\n                        .unwrap_or(false);\n                    if already_dirty {\n                        initial_fut.take();\n                    }\n\n                    while rx.next().await.is_some() {\n                        let update_if_necessary = !owner.paused() && if $should_track {\n                            any_subscriber\n                                .with_observer(|| any_subscriber.update_if_necessary())\n                        } else {\n                            any_subscriber\n                                .with_observer_untracked(|| any_subscriber.update_if_necessary())\n                        };\n                        if update_if_necessary || first_run.is_some() {\n                            match (value.upgrade(), inner.upgrade(), wakers.upgrade(), loading.upgrade()) {\n                                (Some(value), Some(inner), Some(wakers), Some(loading)) => {\n                                    // generate new Future\n                                    let owner = inner.read().or_poisoned().owner.clone();\n                                    let fut = initial_fut.take().unwrap_or_else(|| {\n                                        let fut = if $should_track {\n                                            owner.with_cleanup(|| {\n                                                any_subscriber\n                                                    .with_observer(|| ScopedFuture::new($fun()))\n                                            })\n                                        } else {\n                                            owner.with_cleanup(|| {\n                                                any_subscriber\n                                                    .with_observer_untracked(|| ScopedFuture::new($fun()))\n                                            })\n                                        };\n                                        #[cfg(feature = \"sandboxed-arenas\")]\n                                        let fut = Sandboxed::new(fut);\n                                        Box::pin(fut)\n                                    });\n\n                                    // register with global transition listener, if any\n                                    let ready_tx = first_run.take().unwrap_or_else(|| {\n                                        let (ready_tx, ready_rx) = oneshot::channel();\n                                        if !was_ready {\n                                            AsyncTransition::register(ready_rx);\n                                        }\n                                        ready_tx\n                                    });\n\n                                    // generate and assign new value\n                                    loading.store(true, Ordering::Relaxed);\n\n                                    let this_version = {\n                                        let mut guard = inner.write().or_poisoned();\n                                        guard.version += 1;\n                                        let version = guard.version;\n                                        let suspense_ids = mem::take(&mut guard.suspenses)\n                                            .into_iter()\n                                            .map(|sc| sc.task_id())\n                                            .collect::<Vec<_>>();\n                                        guard.pending_suspenses.extend(suspense_ids);\n                                        version\n                                    };\n\n                                    let new_value = fut.await;\n\n                                    let latest_version = {\n                                        let mut guard = inner.write().or_poisoned();\n                                        drop(mem::take(&mut guard.pending_suspenses));\n                                        guard.version\n                                    };\n\n                                    if latest_version == this_version {\n                                        Self::set_inner_value(new_value, value, wakers, inner, loading, Some(ready_tx)).await;\n                                    }\n                                }\n                                _ => break,\n                            }\n                        }\n                    }\n                };\n\n                #[cfg(feature = \"sandboxed-arenas\")]\n                let fut = Sandboxed::new(fut);\n\n                fut\n            });\n        }\n\n        (this, is_ready)\n    }};\n}\n\nimpl<T: 'static> ArcAsyncDerived<T> {\n    async fn set_inner_value(\n        new_value: SendOption<T>,\n        value: Arc<AsyncRwLock<SendOption<T>>>,\n        wakers: Arc<RwLock<Vec<Waker>>>,\n        inner: Arc<RwLock<ArcAsyncDerivedInner>>,\n        loading: Arc<AtomicBool>,\n        ready_tx: Option<oneshot::Sender<()>>,\n    ) {\n        *value.write().await.deref_mut() = new_value;\n        Self::notify_subs(&wakers, &inner, &loading, ready_tx);\n    }\n\n    fn notify_subs(\n        wakers: &Arc<RwLock<Vec<Waker>>>,\n        inner: &Arc<RwLock<ArcAsyncDerivedInner>>,\n        loading: &Arc<AtomicBool>,\n        ready_tx: Option<oneshot::Sender<()>>,\n    ) {\n        loading.store(false, Ordering::Relaxed);\n\n        let prev_state = mem::replace(\n            &mut inner.write().or_poisoned().state,\n            AsyncDerivedState::Notifying,\n        );\n\n        if let Some(ready_tx) = ready_tx {\n            // if it's an Err, that just means the Receiver was dropped\n            // we don't particularly care about that: the point is to notify if\n            // it still exists, but we don't need to know if Suspense is no\n            // longer listening\n            _ = ready_tx.send(());\n        }\n\n        // notify reactive subscribers that we're not loading any more\n        for sub in (&inner.read().or_poisoned().subscribers).into_iter() {\n            sub.mark_dirty();\n        }\n\n        // notify async .awaiters\n        for waker in mem::take(&mut *wakers.write().or_poisoned()) {\n            waker.wake();\n        }\n\n        // if this was marked dirty before notifications began, this means it\n        // had been notified while loading; marking it clean will cause it not to\n        // run on the next tick of the async loop, so here it should be left dirty\n        inner.write().or_poisoned().state = prev_state;\n    }\n}\n\nimpl<T: 'static> ArcAsyncDerived<T> {\n    /// Creates a new async derived computation.\n    ///\n    /// This runs eagerly: i.e., calls `fun` once when created and immediately spawns the `Future`\n    /// as a new task.\n    #[track_caller]\n    pub fn new<Fut>(fun: impl Fn() -> Fut + Send + Sync + 'static) -> Self\n    where\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Self::new_with_initial(None, fun)\n    }\n\n    /// Creates a new async derived computation with an initial value as a fallback, and begins running the\n    /// `Future` eagerly to get the actual first value.\n    #[track_caller]\n    pub fn new_with_initial<Fut>(\n        initial_value: Option<T>,\n        fun: impl Fn() -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        let fun = move || {\n            let fut = fun();\n            let fut = async move { SendOption::new(Some(fut.await)) };\n            #[cfg(feature = \"sandboxed-arenas\")]\n            let fut = Sandboxed::new(fut);\n            fut\n        };\n        let initial_value = SendOption::new(initial_value);\n        let (this, _) = spawn_derived!(\n            crate::spawn,\n            initial_value,\n            fun,\n            true,\n            true,\n            true,\n            None::<ArcTrigger>\n        );\n        this\n    }\n\n    /// Creates a new async derived computation with an initial value, and does not spawn a task\n    /// initially.\n    ///\n    /// This is mostly used with manual dependency tracking, for primitives built on top of this\n    /// where you do not want to run the run the `Future` unnecessarily.\n    #[doc(hidden)]\n    #[track_caller]\n    pub fn new_with_manual_dependencies<Fut, S>(\n        initial_value: Option<T>,\n        fun: impl Fn() -> Fut + Send + Sync + 'static,\n        source: &S,\n    ) -> Self\n    where\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n        S: Track,\n    {\n        let fun = move || {\n            let fut = fun();\n            let fut =\n                ScopedFuture::new_untracked_with_diagnostics(async move {\n                    SendOption::new(Some(fut.await))\n                });\n            #[cfg(feature = \"sandboxed-arenas\")]\n            let fut = Sandboxed::new(fut);\n            fut\n        };\n        let initial_value = SendOption::new(initial_value);\n        let (this, _) = spawn_derived!(\n            crate::spawn,\n            initial_value,\n            fun,\n            true,\n            false,\n            false,\n            Some(source)\n        );\n        this\n    }\n\n    /// Creates a new async derived computation that will be guaranteed to run on the current\n    /// thread.\n    ///\n    /// This runs eagerly: i.e., calls `fun` once when created and immediately spawns the `Future`\n    /// as a new task.\n    #[track_caller]\n    pub fn new_unsync<Fut>(fun: impl Fn() -> Fut + 'static) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        Self::new_unsync_with_initial(None, fun)\n    }\n\n    /// Creates a new async derived computation with an initial value as a fallback, and begins running the\n    /// `Future` eagerly to get the actual first value.\n    #[track_caller]\n    pub fn new_unsync_with_initial<Fut>(\n        initial_value: Option<T>,\n        fun: impl Fn() -> Fut + 'static,\n    ) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        let fun = move || {\n            let fut = fun();\n            let fut = async move { SendOption::new_local(Some(fut.await)) };\n            #[cfg(feature = \"sandboxed-arenas\")]\n            let fut = Sandboxed::new(fut);\n            fut\n        };\n        let initial_value = SendOption::new_local(initial_value);\n        let (this, _) = spawn_derived!(\n            crate::spawn_local,\n            initial_value,\n            fun,\n            true,\n            true,\n            true,\n            None::<ArcTrigger>\n        );\n        this\n    }\n\n    /// Returns a `Future` that is ready when this resource has next finished loading.\n    pub fn ready(&self) -> AsyncDerivedReadyFuture {\n        AsyncDerivedReadyFuture::new(\n            self.to_any_source(),\n            &self.loading,\n            &self.wakers,\n        )\n    }\n}\n\nimpl<T: 'static> ArcAsyncDerived<T> {\n    #[doc(hidden)]\n    #[track_caller]\n    pub fn new_mock<Fut>(fun: impl Fn() -> Fut + 'static) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        let initial = SendOption::new_local(None::<T>);\n        let fun = move || {\n            let fut = fun();\n            let fut = async move { SendOption::new_local(Some(fut.await)) };\n            #[cfg(feature = \"sandboxed-arenas\")]\n            let fut = Sandboxed::new(fut);\n            fut\n        };\n        let (this, _) = spawn_derived!(\n            crate::spawn_local,\n            initial,\n            fun,\n            false,\n            false,\n            true,\n            None::<ArcTrigger>\n        );\n        this\n    }\n}\n\nimpl<T: 'static> ReadUntracked for ArcAsyncDerived<T> {\n    type Value =\n        ReadGuard<Option<T>, Mapped<AsyncPlain<SendOption<T>>, Option<T>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        if let Some(suspense_context) = use_context::<SuspenseContext>() {\n            // create a handle to register it with suspense\n            let handle = suspense_context.task_id();\n\n            // check if the task is *already* ready\n            let mut ready =\n                Box::pin(SpecialNonReactiveFuture::new(self.ready()));\n            match ready.as_mut().now_or_never() {\n                Some(_) => {\n                    // if it's already ready, drop the handle immediately\n                    // this will immediately notify the suspense context that it's complete\n                    drop(handle);\n                }\n                None => {\n                    // otherwise, spawn a task to wait for it to be ready, then drop the handle,\n                    // which will notify the suspense\n                    crate::spawn(async move {\n                        ready.await;\n                        drop(handle);\n                    });\n                }\n            }\n\n            // register the suspense context with our list of them, to be notified later if this re-runs\n            self.inner\n                .write()\n                .or_poisoned()\n                .suspenses\n                .push(suspense_context);\n        }\n        AsyncPlain::try_new(&self.value).map(|plain| {\n            ReadGuard::new(Mapped::new_with_guard(plain, |v| v.deref()))\n        })\n    }\n}\n\nimpl<T: 'static> Notify for ArcAsyncDerived<T> {\n    fn notify(&self) {\n        Self::notify_subs(&self.wakers, &self.inner, &self.loading, None);\n    }\n}\n\nimpl<T: 'static> Write for ArcAsyncDerived<T> {\n    type Value = Option<T>;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        // increment the version, such that a rerun triggered previously does not overwrite this\n        // new value\n        let mut guard = self.inner.write().or_poisoned();\n        guard.version += 1;\n\n        // tell any suspenses to stop waiting for this\n        drop(mem::take(&mut guard.pending_suspenses));\n\n        Some(MappedMut::new(\n            WriteGuard::new(self.clone(), self.value.blocking_write()),\n            |v| v.deref(),\n            |v| v.deref_mut(),\n        ))\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        // increment the version, such that a rerun triggered previously does not overwrite this\n        // new value\n        let mut guard = self.inner.write().or_poisoned();\n        guard.version += 1;\n\n        // tell any suspenses to stop waiting for this\n        drop(mem::take(&mut guard.pending_suspenses));\n\n        Some(MappedMut::new(\n            self.value.blocking_write(),\n            |v| v.deref(),\n            |v| v.deref_mut(),\n        ))\n    }\n}\n\nimpl<T: 'static> IsDisposed for ArcAsyncDerived<T> {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T: 'static> ToAnySource for ArcAsyncDerived<T> {\n    fn to_any_source(&self) -> AnySource {\n        AnySource(\n            Arc::as_ptr(&self.inner) as usize,\n            Arc::downgrade(&self.inner) as Weak<dyn Source + Send + Sync>,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            self.defined_at,\n        )\n    }\n}\n\nimpl<T: 'static> ToAnySubscriber for ArcAsyncDerived<T> {\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        AnySubscriber(\n            Arc::as_ptr(&self.inner) as usize,\n            Arc::downgrade(&self.inner) as Weak<dyn Subscriber + Send + Sync>,\n        )\n    }\n}\n\nimpl<T> Source for ArcAsyncDerived<T> {\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        self.inner.add_subscriber(subscriber);\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        self.inner.remove_subscriber(subscriber);\n    }\n\n    fn clear_subscribers(&self) {\n        self.inner.clear_subscribers();\n    }\n}\n\nimpl<T> ReactiveNode for ArcAsyncDerived<T> {\n    fn mark_dirty(&self) {\n        self.inner.mark_dirty();\n    }\n\n    fn mark_check(&self) {\n        self.inner.mark_check();\n    }\n\n    fn mark_subscribers_check(&self) {\n        self.inner.mark_subscribers_check();\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        self.inner.update_if_necessary()\n    }\n}\n\nimpl<T> Subscriber for ArcAsyncDerived<T> {\n    fn add_source(&self, source: AnySource) {\n        self.inner.add_source(source);\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        self.inner.clear_sources(subscriber);\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed/async_derived/async_derived.rs",
    "content": "use super::{ArcAsyncDerived, AsyncDerivedReadyFuture, BlockingLock};\nuse crate::{\n    graph::{\n        AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,\n        ToAnySource, ToAnySubscriber,\n    },\n    owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},\n    send_wrapper_ext::SendOption,\n    signal::guards::{AsyncPlain, Mapped, MappedMut, ReadGuard, WriteGuard},\n    traits::{\n        DefinedAt, Dispose, IsDisposed, Notify, ReadUntracked,\n        UntrackableGuard, Write,\n    },\n    unwrap_signal,\n};\nuse core::fmt::Debug;\nuse or_poisoned::OrPoisoned;\nuse std::{\n    future::Future,\n    mem,\n    ops::{Deref, DerefMut},\n    panic::Location,\n};\n\n/// A reactive value that is derived by running an asynchronous computation in response to changes\n/// in its sources.\n///\n/// When one of its dependencies changes, this will re-run its async computation, then notify other\n/// values that depend on it that it has changed.\n///\n/// This is an arena-allocated type, which is `Copy` and is disposed when its reactive\n/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted signal that lives as\n/// as long as a reference to it is alive, see [`ArcAsyncDerived`].\n///\n/// ## Examples\n/// ```rust\n/// # use reactive_graph::computed::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # use reactive_graph::prelude::*;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n///\n/// let signal1 = RwSignal::new(0);\n/// let signal2 = RwSignal::new(0);\n/// let derived = AsyncDerived::new(move || async move {\n///   // reactive values can be tracked anywhere in the `async` block\n///   let value1 = signal1.get();\n///   tokio::time::sleep(std::time::Duration::from_millis(25)).await;\n///   let value2 = signal2.get();\n///\n///   value1 + value2\n/// });\n///\n/// // the value can be accessed synchronously as `Option<T>`\n/// assert_eq!(derived.get(), None);\n/// // we can also .await the value, i.e., convert it into a Future\n/// assert_eq!(derived.await, 0);\n/// assert_eq!(derived.get(), Some(0));\n///\n/// signal1.set(1);\n/// // while the new value is still pending, the signal holds the old value\n/// tokio::time::sleep(std::time::Duration::from_millis(5)).await;\n/// assert_eq!(derived.get(), Some(0));\n///\n/// // setting multiple dependencies will hold until the latest change is ready\n/// signal2.set(1);\n/// assert_eq!(derived.await, 2);\n/// # });\n/// ```\n///\n/// ## Core Trait Implementations\n/// - [`.get()`](crate::traits::Get) clones the current value as an `Option<T>`.\n///   If you call it within an effect, it will cause that effect to subscribe\n///   to the memo, and to re-run whenever the value of the memo changes.\n///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of\n///     without reactively tracking it.\n/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the\n///   value by reference. If you call it within an effect, it will\n///   cause that effect to subscribe to the memo, and to re-run whenever the\n///   value changes.\n///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the\n///     current value without reactively tracking it.\n/// - [`.with()`](crate::traits::With) allows you to reactively access the\n///   value without cloning by applying a callback function.\n///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access\n///     the value by applying a callback function without reactively\n///     tracking it.\n/// - [`IntoFuture`](std::future::Future) allows you to create a [`Future`] that resolves\n///   when this resource is done loading.\npub struct AsyncDerived<T, S = SyncStorage> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    pub(crate) inner: ArenaItem<ArcAsyncDerived<T>, S>,\n}\n\nimpl<T, S> Dispose for AsyncDerived<T, S> {\n    fn dispose(self) {\n        self.inner.dispose()\n    }\n}\n\nimpl<T, S> From<ArcAsyncDerived<T>> for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    fn from(value: ArcAsyncDerived<T>) -> Self {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let defined_at = value.defined_at;\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at,\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T, S> From<AsyncDerived<T, S>> for ArcAsyncDerived<T>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    #[track_caller]\n    fn from(value: AsyncDerived<T, S>) -> Self {\n        value\n            .inner\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(value))\n    }\n}\n\nimpl<T> FromLocal<ArcAsyncDerived<T>> for AsyncDerived<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    fn from_local(value: ArcAsyncDerived<T>) -> Self {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let defined_at = value.defined_at;\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at,\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T> AsyncDerived<T>\nwhere\n    T: 'static,\n{\n    /// Creates a new async derived computation.\n    ///\n    /// This runs eagerly: i.e., calls `fun` once when created and immediately spawns the `Future`\n    /// as a new task.\n    #[track_caller]\n    pub fn new<Fut>(fun: impl Fn() -> Fut + Send + Sync + 'static) -> Self\n    where\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcAsyncDerived::new(fun)),\n        }\n    }\n\n    /// Creates a new async derived computation with an initial value.\n    ///\n    /// If the initial value is `Some(_)`, the task will not be run initially.\n    pub fn new_with_initial<Fut>(\n        initial_value: Option<T>,\n        fun: impl Fn() -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        T: Send + Sync + 'static,\n        Fut: Future<Output = T> + Send + 'static,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(\n                ArcAsyncDerived::new_with_initial(initial_value, fun),\n            ),\n        }\n    }\n}\n\nimpl<T> AsyncDerived<T> {\n    #[doc(hidden)]\n    pub fn new_mock<Fut>(fun: impl Fn() -> Fut + 'static) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcAsyncDerived::new_mock(fun)),\n        }\n    }\n\n    /// Same as [`AsyncDerived::new_unsync`] except it produces AsyncDerived<T> instead of AsyncDerived<T, LocalStorage>.\n    /// The internal value will still be wrapped in a [`send_wrapper::SendWrapper`].\n    pub fn new_unsync_threadsafe_storage<Fut>(\n        fun: impl Fn() -> Fut + 'static,\n    ) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcAsyncDerived::new_unsync(\n                fun,\n            )),\n        }\n    }\n}\n\nimpl<T> AsyncDerived<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    /// Creates a new async derived computation that will be guaranteed to run on the current\n    /// thread.\n    ///\n    /// This runs eagerly: i.e., calls `fun` once when created and immediately spawns the `Future`\n    /// as a new task.\n    pub fn new_unsync<Fut>(fun: impl Fn() -> Fut + 'static) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcAsyncDerived::new_unsync(\n                fun,\n            )),\n        }\n    }\n\n    /// Creates a new async derived computation with an initial value. Async work will be\n    /// guaranteed to run only on the current thread.\n    ///\n    /// If the initial value is `Some(_)`, the task will not be run initially.\n    pub fn new_unsync_with_initial<Fut>(\n        initial_value: Option<T>,\n        fun: impl Fn() -> Fut + 'static,\n    ) -> Self\n    where\n        T: 'static,\n        Fut: Future<Output = T> + 'static,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(\n                ArcAsyncDerived::new_unsync_with_initial(initial_value, fun),\n            ),\n        }\n    }\n}\n\nimpl<T, S> AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    /// Returns a `Future` that is ready when this resource has next finished loading.\n    #[track_caller]\n    pub fn ready(&self) -> AsyncDerivedReadyFuture {\n        let this = self\n            .inner\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(self));\n        this.ready()\n    }\n}\n\nimpl<T, S> Copy for AsyncDerived<T, S> {}\n\nimpl<T, S> Clone for AsyncDerived<T, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> Debug for AsyncDerived<T, S>\nwhere\n    S: Debug,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AsyncDerived\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"store\", &self.inner)\n            .finish()\n    }\n}\n\nimpl<T, S> DefinedAt for AsyncDerived<T, S> {\n    #[inline(always)]\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T, S> ReadUntracked for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    type Value =\n        ReadGuard<Option<T>, Mapped<AsyncPlain<SendOption<T>>, Option<T>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.read_untracked())\n    }\n}\n\nimpl<T, S> Notify for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    fn notify(&self) {\n        self.inner.try_with_value(|inner| inner.notify());\n    }\n}\n\nimpl<T, S> Write for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    type Value = Option<T>;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        let guard = self\n            .inner\n            .try_with_value(|n| n.value.blocking_write_arc())?;\n\n        self.inner.try_with_value(|n| {\n            let mut guard = n.inner.write().or_poisoned();\n            // increment the version, such that a rerun triggered previously does not overwrite this\n            // new value\n            guard.version += 1;\n\n            // tell any suspenses to stop waiting for this\n            drop(mem::take(&mut guard.pending_suspenses));\n        });\n\n        Some(MappedMut::new(\n            WriteGuard::new(*self, guard),\n            |v| v.deref(),\n            |v| v.deref_mut(),\n        ))\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.inner.try_with_value(|n| {\n            let mut guard = n.inner.write().or_poisoned();\n            // increment the version, such that a rerun triggered previously does not overwrite this\n            // new value\n            guard.version += 1;\n\n            // tell any suspenses to stop waiting for this\n            drop(mem::take(&mut guard.pending_suspenses));\n        });\n\n        self.inner\n            .try_with_value(|n| n.value.blocking_write_arc())\n            .map(|inner| {\n                MappedMut::new(inner, |v| v.deref(), |v| v.deref_mut())\n            })\n    }\n}\n\nimpl<T, S> IsDisposed for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<T, S> ToAnySource for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    fn to_any_source(&self) -> AnySource {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.to_any_source())\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T, S> ToAnySubscriber for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.to_any_subscriber())\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T, S> Source for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.add_subscriber(subscriber);\n        }\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.remove_subscriber(subscriber);\n        }\n    }\n\n    fn clear_subscribers(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.clear_subscribers();\n        }\n    }\n}\n\nimpl<T, S> ReactiveNode for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    fn mark_dirty(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.mark_dirty();\n        }\n    }\n\n    fn mark_check(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.mark_check();\n        }\n    }\n\n    fn mark_subscribers_check(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.mark_subscribers_check();\n        }\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.update_if_necessary()\n        } else {\n            false\n        }\n    }\n}\n\nimpl<T, S> Subscriber for AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    fn add_source(&self, source: AnySource) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.add_source(source);\n        }\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.clear_sources(subscriber);\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed/async_derived/future_impls.rs",
    "content": "use super::{inner::ArcAsyncDerivedInner, ArcAsyncDerived, AsyncDerived};\nuse crate::{\n    computed::suspense::SuspenseContext,\n    diagnostics::SpecialNonReactiveZone,\n    graph::{AnySource, ToAnySource},\n    owner::{use_context, Storage},\n    send_wrapper_ext::SendOption,\n    signal::guards::{AsyncPlain, Mapped, ReadGuard},\n    traits::{DefinedAt, Track},\n    unwrap_signal,\n};\nuse futures::pin_mut;\nuse or_poisoned::OrPoisoned;\nuse std::{\n    future::{Future, IntoFuture},\n    pin::Pin,\n    sync::{\n        atomic::{AtomicBool, Ordering},\n        Arc, RwLock,\n    },\n    task::{Context, Poll, Waker},\n};\n\n/// A read guard that holds access to an async derived resource.\n///\n/// Implements [`Deref`](std::ops::Deref) to access the inner value. This should not be held longer\n/// than it is needed, as it prevents updates to the inner value.\npub type AsyncDerivedGuard<T> =\n    ReadGuard<T, Mapped<AsyncPlain<SendOption<T>>, T>>;\n\n/// A [`Future`] that is ready when an [`ArcAsyncDerived`] is finished loading or reloading,\n/// but does not contain its value.\npub struct AsyncDerivedReadyFuture {\n    pub(crate) source: AnySource,\n    pub(crate) loading: Arc<AtomicBool>,\n    pub(crate) wakers: Arc<RwLock<Vec<Waker>>>,\n}\n\nimpl AsyncDerivedReadyFuture {\n    /// Creates a new [`Future`] that will be ready when the given resource is ready.\n    pub fn new(\n        source: AnySource,\n        loading: &Arc<AtomicBool>,\n        wakers: &Arc<RwLock<Vec<Waker>>>,\n    ) -> Self {\n        AsyncDerivedReadyFuture {\n            source,\n            loading: Arc::clone(loading),\n            wakers: Arc::clone(wakers),\n        }\n    }\n}\n\nimpl Future for AsyncDerivedReadyFuture {\n    type Output = ();\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        #[cfg(debug_assertions)]\n        let _guard = SpecialNonReactiveZone::enter();\n        let waker = cx.waker();\n        self.source.track();\n        if self.loading.load(Ordering::Relaxed) {\n            self.wakers.write().or_poisoned().push(waker.clone());\n            Poll::Pending\n        } else {\n            Poll::Ready(())\n        }\n    }\n}\n\nimpl<T> IntoFuture for ArcAsyncDerived<T>\nwhere\n    T: Clone + 'static,\n{\n    type Output = T;\n    type IntoFuture = AsyncDerivedFuture<T>;\n\n    fn into_future(self) -> Self::IntoFuture {\n        AsyncDerivedFuture {\n            source: self.to_any_source(),\n            value: Arc::clone(&self.value),\n            loading: Arc::clone(&self.loading),\n            wakers: Arc::clone(&self.wakers),\n            inner: Arc::clone(&self.inner),\n        }\n    }\n}\n\nimpl<T, S> IntoFuture for AsyncDerived<T, S>\nwhere\n    T: Clone + 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    type Output = T;\n    type IntoFuture = AsyncDerivedFuture<T>;\n\n    #[track_caller]\n    fn into_future(self) -> Self::IntoFuture {\n        let this = self\n            .inner\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(self));\n        this.into_future()\n    }\n}\n\n/// A [`Future`] that is ready when an [`ArcAsyncDerived`] is finished loading or reloading,\n/// and contains its value. `.await`ing this clones the value `T`.\npub struct AsyncDerivedFuture<T> {\n    source: AnySource,\n    value: Arc<async_lock::RwLock<SendOption<T>>>,\n    loading: Arc<AtomicBool>,\n    wakers: Arc<RwLock<Vec<Waker>>>,\n    inner: Arc<RwLock<ArcAsyncDerivedInner>>,\n}\n\nimpl<T> Future for AsyncDerivedFuture<T>\nwhere\n    T: Clone + 'static,\n{\n    type Output = T;\n\n    #[track_caller]\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        #[cfg(debug_assertions)]\n        let _guard = SpecialNonReactiveZone::enter();\n        let waker = cx.waker();\n        self.source.track();\n        let value = self.value.read_arc();\n\n        if let Some(suspense_context) = use_context::<SuspenseContext>() {\n            self.inner\n                .write()\n                .or_poisoned()\n                .suspenses\n                .push(suspense_context);\n        }\n\n        pin_mut!(value);\n        match (self.loading.load(Ordering::Relaxed), value.poll(cx)) {\n            (true, _) => {\n                self.wakers.write().or_poisoned().push(waker.clone());\n                Poll::Pending\n            }\n            (_, Poll::Pending) => Poll::Pending,\n            (_, Poll::Ready(guard)) => {\n                Poll::Ready(guard.as_ref().unwrap().clone())\n            }\n        }\n    }\n}\n\nimpl<T: 'static> ArcAsyncDerived<T> {\n    /// Returns a `Future` that resolves when the computation is finished, and accesses the inner\n    /// value by reference rather than by cloning it.\n    #[track_caller]\n    pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {\n        AsyncDerivedRefFuture {\n            source: self.to_any_source(),\n            value: Arc::clone(&self.value),\n            loading: Arc::clone(&self.loading),\n            wakers: Arc::clone(&self.wakers),\n        }\n    }\n}\n\nimpl<T, S> AsyncDerived<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcAsyncDerived<T>>,\n{\n    /// Returns a `Future` that resolves when the computation is finished, and accesses the inner\n    /// value by reference rather than by cloning it.\n    #[track_caller]\n    pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {\n        let this = self\n            .inner\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(self));\n        this.by_ref()\n    }\n}\n\n/// A [`Future`] that is ready when an [`ArcAsyncDerived`] is finished loading or reloading,\n/// and yields an [`AsyncDerivedGuard`] that dereferences to its value.\npub struct AsyncDerivedRefFuture<T> {\n    source: AnySource,\n    value: Arc<async_lock::RwLock<SendOption<T>>>,\n    loading: Arc<AtomicBool>,\n    wakers: Arc<RwLock<Vec<Waker>>>,\n}\n\nimpl<T> Future for AsyncDerivedRefFuture<T>\nwhere\n    T: 'static,\n{\n    type Output = AsyncDerivedGuard<T>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        #[cfg(debug_assertions)]\n        let _guard = SpecialNonReactiveZone::enter();\n        let waker = cx.waker();\n        self.source.track();\n        let value = self.value.read_arc();\n        pin_mut!(value);\n        match (self.loading.load(Ordering::Relaxed), value.poll(cx)) {\n            (true, _) => {\n                self.wakers.write().or_poisoned().push(waker.clone());\n                Poll::Pending\n            }\n            (_, Poll::Pending) => Poll::Pending,\n            (_, Poll::Ready(guard)) => Poll::Ready(ReadGuard::new(\n                Mapped::new_with_guard(AsyncPlain { guard }, |guard| {\n                    guard.as_ref().unwrap()\n                }),\n            )),\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed/async_derived/inner.rs",
    "content": "use super::suspense::TaskHandle;\nuse crate::{\n    channel::Sender,\n    computed::suspense::SuspenseContext,\n    graph::{\n        AnySource, AnySubscriber, ReactiveNode, Source, SourceSet, Subscriber,\n        SubscriberSet,\n    },\n    owner::Owner,\n};\nuse or_poisoned::OrPoisoned;\nuse std::sync::RwLock;\n\npub(crate) struct ArcAsyncDerivedInner {\n    pub owner: Owner,\n    // holds subscribers so the dependency can be cleared when this needs to rerun\n    pub sources: SourceSet,\n    // tracks reactive subscribers so they can be notified\n    // when the new async value is ready\n    pub subscribers: SubscriberSet,\n    // when a source changes, notifying this will cause the async work to rerun\n    pub notifier: Sender,\n    pub state: AsyncDerivedState,\n    pub version: usize,\n    pub suspenses: Vec<SuspenseContext>,\n    pub pending_suspenses: Vec<TaskHandle>,\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub(crate) enum AsyncDerivedState {\n    Clean,\n    Dirty,\n    Notifying,\n}\n\nimpl ReactiveNode for RwLock<ArcAsyncDerivedInner> {\n    fn mark_dirty(&self) {\n        let mut lock = self.write().or_poisoned();\n        if lock.state != AsyncDerivedState::Notifying {\n            lock.state = AsyncDerivedState::Dirty;\n            lock.notifier.notify();\n        }\n    }\n\n    fn mark_check(&self) {\n        let mut lock = self.write().or_poisoned();\n        if lock.state != AsyncDerivedState::Notifying {\n            lock.notifier.notify();\n        }\n    }\n\n    fn mark_subscribers_check(&self) {\n        let lock = self.read().or_poisoned();\n        for sub in (&lock.subscribers).into_iter() {\n            sub.mark_check();\n        }\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        let mut guard = self.write().or_poisoned();\n        let (is_dirty, sources) = (\n            guard.state == AsyncDerivedState::Dirty,\n            (guard.state != AsyncDerivedState::Notifying)\n                .then(|| guard.sources.clone()),\n        );\n\n        if is_dirty {\n            guard.state = AsyncDerivedState::Clean;\n            return true;\n        }\n        drop(guard);\n\n        for source in sources.into_iter().flatten() {\n            if source.update_if_necessary() {\n                return true;\n            }\n        }\n        false\n    }\n}\n\nimpl Source for RwLock<ArcAsyncDerivedInner> {\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        self.write().or_poisoned().subscribers.subscribe(subscriber);\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        self.write()\n            .or_poisoned()\n            .subscribers\n            .unsubscribe(subscriber);\n    }\n\n    fn clear_subscribers(&self) {\n        self.write().or_poisoned().subscribers.take();\n    }\n}\n\nimpl Subscriber for RwLock<ArcAsyncDerivedInner> {\n    fn add_source(&self, source: AnySource) {\n        self.write().or_poisoned().sources.insert(source);\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        self.write().or_poisoned().sources.clear_sources(subscriber);\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed/async_derived/mod.rs",
    "content": "mod arc_async_derived;\npub use arc_async_derived::*;\n#[allow(clippy::module_inception)] // not a pub mod, who cares?\nmod async_derived;\nmod future_impls;\nmod inner;\nuse crate::{\n    graph::{AnySubscriber, Observer, WithObserver},\n    owner::Owner,\n};\npub use async_derived::*;\npub use future_impls::*;\nuse futures::Future;\nuse pin_project_lite::pin_project;\nuse std::{\n    pin::Pin,\n    task::{Context, Poll},\n};\n\npin_project! {\n    /// A [`Future`] wrapper that sets the [`Owner`] and [`Observer`] before polling the inner\n    /// `Future`.\n    #[derive(Clone)]\n    #[allow(missing_docs)]\n    pub struct ScopedFuture<Fut> {\n        pub owner: Owner,\n        pub observer: Option<AnySubscriber>,\n        #[pin]\n        pub fut: Fut,\n    }\n}\n\nimpl<Fut> ScopedFuture<Fut> {\n    /// Wraps the given `Future` by taking the current [`Owner`] and [`Observer`] and re-setting\n    /// them as the active owner and observer every time the inner `Future` is polled.\n    pub fn new(fut: Fut) -> Self {\n        let owner = Owner::current().unwrap_or_default();\n        let observer = Observer::get();\n        Self {\n            owner,\n            observer,\n            fut,\n        }\n    }\n\n    /// Wraps the given `Future` by taking the current [`Owner`] re-setting it as the\n    /// active owner every time the inner `Future` is polled. Always untracks, i.e., clears\n    /// the active [`Observer`] when polled.\n    pub fn new_untracked(fut: Fut) -> Self {\n        let owner = Owner::current().unwrap_or_default();\n        Self {\n            owner,\n            observer: None,\n            fut,\n        }\n    }\n\n    #[doc(hidden)]\n    #[track_caller]\n    pub fn new_untracked_with_diagnostics(\n        fut: Fut,\n    ) -> ScopedFutureUntrackedWithDiagnostics<Fut> {\n        let owner = Owner::current().unwrap_or_default();\n        ScopedFutureUntrackedWithDiagnostics {\n            owner,\n            observer: None,\n            fut,\n        }\n    }\n}\n\nimpl<Fut: Future> Future for ScopedFuture<Fut> {\n    type Output = Fut::Output;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        this.owner.with(|| {\n            #[cfg(debug_assertions)]\n            let _maybe_guard = if this.observer.is_none() {\n                Some(crate::diagnostics::SpecialNonReactiveZone::enter())\n            } else {\n                None\n            };\n            this.observer.with_observer(|| this.fut.poll(cx))\n        })\n    }\n}\n\npin_project! {\n    /// A [`Future`] wrapper that sets the [`Owner`] and [`Observer`] before polling the inner\n    /// `Future`, output of [`ScopedFuture::new_untracked_with_diagnostics`].\n    ///\n    /// In leptos 0.9 this will be replaced with `ScopedFuture` itself.\n    #[derive(Clone)]\n    pub struct ScopedFutureUntrackedWithDiagnostics<Fut> {\n        owner: Owner,\n        observer: Option<AnySubscriber>,\n        #[pin]\n        fut: Fut,\n    }\n}\n\nimpl<Fut: Future> Future for ScopedFutureUntrackedWithDiagnostics<Fut> {\n    type Output = Fut::Output;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        this.owner\n            .with(|| this.observer.with_observer(|| this.fut.poll(cx)))\n    }\n}\n\n/// Utilities used to track whether asynchronous computeds are currently loading.\npub mod suspense {\n    use crate::{\n        signal::ArcRwSignal,\n        traits::{Update, Write},\n    };\n    use futures::channel::oneshot::Sender;\n    use or_poisoned::OrPoisoned;\n    use slotmap::{DefaultKey, SlotMap};\n    use std::sync::{Arc, Mutex};\n\n    /// Sends a one-time notification that the resource being read from is \"local only,\" i.e.,\n    /// that it will only run on the client, not the server.\n    #[derive(Clone, Debug)]\n    pub struct LocalResourceNotifier(Arc<Mutex<Option<Sender<()>>>>);\n\n    impl LocalResourceNotifier {\n        /// Send the notification. If the inner channel has already been used, this does nothing.\n        pub fn notify(&mut self) {\n            if let Some(tx) = self.0.lock().or_poisoned().take() {\n                tx.send(()).unwrap();\n            }\n        }\n    }\n\n    impl From<Sender<()>> for LocalResourceNotifier {\n        fn from(value: Sender<()>) -> Self {\n            Self(Arc::new(Mutex::new(Some(value))))\n        }\n    }\n\n    /// Tracks the collection of active async tasks.\n    #[derive(Clone, Debug)]\n    pub struct SuspenseContext {\n        /// The set of active tasks.\n        pub tasks: ArcRwSignal<SlotMap<DefaultKey, ()>>,\n    }\n\n    impl SuspenseContext {\n        /// Generates a unique task ID.\n        pub fn task_id(&self) -> TaskHandle {\n            let key = self.tasks.write().insert(());\n            TaskHandle {\n                tasks: self.tasks.clone(),\n                key,\n            }\n        }\n    }\n\n    /// A unique identifier that removes itself from the set of tasks when it is dropped.\n    #[derive(Debug)]\n    pub struct TaskHandle {\n        tasks: ArcRwSignal<SlotMap<DefaultKey, ()>>,\n        key: DefaultKey,\n    }\n\n    impl Drop for TaskHandle {\n        fn drop(&mut self) {\n            self.tasks.update(|tasks| {\n                tasks.remove(self.key);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed/inner.rs",
    "content": "use crate::{\n    graph::{\n        AnySource, AnySubscriber, Observer, ReactiveNode, ReactiveNodeState,\n        Source, SourceSet, Subscriber, SubscriberSet, WithObserver,\n    },\n    owner::{Owner, Storage, StorageAccess},\n};\nuse or_poisoned::OrPoisoned;\nuse std::{\n    fmt::Debug,\n    sync::{Arc, RwLock, RwLockWriteGuard},\n};\n\npub struct MemoInner<T, S>\nwhere\n    S: Storage<T>,\n{\n    /// Must always be acquired *after* the reactivity lock\n    pub(crate) value: Arc<RwLock<Option<S::Wrapped>>>,\n    #[allow(clippy::type_complexity)]\n    pub(crate) fun: Arc<dyn Fn(Option<T>) -> (T, bool) + Send + Sync>,\n    pub(crate) owner: Owner,\n    pub(crate) reactivity: RwLock<MemoInnerReactivity>,\n}\n\npub(crate) struct MemoInnerReactivity {\n    pub(crate) state: ReactiveNodeState,\n    pub(crate) sources: SourceSet,\n    pub(crate) subscribers: SubscriberSet,\n    pub(crate) any_subscriber: AnySubscriber,\n}\n\nimpl<T, S> Debug for MemoInner<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"MemoInner\").finish_non_exhaustive()\n    }\n}\n\nimpl<T: 'static, S> MemoInner<T, S>\nwhere\n    S: Storage<T>,\n{\n    #[allow(clippy::type_complexity)]\n    pub fn new(\n        fun: Arc<dyn Fn(Option<T>) -> (T, bool) + Send + Sync>,\n        any_subscriber: AnySubscriber,\n    ) -> Self {\n        Self {\n            value: Arc::new(RwLock::new(None)),\n            fun,\n            owner: Owner::new(),\n            reactivity: RwLock::new(MemoInnerReactivity {\n                state: ReactiveNodeState::Dirty,\n                sources: Default::default(),\n                subscribers: SubscriberSet::new(),\n                any_subscriber,\n            }),\n        }\n    }\n}\n\nimpl<T: 'static, S> ReactiveNode for MemoInner<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn mark_dirty(&self) {\n        self.reactivity.write().or_poisoned().state = ReactiveNodeState::Dirty;\n        self.mark_subscribers_check();\n    }\n\n    fn mark_check(&self) {\n        /// codegen optimisation:\n        fn inner(reactivity: &RwLock<MemoInnerReactivity>) {\n            {\n                let mut lock = reactivity.write().or_poisoned();\n                if lock.state != ReactiveNodeState::Dirty {\n                    lock.state = ReactiveNodeState::Check;\n                }\n            }\n            for sub in\n                (&reactivity.read().or_poisoned().subscribers).into_iter()\n            {\n                sub.mark_check();\n            }\n        }\n        inner(&self.reactivity);\n    }\n\n    fn mark_subscribers_check(&self) {\n        let lock = self.reactivity.read().or_poisoned();\n        for sub in (&lock.subscribers).into_iter() {\n            sub.mark_check();\n        }\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        /// codegen optimisation:\n        fn needs_update(reactivity: &RwLock<MemoInnerReactivity>) -> bool {\n            let (state, sources) = {\n                let inner = reactivity.read().or_poisoned();\n                (inner.state, inner.sources.clone())\n            };\n            match state {\n                ReactiveNodeState::Clean => false,\n                ReactiveNodeState::Dirty => true,\n                ReactiveNodeState::Check => {\n                    (&sources).into_iter().any(|source| {\n                        source.update_if_necessary()\n                            || reactivity.read().or_poisoned().state\n                                == ReactiveNodeState::Dirty\n                    })\n                }\n            }\n        }\n\n        if needs_update(&self.reactivity) {\n            // No deadlock risk, because we only hold the value lock.\n            let value = self.value.write().or_poisoned().take();\n\n            /// codegen optimisation:\n            fn inner_1(\n                reactivity: &RwLock<MemoInnerReactivity>,\n            ) -> AnySubscriber {\n                let any_subscriber =\n                    reactivity.read().or_poisoned().any_subscriber.clone();\n                any_subscriber.clear_sources(&any_subscriber);\n                any_subscriber\n            }\n            let any_subscriber = inner_1(&self.reactivity);\n\n            let (new_value, changed) = self.owner.with_cleanup(|| {\n                any_subscriber.with_observer(|| {\n                    (self.fun)(value.map(StorageAccess::into_taken))\n                })\n            });\n\n            // Two locks are acquired, so order matters.\n            let reactivity_lock = self.reactivity.write().or_poisoned();\n            {\n                // Safety: Can block endlessly if the user is has a ReadGuard on the value\n                let mut value_lock = self.value.write().or_poisoned();\n                *value_lock = Some(S::wrap(new_value));\n            }\n\n            /// codegen optimisation:\n            fn inner_2(\n                changed: bool,\n                mut reactivity_lock: RwLockWriteGuard<'_, MemoInnerReactivity>,\n            ) {\n                reactivity_lock.state = ReactiveNodeState::Clean;\n\n                if changed {\n                    let subs = reactivity_lock.subscribers.clone();\n                    drop(reactivity_lock);\n                    for sub in subs {\n                        // don't trigger reruns of effects/memos\n                        // basically: if one of the observers has triggered this memo to\n                        // run, it doesn't need to be re-triggered because of this change\n                        if !Observer::is(&sub) {\n                            sub.mark_dirty();\n                        }\n                    }\n                } else {\n                    drop(reactivity_lock);\n                }\n            }\n            inner_2(changed, reactivity_lock);\n\n            changed\n        } else {\n            /// codegen optimisation:\n            fn inner(reactivity: &RwLock<MemoInnerReactivity>) -> bool {\n                let mut lock = reactivity.write().or_poisoned();\n                lock.state = ReactiveNodeState::Clean;\n                false\n            }\n            inner(&self.reactivity)\n        }\n    }\n}\n\nimpl<T: 'static, S> Source for MemoInner<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        let mut lock = self.reactivity.write().or_poisoned();\n        lock.subscribers.subscribe(subscriber);\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        self.reactivity\n            .write()\n            .or_poisoned()\n            .subscribers\n            .unsubscribe(subscriber);\n    }\n\n    fn clear_subscribers(&self) {\n        self.reactivity.write().or_poisoned().subscribers.take();\n    }\n}\n\nimpl<T: 'static, S> Subscriber for MemoInner<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn add_source(&self, source: AnySource) {\n        self.reactivity.write().or_poisoned().sources.insert(source);\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        self.reactivity\n            .write()\n            .or_poisoned()\n            .sources\n            .clear_sources(subscriber);\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed/memo.rs",
    "content": "use super::ArcMemo;\nuse crate::{\n    owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},\n    signal::{\n        guards::{Mapped, Plain, ReadGuard},\n        ArcReadSignal,\n    },\n    traits::{DefinedAt, Dispose, Get, ReadUntracked, Track},\n    unwrap_signal,\n};\nuse std::{fmt::Debug, hash::Hash, panic::Location};\n\n/// A memo is an efficient derived reactive value based on other reactive values.\n///\n/// Unlike a \"derived signal,\" a memo comes with two guarantees:\n/// 1. The memo will only run *once* per change, no matter how many times you\n///    access its value.\n/// 2. The memo will only notify its dependents if the value of the computation changes.\n///\n/// This makes a memo the perfect tool for expensive computations.\n///\n/// Memos have a certain overhead compared to derived signals. In most cases, you should\n/// create a derived signal. But if the derivation calculation is expensive, you should\n/// create a memo.\n///\n/// Memos are lazy: they do not run at all until they are read for the first time, and they will\n/// not re-run the calculation when a source signal changes until they are read again.\n///\n/// This is an arena-allocated type, which is `Copy` and is disposed when its reactive\n/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted signal that lives as\n/// as long as a reference to it is alive, see [`ArcMemo`].\n///\n/// ```\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::computed::Memo;\n/// # use reactive_graph::effect::Effect;\n/// # use reactive_graph::signal::signal;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # tokio::task::LocalSet::new().run_until(async {\n/// # fn really_expensive_computation(value: i32) -> i32 { value };\n/// let (value, set_value) = signal(0);\n///\n/// // 🆗 we could create a derived signal with a simple function\n/// let double_value = move || value.get() * 2;\n/// set_value.set(2);\n/// assert_eq!(double_value(), 4);\n///\n/// // but imagine the computation is really expensive\n/// let expensive = move || really_expensive_computation(value.get()); // lazy: doesn't run until called\n/// Effect::new(move |_| {\n///   // 🆗 run #1: calls `really_expensive_computation` the first time\n///   println!(\"expensive = {}\", expensive());\n/// });\n/// Effect::new(move |_| {\n///   // ❌ run #2: this calls `really_expensive_computation` a second time!\n///   let value = expensive();\n///   // do something else...\n/// });\n///\n/// // instead, we create a memo\n/// // 🆗 run #1: the calculation runs once immediately\n/// let memoized = Memo::new(move |_| really_expensive_computation(value.get()));\n/// Effect::new(move |_| {\n///   // 🆗 reads the current value of the memo\n///   //    can be `memoized()` on nightly\n///   println!(\"memoized = {}\", memoized.get());\n/// });\n/// Effect::new(move |_| {\n///   // ✅ reads the current value **without re-running the calculation**\n///   let value = memoized.get();\n///   // do something else...\n/// });\n/// # });\n/// # });\n/// ```\n///\n/// ## Core Trait Implementations\n/// - [`.get()`](crate::traits::Get) clones the current value of the memo.\n///   If you call it within an effect, it will cause that effect to subscribe\n///   to the memo, and to re-run whenever the value of the memo changes.\n///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of\n///     the memo without reactively tracking it.\n/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the\n///   value of the memo by reference. If you call it within an effect, it will\n///   cause that effect to subscribe to the memo, and to re-run whenever the\n///   value of the memo changes.\n///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the\n///     current value of the memo without reactively tracking it.\n/// - [`.with()`](crate::traits::With) allows you to reactively access the memo’s\n///   value without cloning by applying a callback function.\n///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access\n///     the memo’s value by applying a callback function without reactively\n///     tracking it.\n/// - [`.to_stream()`](crate::traits::ToStream) converts the memo to an `async`\n///   stream of values.\n/// - [`::from_stream()`](crate::traits::FromStream) converts an `async` stream\n///   of values into a memo containing the latest value.\npub struct Memo<T, S = SyncStorage>\nwhere\n    S: Storage<T>,\n{\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    inner: ArenaItem<ArcMemo<T, S>, S>,\n}\n\nimpl<T, S> Dispose for Memo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn dispose(self) {\n        self.inner.dispose()\n    }\n}\n\nimpl<T> From<ArcMemo<T, SyncStorage>> for Memo<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcMemo<T, SyncStorage>) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T> FromLocal<ArcMemo<T, LocalStorage>> for Memo<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    #[track_caller]\n    fn from_local(value: ArcMemo<T, LocalStorage>) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T> Memo<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    #[track_caller]\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"debug\", skip_all)\n    )]\n    /// Creates a new memoized, computed reactive value.\n    ///\n    /// As with an [`Effect`](crate::effect::Effect), the argument to the memo function is the previous value,\n    /// i.e., the current value of the memo, which will be `None` for the initial calculation.\n    /// ```\n    /// # use reactive_graph::prelude::*;\n    /// # use reactive_graph::computed::Memo;\n    /// # use reactive_graph::effect::Effect;\n    /// # use reactive_graph::signal::signal;\n    /// # tokio_test::block_on(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # fn really_expensive_computation(value: i32) -> i32 { value };\n    /// let (value, set_value) = signal(0);\n    ///\n    /// // the memo will reactively update whenever `value` changes\n    /// let memoized =\n    ///     Memo::new(move |_| really_expensive_computation(value.get()));\n    /// # });\n    /// ```\n    pub fn new(fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static) -> Self\n    where\n        T: PartialEq,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcMemo::new(fun)),\n        }\n    }\n\n    #[track_caller]\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    /// Creates a new memo with a custom comparison function. By default, memos simply use\n    /// [`PartialEq`] to compare the previous value to the new value. Passing a custom comparator\n    /// allows you to compare the old and new values using any criteria.\n    ///\n    /// `changed` should be a function that returns `true` if the new value is different from the\n    /// old value.\n    pub fn new_with_compare(\n        fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static,\n        changed: fn(Option<&T>, Option<&T>) -> bool,\n    ) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcMemo::new_with_compare(\n                fun, changed,\n            )),\n        }\n    }\n\n    /// Creates a new memo by passing a function that computes the value.\n    ///\n    /// Unlike [`Memo::new`](), this receives ownership of the previous value. As a result, it\n    /// must return both the new value and a `bool` that is `true` if the value has changed.\n    ///\n    /// This is lazy: the function will not be called until the memo's value is read for the first\n    /// time.\n    #[track_caller]\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    pub fn new_owning(\n        fun: impl Fn(Option<T>) -> (T, bool) + Send + Sync + 'static,\n    ) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcMemo::new_owning(fun)),\n        }\n    }\n}\n\nimpl<T, S> Copy for Memo<T, S> where S: Storage<T> {}\n\nimpl<T, S> Clone for Memo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> Debug for Memo<T, S>\nwhere\n    S: Debug + Storage<T>,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Memo\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"store\", &self.inner)\n            .finish()\n    }\n}\n\nimpl<T, S> PartialEq for Memo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn eq(&self, other: &Self) -> bool {\n        self.inner == other.inner\n    }\n}\n\nimpl<T, S> Eq for Memo<T, S> where S: Storage<T> {}\n\nimpl<T, S> Hash for Memo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.inner.hash(state);\n    }\n}\n\nimpl<T, S> DefinedAt for Memo<T, S>\nwhere\n    S: Storage<T>,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T, S> Track for Memo<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcMemo<T, S>> + Storage<T>,\n    ArcMemo<T, S>: Track,\n{\n    #[track_caller]\n    fn track(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.track();\n        }\n    }\n}\n\nimpl<T, S> ReadUntracked for Memo<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcMemo<T, S>> + Storage<T>,\n{\n    type Value =\n        ReadGuard<T, Mapped<Plain<Option<<S as Storage<T>>::Wrapped>>, T>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.read_untracked())\n    }\n}\n\nimpl<T, S> From<Memo<T, S>> for ArcMemo<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcMemo<T, S>> + Storage<T>,\n{\n    #[track_caller]\n    fn from(value: Memo<T, S>) -> Self {\n        value\n            .inner\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(value))\n    }\n}\n\nimpl<T> From<ArcReadSignal<T>> for Memo<T>\nwhere\n    T: Clone + PartialEq + Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcReadSignal<T>) -> Self {\n        Memo::new(move |_| value.get())\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed/selector.rs",
    "content": "use crate::{\n    effect::RenderEffect,\n    signal::ArcRwSignal,\n    traits::{Track, Update},\n};\nuse or_poisoned::OrPoisoned;\nuse rustc_hash::FxHashMap;\nuse std::{\n    hash::Hash,\n    sync::{Arc, RwLock},\n};\n\n/// A conditional signal that only notifies subscribers when a change\n/// in the source signal’s value changes whether the given function is true.\n///\n/// **You probably don’t need this,** but it can be a very useful optimization\n/// in certain situations (e.g., “set the class `selected` if `selected() == this_row_index`)\n/// because it reduces them from `O(n)` to `O(1)`.\n///\n/// ```\n/// # use reactive_graph::computed::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::effect::Effect;\n/// # use reactive_graph::owner::StoredValue; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # tokio_test::block_on(async move {\n/// # tokio::task::LocalSet::new().run_until(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n/// let a = RwSignal::new(0);\n/// let is_selected = Selector::new(move || a.get());\n/// let total_notifications = StoredValue::new(0);\n/// Effect::new_isomorphic({\n///     let is_selected = is_selected.clone();\n///     move |_| {\n///         if is_selected.selected(&5) {\n///             total_notifications.update_value(|n| *n += 1);\n///         }\n///     }\n/// });\n///\n/// assert_eq!(is_selected.selected(&5), false);\n/// assert_eq!(total_notifications.get_value(), 0);\n/// a.set(5);\n/// # any_spawner::Executor::tick().await;\n///\n/// assert_eq!(is_selected.selected(&5), true);\n/// assert_eq!(total_notifications.get_value(), 1);\n/// a.set(5);\n/// # any_spawner::Executor::tick().await;\n///\n/// assert_eq!(is_selected.selected(&5), true);\n/// assert_eq!(total_notifications.get_value(), 1);\n/// a.set(4);\n///\n/// # any_spawner::Executor::tick().await;\n/// assert_eq!(is_selected.selected(&5), false);\n/// # }).await;\n/// # });\n/// ```\n#[derive(Clone)]\npub struct Selector<T>\nwhere\n    T: PartialEq + Eq + Clone + Hash + 'static,\n{\n    subs: Arc<RwLock<FxHashMap<T, ArcRwSignal<bool>>>>,\n    v: Arc<RwLock<Option<T>>>,\n    #[allow(clippy::type_complexity)]\n    f: Arc<dyn Fn(&T, &T) -> bool + Send + Sync>,\n    // owning the effect keeps it alive, to keep updating the selector\n    #[allow(dead_code)]\n    effect: Arc<RenderEffect<T>>,\n}\n\nimpl<T> Selector<T>\nwhere\n    T: PartialEq + Send + Sync + Eq + Clone + Hash + 'static,\n{\n    /// Creates a new selector that compares values using [`PartialEq`].\n    pub fn new(source: impl Fn() -> T + Send + Sync + Clone + 'static) -> Self {\n        Self::new_with_fn(source, PartialEq::eq)\n    }\n\n    /// Creates a new selector that compares values by returning `true` from a comparator function\n    /// if the values are the same.\n    pub fn new_with_fn(\n        source: impl Fn() -> T + Clone + Send + Sync + 'static,\n        f: impl Fn(&T, &T) -> bool + Send + Sync + Clone + 'static,\n    ) -> Self {\n        let subs: Arc<RwLock<FxHashMap<T, ArcRwSignal<bool>>>> =\n            Default::default();\n        let v: Arc<RwLock<Option<T>>> = Default::default();\n        let f = Arc::new(f) as Arc<dyn Fn(&T, &T) -> bool + Send + Sync>;\n\n        let effect = Arc::new(RenderEffect::new_isomorphic({\n            let subs = Arc::clone(&subs);\n            let f = Arc::clone(&f);\n            let v = Arc::clone(&v);\n            move |prev: Option<T>| {\n                let next_value = source();\n                *v.write().or_poisoned() = Some(next_value.clone());\n                if prev.as_ref() != Some(&next_value) {\n                    for (key, signal) in &*subs.read().or_poisoned() {\n                        if f(key, &next_value)\n                            || (prev.is_some()\n                                && f(key, prev.as_ref().unwrap()))\n                        {\n                            signal.update(|n| *n = true);\n                        }\n                    }\n                }\n                next_value\n            }\n        }));\n\n        Selector { subs, v, f, effect }\n    }\n\n    /// Reactively checks whether the given key is selected.\n    pub fn selected(&self, key: &T) -> bool {\n        let read = {\n            let sub = self.subs.read().or_poisoned().get(key).cloned();\n            sub.unwrap_or_else(|| {\n                self.subs\n                    .write()\n                    .or_poisoned()\n                    .entry(key.clone())\n                    .or_insert_with(|| ArcRwSignal::new(false))\n                    .clone()\n            })\n        };\n        read.track();\n        (self.f)(key, self.v.read().or_poisoned().as_ref().unwrap())\n    }\n\n    /// Removes the listener for the given key.\n    pub fn remove(&self, key: &T) {\n        let mut subs = self.subs.write().or_poisoned();\n        subs.remove(key);\n    }\n\n    /// Clears the listeners for all keys.\n    pub fn clear(&self) {\n        let mut subs = self.subs.write().or_poisoned();\n        subs.clear();\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/computed.rs",
    "content": "//! Computed reactive values that derive from other reactive values.\n\nmod arc_memo;\nmod async_derived;\nmod inner;\nmod memo;\nmod selector;\nuse crate::{\n    prelude::*,\n    signal::RwSignal,\n    wrappers::{\n        read::Signal,\n        write::{IntoSignalSetter, SignalSetter},\n    },\n};\npub use arc_memo::*;\npub use async_derived::*;\npub use memo::*;\npub use selector::*;\n\n/// Derives a reactive slice of an [`RwSignal`].\n///\n/// Slices have the same guarantees as [`Memo`s](crate::computed::Memo):\n/// they only emit their value when it has actually been changed.\n///\n/// Slices need a getter and a setter, and you must make sure that\n/// the setter and getter only touch their respective field and nothing else.\n/// They optimally should not have any side effects.\n///\n/// You can use slices whenever you want to react to only parts\n/// of a bigger signal. The prime example would be state management,\n/// where you want all state variables grouped together, but also need\n/// fine-grained signals for each or some of these variables.\n/// In the example below, setting an auth token will only trigger\n/// the token signal, but none of the other derived signals.\n/// ```\n/// # use reactive_graph::prelude::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # use reactive_graph::effect::Effect;\n/// # use reactive_graph::signal::RwSignal;\n/// # use reactive_graph::computed::*;\n///\n/// // some global state with independent fields\n/// #[derive(Default, Clone, Debug)]\n/// struct GlobalState {\n///     count: u32,\n///     name: String,\n/// }\n///\n/// let state = RwSignal::new(GlobalState::default());\n///\n/// // `create_slice` lets us create a \"lens\" into the data\n/// let (count, set_count) = create_slice(\n///     // we take a slice *from* `state`\n///     state,\n///     // our getter returns a \"slice\" of the data\n///     |state| state.count,\n///     // our setter describes how to mutate that slice, given a new value\n///     |state, n| state.count = n,\n/// );\n///\n/// // this slice is completely independent of the `count` slice\n/// // neither of them will cause the other to rerun\n/// let (name, set_name) = create_slice(\n///     // we take a slice *from* `state`\n///     state,\n///     // our getter returns a \"slice\" of the data\n///     |state| state.name.clone(),\n///     // our setter describes how to mutate that slice, given a new value\n///     |state, n| state.name = n,\n/// );\n///\n/// # if false { // don't run effects in doctest\n/// Effect::new(move |_| {\n///     println!(\"name is {}\", name.get());\n/// });\n/// Effect::new(move |_| {\n///     println!(\"count is {}\", count.get());\n/// });\n/// # }\n///\n/// // setting count only causes count to log, not name\n/// set_count.set(42);\n///\n/// // setting name only causes name to log, not count\n/// set_name.set(\"Bob\".into());\n/// ```\n#[track_caller]\npub fn create_slice<T, O, S>(\n    signal: RwSignal<T>,\n    getter: impl Fn(&T) -> O + Copy + Send + Sync + 'static,\n    setter: impl Fn(&mut T, S) + Copy + Send + Sync + 'static,\n) -> (Signal<O>, SignalSetter<S>)\nwhere\n    T: Send + Sync + 'static,\n    O: PartialEq + Send + Sync + 'static,\n{\n    (\n        create_read_slice(signal, getter),\n        create_write_slice(signal, setter),\n    )\n}\n\n/// Takes a memoized, read-only slice of a signal. This is equivalent to the\n/// read-only half of [`create_slice`].\n#[track_caller]\npub fn create_read_slice<T, O>(\n    signal: RwSignal<T>,\n    getter: impl Fn(&T) -> O + Copy + Send + Sync + 'static,\n) -> Signal<O>\nwhere\n    T: Send + Sync + 'static,\n    O: PartialEq + Send + Sync + 'static,\n{\n    Memo::new(move |_| signal.with(getter)).into()\n}\n\n/// Creates a setter to access one slice of a signal. This is equivalent to the\n/// write-only half of [`create_slice`].\n#[track_caller]\npub fn create_write_slice<T, O>(\n    signal: RwSignal<T>,\n    setter: impl Fn(&mut T, O) + Copy + Send + Sync + 'static,\n) -> SignalSetter<O>\nwhere\n    T: Send + Sync + 'static,\n{\n    let setter = move |value| signal.update(|x| setter(x, value));\n    setter.into_signal_setter()\n}\n\n/// Creates a new memoized, computed reactive value.\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `Memo::new()` instead.\"]\npub fn create_memo<T>(\n    fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static,\n) -> Memo<T>\nwhere\n    T: PartialEq + Send + Sync + 'static,\n{\n    Memo::new(fun)\n}\n\n/// Creates a new memo by passing a function that computes the value.\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `Memo::new_owning()` instead.\"]\npub fn create_owning_memo<T>(\n    fun: impl Fn(Option<T>) -> (T, bool) + Send + Sync + 'static,\n) -> Memo<T>\nwhere\n    T: PartialEq + Send + Sync + 'static,\n{\n    Memo::new_owning(fun)\n}\n\n/// A conditional signal that only notifies subscribers when a change\n/// in the source signal’s value changes whether the given function is true.\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `Selector::new()` instead.\"]\npub fn create_selector<T>(\n    source: impl Fn() -> T + Clone + Send + Sync + 'static,\n) -> Selector<T>\nwhere\n    T: PartialEq + Eq + Send + Sync + Clone + std::hash::Hash + 'static,\n{\n    Selector::new(source)\n}\n\n/// Creates a conditional signal that only notifies subscribers when a change\n/// in the source signal’s value changes whether the given function is true.\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `Selector::new_with_fn()` instead.\"]\npub fn create_selector_with_fn<T>(\n    source: impl Fn() -> T + Clone + Send + Sync + 'static,\n    f: impl Fn(&T, &T) -> bool + Send + Sync + Clone + 'static,\n) -> Selector<T>\nwhere\n    T: PartialEq + Eq + Send + Sync + Clone + std::hash::Hash + 'static,\n{\n    Selector::new_with_fn(source, f)\n}\n"
  },
  {
    "path": "reactive_graph/src/diagnostics.rs",
    "content": "//! By default, attempting to [`Track`](crate::traits::Track) a signal when you are not in a\n//! reactive tracking context will cause a warning when you are in debug mode.\n//!\n//! In some cases, this warning is a false positive. For example, inside an event listener in a\n//! user interface, you never want to read from a signal reactively; the event listener should run\n//! when the event fires, not when a signal read in the event listener changes.\n//!\n//! This module provides utilities to suppress those warnings by entering a\n//! [`SpecialNonReactiveZone`].\n\n/// Marks an execution block that is known not to be reactive, and suppresses warnings.\n#[derive(Debug)]\npub struct SpecialNonReactiveZone;\n\n/// Exits the \"special non-reactive zone\" when dropped.\n#[derive(Debug)]\npub struct SpecialNonReactiveZoneGuard(bool);\n\nuse pin_project_lite::pin_project;\nuse std::{\n    cell::Cell,\n    future::Future,\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nthread_local! {\n    static IS_SPECIAL_ZONE: Cell<bool> = const { Cell::new(false) };\n}\n\nimpl SpecialNonReactiveZone {\n    /// Suppresses warnings about non-reactive accesses until the guard is dropped.\n    pub fn enter() -> SpecialNonReactiveZoneGuard {\n        let prev = IS_SPECIAL_ZONE.replace(true);\n        SpecialNonReactiveZoneGuard(prev)\n    }\n\n    #[cfg(all(debug_assertions, feature = \"effects\"))]\n    #[inline(always)]\n    pub(crate) fn is_inside() -> bool {\n        if cfg!(debug_assertions) {\n            IS_SPECIAL_ZONE.get()\n        } else {\n            false\n        }\n    }\n}\n\nimpl Drop for SpecialNonReactiveZoneGuard {\n    fn drop(&mut self) {\n        IS_SPECIAL_ZONE.set(self.0);\n    }\n}\n\npin_project! {\n    #[doc(hidden)]\n    pub struct SpecialNonReactiveFuture<Fut> {\n        #[pin]\n        inner: Fut\n    }\n}\n\nimpl<Fut> SpecialNonReactiveFuture<Fut> {\n    pub fn new(inner: Fut) -> Self {\n        Self { inner }\n    }\n}\n\nimpl<Fut> Future for SpecialNonReactiveFuture<Fut>\nwhere\n    Fut: Future,\n{\n    type Output = Fut::Output;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        #[cfg(debug_assertions)]\n        let _rw = SpecialNonReactiveZone::enter();\n        let this = self.project();\n        this.inner.poll(cx)\n    }\n}\n\nthread_local! {\n    static SUPPRESS_RESOURCE_LOAD: Cell<bool> = const { Cell::new(false) };\n}\n\n#[doc(hidden)]\npub fn suppress_resource_load(suppress: bool) {\n    SUPPRESS_RESOURCE_LOAD.with(|w| w.set(suppress));\n}\n\n#[doc(hidden)]\npub fn is_suppressing_resource_load() -> bool {\n    SUPPRESS_RESOURCE_LOAD.with(|w| w.get())\n}\n"
  },
  {
    "path": "reactive_graph/src/effect/effect.rs",
    "content": "use crate::{\n    channel::{channel, Receiver},\n    effect::{inner::EffectInner, EffectFunction},\n    graph::{\n        AnySubscriber, ReactiveNode, SourceSet, Subscriber, ToAnySubscriber,\n        WithObserver,\n    },\n    owner::{ArenaItem, LocalStorage, Owner, Storage, SyncStorage},\n    traits::Dispose,\n};\nuse any_spawner::Executor;\nuse futures::StreamExt;\nuse or_poisoned::OrPoisoned;\nuse std::{\n    mem,\n    sync::{atomic::AtomicBool, Arc, RwLock},\n};\n\n/// Effects run a certain chunk of code whenever the signals they depend on change.\n///\n/// Creating an effect runs the given function once after any current synchronous work is done.\n/// This tracks its reactive values read within it, and reruns the function whenever the value\n/// of a dependency changes.\n///\n/// Effects are intended to run *side-effects* of the system, not to synchronize state\n/// *within* the system. In other words: In most cases, you usually should not write to\n/// signals inside effects. (If you need to define a signal that depends on the value of\n/// other signals, use a derived signal or a [`Memo`](crate::computed::Memo)).\n///\n/// You can provide an effect function without parameters or one with one parameter.\n/// If you provide such a parameter, the effect function is called with an argument containing\n/// whatever value it returned the last time it ran. On the initial run, this is `None`.\n///\n/// Effects stop running when their reactive [`Owner`] is disposed.\n///\n///\n/// ## Example\n///\n/// ```\n/// # use reactive_graph::computed::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::effect::Effect;\n/// # use reactive_graph::owner::ArenaItem;\n/// # tokio_test::block_on(async move {\n/// # tokio::task::LocalSet::new().run_until(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let a = RwSignal::new(0);\n/// let b = RwSignal::new(0);\n///\n/// // ✅ use effects to interact between reactive state and the outside world\n/// Effect::new(move || {\n///   // on the next “tick” prints \"Value: 0\" and subscribes to `a`\n///   println!(\"Value: {}\", a.get());\n/// });\n///\n/// # assert_eq!(a.get(), 0);\n/// a.set(1);\n/// # assert_eq!(a.get(), 1);\n/// // ✅ because it's subscribed to `a`, the effect reruns and prints \"Value: 1\"\n///\n/// // ❌ don't use effects to synchronize state within the reactive system\n/// Effect::new(move || {\n///   // this technically works but can cause unnecessary re-renders\n///   // and easily lead to problems like infinite loops\n///   b.set(a.get() + 1);\n/// });\n/// # }).await;\n/// # });\n/// ```\n/// ## Web-Specific Notes\n///\n/// 1. **Scheduling**: Effects run after synchronous work, on the next “tick” of the reactive\n///    system. This makes them suitable for “on mount” actions: they will fire immediately after\n///    DOM rendering.\n/// 2. By default, effects do not run unless the `effects` feature is enabled. If you are using\n///    this with a web framework, this generally means that effects **do not run on the server**.\n///    and you can call browser-specific APIs within the effect function without causing issues.\n///    If you need an effect to run on the server, use [`Effect::new_isomorphic`].\n#[derive(Debug, Clone, Copy)]\npub struct Effect<S> {\n    inner: Option<ArenaItem<StoredEffect, S>>,\n}\n\ntype StoredEffect = Option<Arc<RwLock<EffectInner>>>;\n\nimpl<S> Dispose for Effect<S> {\n    fn dispose(self) {\n        if let Some(inner) = self.inner {\n            inner.dispose()\n        }\n    }\n}\n\nfn effect_base() -> (Receiver, Owner, Arc<RwLock<EffectInner>>) {\n    let (mut observer, rx) = channel();\n\n    // spawn the effect asynchronously\n    // we'll notify once so it runs on the next tick,\n    // to register observed values\n    observer.notify();\n\n    let owner = Owner::new();\n    let inner = Arc::new(RwLock::new(EffectInner {\n        dirty: true,\n        observer,\n        sources: SourceSet::new(),\n    }));\n\n    (rx, owner, inner)\n}\n\n#[cfg(debug_assertions)]\nthread_local! {\n    static EFFECT_SCOPE_ACTIVE: AtomicBool = const { AtomicBool::new(false) };\n}\n\n#[cfg(debug_assertions)]\n/// Returns whether the current thread is currently running an effect.\npub fn in_effect_scope() -> bool {\n    EFFECT_SCOPE_ACTIVE\n        .with(|scope| scope.load(std::sync::atomic::Ordering::Relaxed))\n}\n\n/// Set a static to true whilst running the given function.\n/// [`is_in_effect_scope`] will return true whilst the function is running.\nfn run_in_effect_scope<T>(fun: impl FnOnce() -> T) -> T {\n    #[cfg(debug_assertions)]\n    {\n        // For the theoretical nested case, set back to initial value rather than false:\n        let initial = EFFECT_SCOPE_ACTIVE.with(|scope| {\n            scope.swap(true, std::sync::atomic::Ordering::Relaxed)\n        });\n        let result = fun();\n        EFFECT_SCOPE_ACTIVE.with(|scope| {\n            scope.store(initial, std::sync::atomic::Ordering::Relaxed)\n        });\n        result\n    }\n    #[cfg(not(debug_assertions))]\n    {\n        fun()\n    }\n}\n\nimpl<S> Effect<S>\nwhere\n    S: Storage<StoredEffect>,\n{\n    /// Stops this effect before it is disposed.\n    pub fn stop(self) {\n        if let Some(inner) = self\n            .inner\n            .and_then(|this| this.try_update_value(|inner| inner.take()))\n        {\n            drop(inner);\n        }\n    }\n}\n\nimpl Effect<LocalStorage> {\n    /// Creates a new effect, which runs once on the next “tick”, and then runs again when reactive values\n    /// that are read inside it change.\n    ///\n    /// This spawns a task on the local thread using\n    /// [`spawn_local`](any_spawner::Executor::spawn_local). For an effect that can be spawned on\n    /// any thread, use [`new_sync`](Effect::new_sync).\n    pub fn new<T, M>(mut fun: impl EffectFunction<T, M> + 'static) -> Self\n    where\n        T: 'static,\n    {\n        let inner = cfg!(feature = \"effects\").then(|| {\n            let (mut rx, owner, inner) = effect_base();\n            let value = Arc::new(RwLock::new(None::<T>));\n            let mut first_run = true;\n\n            Executor::spawn_local({\n                let value = Arc::clone(&value);\n                let subscriber = inner.to_any_subscriber();\n\n                async move {\n                    while rx.next().await.is_some() {\n                        if !owner.paused()\n                            && (subscriber.with_observer(|| {\n                                subscriber.update_if_necessary()\n                            }) || first_run)\n                        {\n                            first_run = false;\n                            subscriber.clear_sources(&subscriber);\n\n                            let old_value =\n                                mem::take(&mut *value.write().or_poisoned());\n                            let new_value = owner.with_cleanup(|| {\n                                subscriber.with_observer(|| {\n                                    run_in_effect_scope(|| fun.run(old_value))\n                                })\n                            });\n                            *value.write().or_poisoned() = Some(new_value);\n                        }\n                    }\n                }\n            });\n\n            ArenaItem::new_with_storage(Some(inner))\n        });\n\n        Self { inner }\n    }\n\n    /// A version of [`Effect::new`] that only listens to any dependency\n    /// that is accessed inside `dependency_fn`.\n    ///\n    /// The return value of `dependency_fn` is passed into `handler` as an argument together with the previous value.\n    /// Additionally, the last return value of `handler` is provided as a third argument, as is done in [`Effect::new`].\n    ///\n    /// ## Usage\n    ///\n    /// ```\n    /// # use reactive_graph::effect::Effect;\n    /// # use reactive_graph::traits::*;\n    /// # use reactive_graph::signal::signal;\n    /// # tokio_test::block_on(async move {\n    /// # tokio::task::LocalSet::new().run_until(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// #\n    /// let (num, set_num) = signal(0);\n    ///\n    /// let effect = Effect::watch(\n    ///     move || num.get(),\n    ///     move |num, prev_num, _| {\n    ///         // log::debug!(\"Number: {}; Prev: {:?}\", num, prev_num);\n    ///     },\n    ///     false,\n    /// );\n    /// # assert_eq!(num.get(), 0);\n    ///\n    /// set_num.set(1); // > \"Number: 1; Prev: Some(0)\"\n    /// # assert_eq!(num.get(), 1);\n    ///\n    /// effect.stop(); // stop watching\n    ///\n    /// set_num.set(2); // (nothing happens)\n    /// # assert_eq!(num.get(), 2);\n    /// # }).await;\n    /// # });\n    /// ```\n    ///\n    /// The callback itself doesn't track any signal that is accessed within it.\n    ///\n    /// ```\n    /// # use reactive_graph::effect::Effect;\n    /// # use reactive_graph::traits::*;\n    /// # use reactive_graph::signal::signal;\n    /// # tokio_test::block_on(async move {\n    /// # tokio::task::LocalSet::new().run_until(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// #\n    /// let (num, set_num) = signal(0);\n    /// let (cb_num, set_cb_num) = signal(0);\n    ///\n    /// Effect::watch(\n    ///     move || num.get(),\n    ///     move |num, _, _| {\n    ///         // log::debug!(\"Number: {}; Cb: {}\", num, cb_num.get());\n    ///     },\n    ///     false,\n    /// );\n    ///\n    /// # assert_eq!(num.get(), 0);\n    /// set_num.set(1); // > \"Number: 1; Cb: 0\"\n    /// # assert_eq!(num.get(), 1);\n    ///\n    /// # assert_eq!(cb_num.get(), 0);\n    /// set_cb_num.set(1); // (nothing happens)\n    /// # assert_eq!(cb_num.get(), 1);\n    ///\n    /// set_num.set(2); // > \"Number: 2; Cb: 1\"\n    /// # assert_eq!(num.get(), 2);\n    /// # }).await;\n    /// # });\n    /// ```\n    ///\n    /// ## Immediate\n    ///\n    /// If the final parameter `immediate` is true, the `handler` will run immediately.\n    /// If it's `false`, the `handler` will run only after\n    /// the first change is detected of any signal that is accessed in `dependency_fn`.\n    ///\n    /// ```\n    /// # use reactive_graph::effect::Effect;\n    /// # use reactive_graph::traits::*;\n    /// # use reactive_graph::signal::signal;\n    /// # tokio_test::block_on(async move {\n    /// # tokio::task::LocalSet::new().run_until(async move {\n    /// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// #\n    /// let (num, set_num) = signal(0);\n    ///\n    /// Effect::watch(\n    ///     move || num.get(),\n    ///     move |num, prev_num, _| {\n    ///         // log::debug!(\"Number: {}; Prev: {:?}\", num, prev_num);\n    ///     },\n    ///     true,\n    /// ); // > \"Number: 0; Prev: None\"\n    ///\n    /// # assert_eq!(num.get(), 0);\n    /// set_num.set(1); // > \"Number: 1; Prev: Some(0)\"\n    /// # assert_eq!(num.get(), 1);\n    /// # }).await;\n    /// # });\n    /// ```\n    pub fn watch<D, T>(\n        mut dependency_fn: impl FnMut() -> D + 'static,\n        mut handler: impl FnMut(&D, Option<&D>, Option<T>) -> T + 'static,\n        immediate: bool,\n    ) -> Self\n    where\n        D: 'static,\n        T: 'static,\n    {\n        let inner = cfg!(feature = \"effects\").then(|| {\n            let (mut rx, owner, inner) = effect_base();\n            let mut first_run = true;\n            let dep_value = Arc::new(RwLock::new(None::<D>));\n            let watch_value = Arc::new(RwLock::new(None::<T>));\n\n            Executor::spawn_local({\n                let dep_value = Arc::clone(&dep_value);\n                let watch_value = Arc::clone(&watch_value);\n                let subscriber = inner.to_any_subscriber();\n\n                async move {\n                    while rx.next().await.is_some() {\n                        if !owner.paused()\n                            && (subscriber.with_observer(|| {\n                                subscriber.update_if_necessary()\n                            }) || first_run)\n                        {\n                            subscriber.clear_sources(&subscriber);\n\n                            let old_dep_value = mem::take(\n                                &mut *dep_value.write().or_poisoned(),\n                            );\n                            let new_dep_value = owner.with_cleanup(|| {\n                                subscriber.with_observer(&mut dependency_fn)\n                            });\n\n                            let old_watch_value = mem::take(\n                                &mut *watch_value.write().or_poisoned(),\n                            );\n\n                            if immediate || !first_run {\n                                let new_watch_value = handler(\n                                    &new_dep_value,\n                                    old_dep_value.as_ref(),\n                                    old_watch_value,\n                                );\n\n                                *watch_value.write().or_poisoned() =\n                                    Some(new_watch_value);\n                            }\n\n                            *dep_value.write().or_poisoned() =\n                                Some(new_dep_value);\n\n                            first_run = false;\n                        }\n                    }\n                }\n            });\n\n            ArenaItem::new_with_storage(Some(inner))\n        });\n\n        Self { inner }\n    }\n}\n\nimpl Effect<SyncStorage> {\n    /// Creates a new effect, which runs once on the next “tick”, and then runs again when reactive values\n    /// that are read inside it change.\n    ///\n    /// This spawns a task that can be run on any thread. For an effect that will be spawned on\n    /// the current thread, use [`new`](Effect::new).\n    pub fn new_sync<T, M>(\n        fun: impl EffectFunction<T, M> + Send + Sync + 'static,\n    ) -> Self\n    where\n        T: Send + Sync + 'static,\n    {\n        if !cfg!(feature = \"effects\") {\n            return Self { inner: None };\n        }\n\n        Self::new_isomorphic(fun)\n    }\n\n    /// Creates a new effect, which runs once on the next “tick”, and then runs again when reactive values\n    /// that are read inside it change.\n    ///\n    /// This will run whether the `effects` feature is enabled or not.\n    pub fn new_isomorphic<T, M>(\n        mut fun: impl EffectFunction<T, M> + Send + Sync + 'static,\n    ) -> Self\n    where\n        T: Send + Sync + 'static,\n    {\n        let (mut rx, owner, inner) = effect_base();\n        let mut first_run = true;\n        let value = Arc::new(RwLock::new(None::<T>));\n\n        let task = {\n            let value = Arc::clone(&value);\n            let subscriber = inner.to_any_subscriber();\n\n            async move {\n                while rx.next().await.is_some() {\n                    if !owner.paused()\n                        && (subscriber\n                            .with_observer(|| subscriber.update_if_necessary())\n                            || first_run)\n                    {\n                        first_run = false;\n                        subscriber.clear_sources(&subscriber);\n\n                        let old_value =\n                            mem::take(&mut *value.write().or_poisoned());\n                        let new_value = owner.with_cleanup(|| {\n                            subscriber.with_observer(|| {\n                                run_in_effect_scope(|| fun.run(old_value))\n                            })\n                        });\n                        *value.write().or_poisoned() = Some(new_value);\n                    }\n                }\n            }\n        };\n\n        crate::spawn(task);\n\n        Self {\n            inner: Some(ArenaItem::new_with_storage(Some(inner))),\n        }\n    }\n\n    /// This is to [`Effect::watch`] what [`Effect::new_sync`] is to [`Effect::new`].\n    pub fn watch_sync<D, T>(\n        mut dependency_fn: impl FnMut() -> D + Send + Sync + 'static,\n        mut handler: impl FnMut(&D, Option<&D>, Option<T>) -> T\n            + Send\n            + Sync\n            + 'static,\n        immediate: bool,\n    ) -> Self\n    where\n        D: Send + Sync + 'static,\n        T: Send + Sync + 'static,\n    {\n        let (mut rx, owner, inner) = effect_base();\n        let mut first_run = true;\n        let dep_value = Arc::new(RwLock::new(None::<D>));\n        let watch_value = Arc::new(RwLock::new(None::<T>));\n\n        let inner = cfg!(feature = \"effects\").then(|| {\n            crate::spawn({\n                let dep_value = Arc::clone(&dep_value);\n                let watch_value = Arc::clone(&watch_value);\n                let subscriber = inner.to_any_subscriber();\n\n                async move {\n                    while rx.next().await.is_some() {\n                        if !owner.paused()\n                            && (subscriber.with_observer(|| {\n                                subscriber.update_if_necessary()\n                            }) || first_run)\n                        {\n                            subscriber.clear_sources(&subscriber);\n\n                            let old_dep_value = mem::take(\n                                &mut *dep_value.write().or_poisoned(),\n                            );\n                            let new_dep_value = owner.with_cleanup(|| {\n                                subscriber.with_observer(&mut dependency_fn)\n                            });\n\n                            let old_watch_value = mem::take(\n                                &mut *watch_value.write().or_poisoned(),\n                            );\n\n                            if immediate || !first_run {\n                                let new_watch_value = handler(\n                                    &new_dep_value,\n                                    old_dep_value.as_ref(),\n                                    old_watch_value,\n                                );\n\n                                *watch_value.write().or_poisoned() =\n                                    Some(new_watch_value);\n                            }\n\n                            *dep_value.write().or_poisoned() =\n                                Some(new_dep_value);\n\n                            first_run = false;\n                        }\n                    }\n                }\n            });\n\n            ArenaItem::new_with_storage(Some(inner))\n        });\n\n        Self { inner }\n    }\n}\n\nimpl<S> ToAnySubscriber for Effect<S>\nwhere\n    S: Storage<StoredEffect>,\n{\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        self.inner\n            .and_then(|inner| {\n                inner\n                    .try_with_value(|inner| {\n                        inner.as_ref().map(|inner| inner.to_any_subscriber())\n                    })\n                    .flatten()\n            })\n            .expect(\"tried to set effect that has been stopped\")\n    }\n}\n\n/// Creates an [`Effect`].\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `Effect::new()` instead.\"]\npub fn create_effect<T>(\n    fun: impl FnMut(Option<T>) -> T + 'static,\n) -> Effect<LocalStorage>\nwhere\n    T: 'static,\n{\n    Effect::new(fun)\n}\n\n/// Creates an [`Effect`], equivalent to [Effect::watch].\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `Effect::watch()` instead.\"]\npub fn watch<W, T>(\n    deps: impl Fn() -> W + 'static,\n    callback: impl Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,\n    immediate: bool,\n) -> impl Fn() + Clone\nwhere\n    W: Clone + 'static,\n    T: 'static,\n{\n    let watch = Effect::watch(deps, callback, immediate);\n\n    move || watch.stop()\n}\n"
  },
  {
    "path": "reactive_graph/src/effect/effect_function.rs",
    "content": "/// Trait to enable effect functions that have zero or one parameter\npub trait EffectFunction<T, M> {\n    /// Call this to execute the function. In case the actual function has no parameters\n    /// the parameter `p` will simply be ignored.\n    fn run(&mut self, p: Option<T>) -> T;\n}\n\n/// Marker for single parameter functions\npub struct SingleParam;\n/// Marker for no parameter functions\npub struct NoParam;\n\nimpl<Func, T> EffectFunction<T, SingleParam> for Func\nwhere\n    Func: FnMut(Option<T>) -> T,\n{\n    #[inline(always)]\n    fn run(&mut self, p: Option<T>) -> T {\n        (self)(p)\n    }\n}\n\nimpl<Func> EffectFunction<(), NoParam> for Func\nwhere\n    Func: FnMut(),\n{\n    #[inline(always)]\n    fn run(&mut self, _: Option<()>) {\n        self()\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/effect/immediate.rs",
    "content": "use crate::{\n    graph::{AnySubscriber, ReactiveNode, ToAnySubscriber},\n    owner::on_cleanup,\n    traits::{DefinedAt, Dispose},\n};\nuse or_poisoned::OrPoisoned;\nuse std::{\n    panic::Location,\n    sync::{Arc, Mutex, RwLock},\n};\n\n/// Effects run a certain chunk of code whenever the signals they depend on change.\n///\n/// The effect runs on creation and again as soon as any tracked signal changes.\n///\n/// NOTE: you probably want use [`Effect`](super::Effect) instead.\n/// This is for the few cases where it's important to execute effects immediately and in order.\n///\n/// [ImmediateEffect]s stop running when dropped.\n///\n/// NOTE: since effects are executed immediately, they might recurse.\n/// Under recursion or parallelism only the last run to start is tracked.\n///\n/// ## Example\n///\n/// ```\n/// # use reactive_graph::computed::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::effect::ImmediateEffect;\n/// # use reactive_graph::owner::ArenaItem;\n/// # let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let a = RwSignal::new(0);\n/// let b = RwSignal::new(0);\n///\n/// // ✅ use effects to interact between reactive state and the outside world\n/// let _drop_guard = ImmediateEffect::new(move || {\n///   // on the next “tick” prints \"Value: 0\" and subscribes to `a`\n///   println!(\"Value: {}\", a.get());\n/// });\n///\n/// // The effect runs immediately and subscribes to `a`, in the process it prints \"Value: 0\"\n/// # assert_eq!(a.get(), 0);\n/// a.set(1);\n/// # assert_eq!(a.get(), 1);\n/// // ✅ because it's subscribed to `a`, the effect reruns and prints \"Value: 1\"\n/// ```\n/// ## Notes\n///\n/// 1. **Scheduling**: Effects run immediately, as soon as any tracked signal changes.\n/// 2. By default, effects do not run unless the `effects` feature is enabled. If you are using\n///    this with a web framework, this generally means that effects **do not run on the server**.\n///    and you can call browser-specific APIs within the effect function without causing issues.\n///    If you need an effect to run on the server, use [`ImmediateEffect::new_isomorphic`].\n#[derive(Debug, Clone)]\npub struct ImmediateEffect {\n    inner: StoredEffect,\n}\n\ntype StoredEffect = Option<Arc<RwLock<inner::EffectInner>>>;\n\nimpl Dispose for ImmediateEffect {\n    fn dispose(self) {}\n}\n\nimpl ImmediateEffect {\n    /// Creates a new effect which runs immediately, then again as soon as any tracked signal changes.\n    /// (Unless [batch] is used.)\n    ///\n    /// NOTE: this requires a `Fn` function because it might recurse.\n    /// Use [Self::new_mut] to pass a `FnMut` function, it'll panic on recursion.\n    #[track_caller]\n    #[must_use]\n    pub fn new(fun: impl Fn() + Send + Sync + 'static) -> Self {\n        if !cfg!(feature = \"effects\") {\n            return Self { inner: None };\n        }\n\n        let inner = inner::EffectInner::new(fun);\n\n        inner.update_if_necessary();\n\n        Self { inner: Some(inner) }\n    }\n    /// Creates a new effect which runs immediately, then again as soon as any tracked signal changes.\n    /// (Unless [batch] is used.)\n    ///\n    /// # Panics\n    /// Panics on recursion or if triggered in parallel. Also see [Self::new]\n    #[track_caller]\n    #[must_use]\n    pub fn new_mut(fun: impl FnMut() + Send + Sync + 'static) -> Self {\n        const MSG: &str = \"The effect recursed or its function panicked.\";\n        let fun = Mutex::new(fun);\n        Self::new(move || fun.try_lock().expect(MSG)())\n    }\n    /// Creates a new effect which runs immediately, then again as soon as any tracked signal changes.\n    /// (Unless [batch] is used.)\n    ///\n    /// NOTE: this requires a `Fn` function because it might recurse.\n    /// Use [Self::new_mut_scoped] to pass a `FnMut` function, it'll panic on recursion.\n    /// NOTE: this effect is automatically cleaned up when the current owner is cleared or disposed.\n    #[track_caller]\n    pub fn new_scoped(fun: impl Fn() + Send + Sync + 'static) {\n        let effect = Self::new(fun);\n\n        on_cleanup(move || effect.dispose());\n    }\n    /// Creates a new effect which runs immediately, then again as soon as any tracked signal changes.\n    /// (Unless [batch] is used.)\n    ///\n    /// NOTE: this effect is automatically cleaned up when the current owner is cleared or disposed.\n    ///\n    /// # Panics\n    /// Panics on recursion or if triggered in parallel. Also see [Self::new_scoped]\n    #[track_caller]\n    pub fn new_mut_scoped(fun: impl FnMut() + Send + Sync + 'static) {\n        let effect = Self::new_mut(fun);\n\n        on_cleanup(move || effect.dispose());\n    }\n\n    /// Creates a new effect which runs immediately, then again as soon as any tracked signal changes.\n    ///\n    /// This will run whether the `effects` feature is enabled or not.\n    #[track_caller]\n    #[must_use]\n    pub fn new_isomorphic(fun: impl Fn() + Send + Sync + 'static) -> Self {\n        let inner = inner::EffectInner::new(fun);\n\n        inner.update_if_necessary();\n\n        Self { inner: Some(inner) }\n    }\n}\n\nimpl ToAnySubscriber for ImmediateEffect {\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        const MSG: &str = \"tried to set effect that has been stopped\";\n        self.inner.as_ref().expect(MSG).to_any_subscriber()\n    }\n}\n\nimpl DefinedAt for ImmediateEffect {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        self.inner.as_ref()?.read().or_poisoned().defined_at()\n    }\n}\n\n/// Defers any [ImmediateEffect]s from running until the end of the function.\n///\n/// NOTE: this affects only [ImmediateEffect]s, not other effects.\n///\n/// NOTE: this is rarely needed, but it is useful for example when multiple signals\n/// need to be updated atomically (for example a double-bound signal tree).\npub fn batch<T>(f: impl FnOnce() -> T) -> T {\n    struct ExecuteOnDrop;\n    impl Drop for ExecuteOnDrop {\n        fn drop(&mut self) {\n            let effects = {\n                let mut batch = inner::BATCH.write().or_poisoned();\n                batch.take().unwrap().into_inner().expect(\"lock poisoned\")\n            };\n            // TODO: Should we skip the effects if it's panicking?\n            for effect in effects {\n                effect.update_if_necessary();\n            }\n        }\n    }\n    let mut execute_on_drop = None;\n    {\n        let mut batch = inner::BATCH.write().or_poisoned();\n        if batch.is_none() {\n            execute_on_drop = Some(ExecuteOnDrop);\n        } else {\n            // Nested batching has no effect.\n        }\n        *batch = Some(batch.take().unwrap_or_default());\n    }\n    let ret = f();\n    drop(execute_on_drop);\n    ret\n}\n\nmod inner {\n    use crate::{\n        graph::{\n            AnySource, AnySubscriber, ReactiveNode, ReactiveNodeState,\n            SourceSet, Subscriber, ToAnySubscriber, WithObserver,\n        },\n        log_warning,\n        owner::Owner,\n        traits::DefinedAt,\n    };\n    use indexmap::IndexSet;\n    use or_poisoned::OrPoisoned;\n    use std::{\n        panic::Location,\n        sync::{Arc, RwLock, Weak},\n        thread::{self, ThreadId},\n    };\n\n    /// Only the [super::batch] function ever writes to the outer RwLock.\n    /// While the effects will write to the inner one.\n    pub(super) static BATCH: RwLock<Option<RwLock<IndexSet<AnySubscriber>>>> =\n        RwLock::new(None);\n\n    /// Handles subscription logic for effects.\n    ///\n    /// To handle parallelism and recursion we assign ordered (1..) ids to each run.\n    /// We only keep the sources tracked by the run with the highest id (the last one).\n    ///\n    /// We do this by:\n    /// - Clearing the sources before every run, so the last one clears anything before it.\n    /// - We stop tracking sources after the last run has completed.\n    ///   (A parent run will start before and end after a recursive child run.)\n    /// - To handle parallelism with the last run, we only allow sources to be added by its thread.\n    pub(super) struct EffectInner {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        defined_at: &'static Location<'static>,\n        owner: Owner,\n        state: ReactiveNodeState,\n        /// The number of effect runs in this 'batch'.\n        /// Cleared when no runs are *ongoing* anymore.\n        /// Used to assign ordered ids to each run, and to know when we can clear these values.\n        run_count_start: usize,\n        /// The number of effect runs that have completed in the current 'batch'.\n        /// Cleared when no runs are *ongoing* anymore.\n        /// Used to know when we can clear these values.\n        run_done_count: usize,\n        /// Given ordered ids (1..), the run with the highest id that has completed in this 'batch'.\n        /// Cleared when no runs are *ongoing* anymore.\n        /// Used to know whether the current run is the latest one.\n        run_done_max: usize,\n        /// The [ThreadId] of the run with the highest id.\n        /// Used to prevent over-subscribing during parallel execution with the last run.\n        ///\n        /// ```text\n        /// Thread 1:\n        /// -------------------------\n        ///   ---   ---    =======\n        ///\n        /// Thread 2:\n        /// -------------------------\n        ///             -----------\n        /// ```\n        ///\n        /// In the parallel example above, we can see why we need this.\n        /// The last run is marked using `=`, but another run in the other thread might\n        /// also be gathering sources. So we only allow the run from the correct [ThreadId] to push sources.\n        last_run_thread_id: ThreadId,\n        fun: Arc<dyn Fn() + Send + Sync>,\n        sources: SourceSet,\n        any_subscriber: AnySubscriber,\n    }\n\n    impl EffectInner {\n        #[track_caller]\n        pub fn new(\n            fun: impl Fn() + Send + Sync + 'static,\n        ) -> Arc<RwLock<EffectInner>> {\n            let owner = Owner::new();\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            let defined_at = Location::caller();\n\n            Arc::new_cyclic(|weak| {\n                let any_subscriber = AnySubscriber(\n                    weak.as_ptr() as usize,\n                    Weak::clone(weak) as Weak<dyn Subscriber + Send + Sync>,\n                );\n\n                RwLock::new(EffectInner {\n                    #[cfg(any(debug_assertions, leptos_debuginfo))]\n                    defined_at,\n                    owner,\n                    state: ReactiveNodeState::Dirty,\n                    run_count_start: 0,\n                    run_done_count: 0,\n                    run_done_max: 0,\n                    last_run_thread_id: thread::current().id(),\n                    fun: Arc::new(fun),\n                    sources: SourceSet::new(),\n                    any_subscriber,\n                })\n            })\n        }\n    }\n\n    impl ToAnySubscriber for Arc<RwLock<EffectInner>> {\n        fn to_any_subscriber(&self) -> AnySubscriber {\n            AnySubscriber(\n                Arc::as_ptr(self) as usize,\n                Arc::downgrade(self) as Weak<dyn Subscriber + Send + Sync>,\n            )\n        }\n    }\n\n    impl ReactiveNode for RwLock<EffectInner> {\n        fn mark_subscribers_check(&self) {}\n\n        fn update_if_necessary(&self) -> bool {\n            let state = {\n                let guard = self.read().or_poisoned();\n\n                if guard.owner.paused() {\n                    return false;\n                }\n\n                guard.state\n            };\n\n            let needs_update = match state {\n                ReactiveNodeState::Clean => false,\n                ReactiveNodeState::Check => {\n                    let sources = self.read().or_poisoned().sources.clone();\n                    sources\n                        .into_iter()\n                        .any(|source| source.update_if_necessary())\n                }\n                ReactiveNodeState::Dirty => true,\n            };\n\n            {\n                if let Some(batch) = &*BATCH.read().or_poisoned() {\n                    let mut batch = batch.write().or_poisoned();\n                    let subscriber =\n                        self.read().or_poisoned().any_subscriber.clone();\n\n                    batch.insert(subscriber);\n                    return needs_update;\n                }\n            }\n\n            if needs_update {\n                let mut guard = self.write().or_poisoned();\n\n                let owner = guard.owner.clone();\n                let any_subscriber = guard.any_subscriber.clone();\n                let fun = guard.fun.clone();\n\n                // New run has started.\n                guard.run_count_start += 1;\n                // We get a value for this run, the highest value will be what we keep the sources from.\n                let recursion_count = guard.run_count_start;\n                // We clear the sources before running the effect.\n                // Note that this is tied to the ordering of the initial write lock acquisition\n                // to ensure the last run is also the last to clear them.\n                guard.sources.clear_sources(&any_subscriber);\n                // Only this thread will be able to subscribe.\n                guard.last_run_thread_id = thread::current().id();\n\n                if recursion_count > 2 {\n                    warn_excessive_recursion(&guard);\n                }\n\n                drop(guard);\n\n                // We execute the effect.\n                // Note that *this could happen in parallel across threads*.\n                owner.with_cleanup(|| any_subscriber.with_observer(|| fun()));\n\n                let mut guard = self.write().or_poisoned();\n\n                // This run has completed.\n                guard.run_done_count += 1;\n\n                // We update the done count.\n                // Sources will only be added if recursion_done_max < recursion_count_start.\n                // (Meaning the last run is not done yet.)\n                guard.run_done_max =\n                    Ord::max(recursion_count, guard.run_done_max);\n\n                // The same amount of runs has started and completed,\n                // so we can clear everything up for next time.\n                if guard.run_count_start == guard.run_done_count {\n                    guard.run_count_start = 0;\n                    guard.run_done_count = 0;\n                    guard.run_done_max = 0;\n                    // Can be left unchanged, it'll be set again next time.\n                    // guard.last_run_thread_id = thread::current().id();\n                }\n\n                guard.state = ReactiveNodeState::Clean;\n            }\n\n            needs_update\n        }\n\n        fn mark_check(&self) {\n            self.write().or_poisoned().state = ReactiveNodeState::Check;\n            self.update_if_necessary();\n        }\n\n        fn mark_dirty(&self) {\n            self.write().or_poisoned().state = ReactiveNodeState::Dirty;\n            self.update_if_necessary();\n        }\n    }\n\n    impl Subscriber for RwLock<EffectInner> {\n        fn add_source(&self, source: AnySource) {\n            let mut guard = self.write().or_poisoned();\n            if guard.run_done_max < guard.run_count_start\n                && guard.last_run_thread_id == thread::current().id()\n            {\n                guard.sources.insert(source);\n            }\n        }\n\n        fn clear_sources(&self, subscriber: &AnySubscriber) {\n            self.write().or_poisoned().sources.clear_sources(subscriber);\n        }\n    }\n\n    impl DefinedAt for EffectInner {\n        fn defined_at(&self) -> Option<&'static Location<'static>> {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            {\n                Some(self.defined_at)\n            }\n            #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n            {\n                None\n            }\n        }\n    }\n\n    impl std::fmt::Debug for EffectInner {\n        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n            f.debug_struct(\"EffectInner\")\n                .field(\"owner\", &self.owner)\n                .field(\"state\", &self.state)\n                .field(\"sources\", &self.sources)\n                .field(\"any_subscriber\", &self.any_subscriber)\n                .finish()\n        }\n    }\n\n    fn warn_excessive_recursion(effect: &EffectInner) {\n        const MSG: &str = \"ImmediateEffect recursed more than once.\";\n        match effect.defined_at() {\n            Some(defined_at) => {\n                log_warning(format_args!(\"{MSG} Defined at: {defined_at}\"));\n            }\n            None => {\n                log_warning(format_args!(\"{MSG}\"));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/effect/inner.rs",
    "content": "use crate::{\n    channel::Sender,\n    graph::{\n        AnySource, AnySubscriber, ReactiveNode, SourceSet, Subscriber,\n        ToAnySubscriber,\n    },\n};\nuse or_poisoned::OrPoisoned;\nuse std::sync::{Arc, RwLock, Weak};\n\n/// Handles internal subscription logic for effects.\n#[derive(Debug)]\npub struct EffectInner {\n    pub(crate) dirty: bool,\n    pub(crate) observer: Sender,\n    pub(crate) sources: SourceSet,\n}\n\nimpl ToAnySubscriber for Arc<RwLock<EffectInner>> {\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        AnySubscriber(\n            Arc::as_ptr(self) as usize,\n            Arc::downgrade(self) as Weak<dyn Subscriber + Send + Sync>,\n        )\n    }\n}\n\nimpl ReactiveNode for RwLock<EffectInner> {\n    fn mark_subscribers_check(&self) {}\n\n    fn update_if_necessary(&self) -> bool {\n        let mut guard = self.write().or_poisoned();\n\n        if guard.dirty {\n            guard.dirty = false;\n            return true;\n        }\n\n        let sources = guard.sources.clone();\n\n        drop(guard);\n\n        sources\n            .into_iter()\n            .any(|source| source.update_if_necessary())\n    }\n\n    fn mark_check(&self) {\n        self.write().or_poisoned().observer.notify()\n    }\n\n    fn mark_dirty(&self) {\n        let mut lock = self.write().or_poisoned();\n        lock.dirty = true;\n        lock.observer.notify()\n    }\n}\n\nimpl Subscriber for RwLock<EffectInner> {\n    fn add_source(&self, source: AnySource) {\n        self.write().or_poisoned().sources.insert(source);\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        self.write().or_poisoned().sources.clear_sources(subscriber);\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/effect/render_effect.rs",
    "content": "use crate::{\n    channel::channel,\n    effect::inner::EffectInner,\n    graph::{\n        AnySubscriber, ReactiveNode, SourceSet, Subscriber, ToAnySubscriber,\n        WithObserver,\n    },\n    owner::Owner,\n};\nuse futures::StreamExt;\nuse or_poisoned::OrPoisoned;\n#[cfg(feature = \"subsecond\")]\nuse std::sync::Mutex;\nuse std::{\n    fmt::Debug,\n    future::{Future, IntoFuture},\n    mem,\n    pin::Pin,\n    sync::{Arc, RwLock, Weak},\n};\n\n/// A render effect is similar to an [`Effect`](super::Effect), but with two key differences:\n/// 1. Its first run takes place immediately and synchronously: for example, if it is being used to\n///    drive a user interface, it will run during rendering, not on the next tick after rendering.\n///    (Hence “render effect.”)\n/// 2. It is canceled when the `RenderEffect` itself is dropped, rather than being stored in the\n///    reactive system and canceled when the `Owner` cleans up.\n///\n/// Unless you are implementing a rendering framework, or require one of these two characteristics,\n/// it is unlikely you will use render effects directly.\n///\n/// Like an [`Effect`](super::Effect), a render effect runs only with the `effects` feature\n/// enabled.\n#[must_use = \"A RenderEffect will be canceled when it is dropped. Creating a \\\n              RenderEffect that is not stored in some other data structure or \\\n              leaked will drop it immediately, and it will not react to \\\n              changes in signals it reads.\"]\npub struct RenderEffect<T>\nwhere\n    T: 'static,\n{\n    value: Arc<RwLock<Option<T>>>,\n    inner: Arc<RwLock<EffectInner>>,\n}\n\nimpl<T> Debug for RenderEffect<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"RenderEffect\")\n            .field(\"inner\", &Arc::as_ptr(&self.inner))\n            .finish()\n    }\n}\n\n#[cfg(feature = \"subsecond\")]\ntype CurrentHotPtr = Box<dyn Fn() -> Option<subsecond::HotFnPtr> + Send + Sync>;\n\nimpl<T> RenderEffect<T>\nwhere\n    T: 'static,\n{\n    /// Creates a new render effect, which immediately runs `fun`.\n    pub fn new(fun: impl FnMut(Option<T>) -> T + 'static) -> Self {\n        #[cfg(feature = \"subsecond\")]\n        let (hot_fn_ptr, fun) = {\n            let fun = Arc::new(Mutex::new(subsecond::HotFn::current(fun)));\n            (\n                {\n                    let fun = Arc::downgrade(&fun);\n                    let wrapped = send_wrapper::SendWrapper::new(move || {\n                        fun.upgrade()\n                            .map(|n| n.lock().or_poisoned().ptr_address())\n                    });\n                    // it's not redundant, it's due to the SendWrapper deref\n                    #[allow(clippy::redundant_closure)]\n                    Box::new(move || wrapped())\n                },\n                move |prev| fun.lock().or_poisoned().call((prev,)),\n            )\n        };\n\n        Self::new_with_value_erased(\n            Box::new(fun),\n            None,\n            #[cfg(feature = \"subsecond\")]\n            hot_fn_ptr,\n        )\n    }\n\n    /// Creates a new render effect with an initial value.\n    pub fn new_with_value(\n        fun: impl FnMut(Option<T>) -> T + 'static,\n        initial_value: Option<T>,\n    ) -> Self {\n        #[cfg(feature = \"subsecond\")]\n        let (hot_fn_ptr, fun) = {\n            let fun = Arc::new(Mutex::new(subsecond::HotFn::current(fun)));\n            (\n                {\n                    let fun = Arc::downgrade(&fun);\n                    let wrapped = send_wrapper::SendWrapper::new(move || {\n                        fun.upgrade()\n                            .map(|n| n.lock().or_poisoned().ptr_address())\n                    });\n                    // it's not redundant, it's due to the SendWrapper deref\n                    #[allow(clippy::redundant_closure)]\n                    Box::new(move || wrapped())\n                },\n                move |prev| fun.lock().or_poisoned().call((prev,)),\n            )\n        };\n\n        Self::new_with_value_erased(\n            Box::new(fun),\n            initial_value,\n            #[cfg(feature = \"subsecond\")]\n            hot_fn_ptr,\n        )\n    }\n\n    /// Creates a new render effect, which immediately runs `fun`.\n    pub async fn new_with_async_value(\n        fun: impl FnMut(Option<T>) -> T + 'static,\n        value: impl IntoFuture<Output = T> + 'static,\n    ) -> Self {\n        #[cfg(feature = \"subsecond\")]\n        let mut fun = subsecond::HotFn::current(fun);\n        #[cfg(feature = \"subsecond\")]\n        let fun = move |prev| fun.call((prev,));\n\n        Self::new_with_async_value_erased(\n            Box::new(fun),\n            Box::pin(value.into_future()),\n        )\n        .await\n    }\n\n    fn new_with_value_erased(\n        #[allow(unused_mut)] mut fun: Box<dyn FnMut(Option<T>) -> T + 'static>,\n        initial_value: Option<T>,\n        // this argument can be used to invalidate individual effects in the future\n        // in present experiments, I have found that it is not actually granular enough to make a difference\n        #[allow(unused)]\n        #[cfg(feature = \"subsecond\")]\n        hot_fn_ptr: CurrentHotPtr,\n    ) -> Self {\n        // codegen optimisation:\n        fn prep() -> (Owner, Arc<RwLock<EffectInner>>, crate::channel::Receiver)\n        {\n            let (observer, rx) = channel();\n            let owner = Owner::new();\n            let inner = Arc::new(RwLock::new(EffectInner {\n                dirty: false,\n                observer,\n                sources: SourceSet::new(),\n            }));\n            (owner, inner, rx)\n        }\n\n        let (owner, inner, mut rx) = prep();\n\n        let value = Arc::new(RwLock::new(None::<T>));\n\n        #[cfg(not(feature = \"effects\"))]\n        {\n            let _ = initial_value;\n            let _ = owner;\n            let _ = &mut rx;\n            let _ = fun;\n        }\n\n        #[cfg(feature = \"effects\")]\n        {\n            let subscriber = inner.to_any_subscriber();\n\n            #[cfg(all(feature = \"subsecond\", debug_assertions))]\n            let mut fun = {\n                use crate::graph::ReactiveNode;\n                use rustc_hash::FxHashMap;\n                use std::sync::{Arc, LazyLock, Mutex};\n                use subsecond::HotFnPtr;\n\n                static HOT_RELOAD_SUBSCRIBERS: LazyLock<\n                    Mutex<FxHashMap<AnySubscriber, (HotFnPtr, CurrentHotPtr)>>,\n                > = LazyLock::new(|| {\n                    subsecond::register_handler(Arc::new(|| {\n                        HOT_RELOAD_SUBSCRIBERS.lock().or_poisoned().retain(\n                            |subscriber, (prev_ptr, hot_fn_ptr)| {\n                                match hot_fn_ptr() {\n                                    None => false,\n                                    Some(curr_hot_ptr) => {\n                                        if curr_hot_ptr != *prev_ptr {\n                                            crate::log_warning(format_args!(\n                                                \"{prev_ptr:?} <> \\\n                                                 {curr_hot_ptr:?}\",\n                                            ));\n                                            *prev_ptr = curr_hot_ptr;\n\n                                            subscriber.mark_dirty();\n                                        }\n                                        true\n                                    }\n                                }\n                            },\n                        );\n                    }));\n                    Default::default()\n                });\n\n                let mut fun = subsecond::HotFn::current(fun);\n                let initial_ptr = hot_fn_ptr().unwrap();\n                HOT_RELOAD_SUBSCRIBERS\n                    .lock()\n                    .or_poisoned()\n                    .insert(subscriber.clone(), (initial_ptr, hot_fn_ptr));\n                move |prev| fun.call((prev,))\n            };\n\n            *value.write().or_poisoned() = Some(\n                owner.with(|| subscriber.with_observer(|| fun(initial_value))),\n            );\n\n            any_spawner::Executor::spawn_local({\n                let value = Arc::clone(&value);\n\n                async move {\n                    while rx.next().await.is_some() {\n                        if !owner.paused()\n                            && subscriber.with_observer(|| {\n                                subscriber.update_if_necessary()\n                            })\n                        {\n                            subscriber.clear_sources(&subscriber);\n\n                            let old_value =\n                                mem::take(&mut *value.write().or_poisoned());\n                            let new_value = owner.with_cleanup(|| {\n                                subscriber.with_observer(|| fun(old_value))\n                            });\n                            *value.write().or_poisoned() = Some(new_value);\n                        }\n                    }\n                }\n            });\n        }\n\n        RenderEffect { value, inner }\n    }\n\n    async fn new_with_async_value_erased(\n        mut fun: Box<dyn FnMut(Option<T>) -> T + 'static>,\n        initial_value: Pin<Box<dyn Future<Output = T>>>,\n    ) -> Self {\n        // codegen optimisation:\n        fn prep() -> (Owner, Arc<RwLock<EffectInner>>, crate::channel::Receiver)\n        {\n            let (observer, rx) = channel();\n            let owner = Owner::new();\n            let inner = Arc::new(RwLock::new(EffectInner {\n                dirty: false,\n                observer,\n                sources: SourceSet::new(),\n            }));\n            (owner, inner, rx)\n        }\n\n        let (owner, inner, mut rx) = prep();\n\n        let value = Arc::new(RwLock::new(None::<T>));\n\n        #[cfg(not(feature = \"effects\"))]\n        {\n            drop(initial_value);\n            let _ = owner;\n            let _ = &mut rx;\n            let _ = &mut fun;\n        }\n\n        #[cfg(feature = \"effects\")]\n        {\n            use crate::computed::ScopedFuture;\n\n            let subscriber = inner.to_any_subscriber();\n\n            let initial = subscriber\n                .with_observer(|| ScopedFuture::new(initial_value))\n                .await;\n            *value.write().or_poisoned() = Some(initial);\n\n            any_spawner::Executor::spawn_local({\n                let value = Arc::clone(&value);\n\n                async move {\n                    while rx.next().await.is_some() {\n                        if !owner.paused()\n                            && subscriber.with_observer(|| {\n                                subscriber.update_if_necessary()\n                            })\n                        {\n                            subscriber.clear_sources(&subscriber);\n\n                            let old_value =\n                                mem::take(&mut *value.write().or_poisoned());\n                            let new_value = owner.with_cleanup(|| {\n                                subscriber.with_observer(|| fun(old_value))\n                            });\n                            *value.write().or_poisoned() = Some(new_value);\n                        }\n                    }\n                }\n            });\n        }\n\n        RenderEffect { value, inner }\n    }\n\n    /// Mutably accesses the current value.\n    pub fn with_value_mut<U>(\n        &self,\n        fun: impl FnOnce(&mut T) -> U,\n    ) -> Option<U> {\n        self.value.write().or_poisoned().as_mut().map(fun)\n    }\n\n    /// Takes the current value, replacing it with `None`.\n    pub fn take_value(&self) -> Option<T> {\n        self.value.write().or_poisoned().take()\n    }\n}\n\nimpl<T> RenderEffect<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    /// Creates a render effect that will run whether the `effects` feature is enabled or not.\n    pub fn new_isomorphic(\n        fun: impl FnMut(Option<T>) -> T + Send + Sync + 'static,\n    ) -> Self {\n        #[cfg(feature = \"subsecond\")]\n        let mut fun = subsecond::HotFn::current(fun);\n        #[cfg(feature = \"subsecond\")]\n        let fun = move |prev| fun.call((prev,));\n\n        fn erased<T: Send + Sync + 'static>(\n            mut fun: Box<dyn FnMut(Option<T>) -> T + Send + Sync + 'static>,\n        ) -> RenderEffect<T> {\n            let (observer, mut rx) = channel();\n            let value = Arc::new(RwLock::new(None::<T>));\n            let owner = Owner::new();\n            let inner = Arc::new(RwLock::new(EffectInner {\n                dirty: false,\n                observer,\n                sources: SourceSet::new(),\n            }));\n\n            let initial_value = owner\n                .with(|| inner.to_any_subscriber().with_observer(|| fun(None)));\n            *value.write().or_poisoned() = Some(initial_value);\n\n            crate::spawn({\n                let value = Arc::clone(&value);\n                let subscriber = inner.to_any_subscriber();\n\n                async move {\n                    while rx.next().await.is_some() {\n                        if !owner.paused()\n                            && subscriber.with_observer(|| {\n                                subscriber.update_if_necessary()\n                            })\n                        {\n                            subscriber.clear_sources(&subscriber);\n\n                            let old_value =\n                                mem::take(&mut *value.write().or_poisoned());\n                            let new_value = owner.with_cleanup(|| {\n                                subscriber.with_observer(|| fun(old_value))\n                            });\n                            *value.write().or_poisoned() = Some(new_value);\n                        }\n                    }\n                }\n            });\n\n            RenderEffect { value, inner }\n        }\n\n        erased(Box::new(fun))\n    }\n}\n\nimpl<T> ToAnySubscriber for RenderEffect<T> {\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        AnySubscriber(\n            Arc::as_ptr(&self.inner) as usize,\n            Arc::downgrade(&self.inner) as Weak<dyn Subscriber + Send + Sync>,\n        )\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/effect.rs",
    "content": "//! Side effects that run in response to changes in the reactive values they read from.\n\n#[allow(clippy::module_inception)]\nmod effect;\nmod effect_function;\nmod immediate;\nmod inner;\nmod render_effect;\n\npub use effect::*;\npub use effect_function::*;\npub use immediate::*;\npub use render_effect::*;\n\n/// Creates a new render effect, which immediately runs `fun`.\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `RenderEffect::new()` instead.\"]\npub fn create_render_effect<T>(\n    fun: impl FnMut(Option<T>) -> T + 'static,\n) -> RenderEffect<T>\nwhere\n    T: 'static,\n{\n    RenderEffect::new(fun)\n}\n"
  },
  {
    "path": "reactive_graph/src/graph/node.rs",
    "content": "/// A node in the reactive graph.\npub trait ReactiveNode {\n    /// Notifies the source's dependencies that it has changed.\n    fn mark_dirty(&self);\n\n    /// Notifies the source's dependencies that it may have changed.\n    fn mark_check(&self);\n\n    /// Marks that all subscribers need to be checked.\n    fn mark_subscribers_check(&self);\n\n    /// Regenerates the value for this node, if needed, and returns whether\n    /// it has actually changed or not.\n    fn update_if_necessary(&self) -> bool;\n}\n\n/// The current state of a reactive node.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub enum ReactiveNodeState {\n    /// The node is known to be clean: i.e., either none of its sources have changed, or its\n    /// sources have changed but its value is unchanged and its dependencies do not need to change.\n    Clean,\n    /// The node may have changed, but it is not yet known whether it has actually changed.\n    Check,\n    /// The node's value has definitely changed, and subscribers will need to update.\n    Dirty,\n}\n"
  },
  {
    "path": "reactive_graph/src/graph/sets.rs",
    "content": "//! Types that hold the set of sources or subscribers affiliated with a reactive node.\n//!\n//! At the moment, these are implemented as linear maps built on a `Vec<_>`. This is for the sake\n//! of minimizing binary size as much as possible, and on the assumption that the M:N relationship\n//! between sources and subscribers usually consists of fairly small numbers, such that the cost of\n//! a linear search is not significantly more expensive than a hash and lookup.\n\nuse super::{AnySource, AnySubscriber, Source};\nuse indexmap::IndexSet;\nuse rustc_hash::FxHasher;\nuse std::{hash::BuildHasherDefault, mem};\n\ntype FxIndexSet<T> = IndexSet<T, BuildHasherDefault<FxHasher>>;\n\n#[derive(Default, Clone, Debug)]\npub struct SourceSet(FxIndexSet<AnySource>);\n\nimpl SourceSet {\n    pub fn new() -> Self {\n        Self(Default::default())\n    }\n\n    pub fn insert(&mut self, source: AnySource) {\n        self.0.insert(source);\n    }\n\n    pub fn remove(&mut self, source: &AnySource) {\n        self.0.shift_remove(source);\n    }\n\n    pub fn take(&mut self) -> FxIndexSet<AnySource> {\n        mem::take(&mut self.0)\n    }\n\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n\n    pub fn clear_sources(&mut self, subscriber: &AnySubscriber) {\n        for source in self.take() {\n            source.remove_subscriber(subscriber);\n        }\n    }\n}\n\nimpl IntoIterator for SourceSet {\n    type Item = AnySource;\n    type IntoIter = <FxIndexSet<AnySource> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.into_iter()\n    }\n}\n\nimpl<'a> IntoIterator for &'a SourceSet {\n    type Item = &'a AnySource;\n    type IntoIter = <&'a FxIndexSet<AnySource> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.iter()\n    }\n}\n#[derive(Debug, Default, Clone)]\npub struct SubscriberSet(FxIndexSet<AnySubscriber>);\n\nimpl SubscriberSet {\n    pub fn new() -> Self {\n        Self(FxIndexSet::with_capacity_and_hasher(2, Default::default()))\n    }\n\n    pub fn subscribe(&mut self, subscriber: AnySubscriber) {\n        self.0.insert(subscriber);\n    }\n\n    pub fn unsubscribe(&mut self, subscriber: &AnySubscriber) {\n        // note: do not use `.swap_remove()` here.\n        // using `.remove()` is slower because it shifts other items\n        // but it maintains the order of the subscribers, which is important\n        // to correctness when you're using this to drive something like a UI,\n        // which can have nested effects, where the inner one assumes the outer\n        // has already run (for example, an outer effect that checks .is_some(),\n        // and an inner effect that unwraps)\n        self.0.shift_remove(subscriber);\n    }\n\n    pub fn take(&mut self) -> FxIndexSet<AnySubscriber> {\n        mem::take(&mut self.0)\n    }\n\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n}\n\nimpl IntoIterator for SubscriberSet {\n    type Item = AnySubscriber;\n    type IntoIter = <FxIndexSet<AnySubscriber> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.into_iter()\n    }\n}\n\nimpl<'a> IntoIterator for &'a SubscriberSet {\n    type Item = &'a AnySubscriber;\n    type IntoIter = <&'a FxIndexSet<AnySubscriber> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.iter()\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/graph/source.rs",
    "content": "use super::{node::ReactiveNode, AnySubscriber};\nuse crate::traits::{DefinedAt, IsDisposed};\nuse core::{fmt::Debug, hash::Hash};\nuse std::{panic::Location, sync::Weak};\n\n/// Abstracts over the type of any reactive source.\npub trait ToAnySource: IsDisposed {\n    /// Converts this type to its type-erased equivalent.\n    fn to_any_source(&self) -> AnySource;\n}\n\n/// Describes the behavior of any source of reactivity (like a signal, trigger, or memo.)\npub trait Source: ReactiveNode {\n    /// Adds a subscriber to this source's list of dependencies.\n    fn add_subscriber(&self, subscriber: AnySubscriber);\n\n    /// Removes a subscriber from this source's list of dependencies.\n    fn remove_subscriber(&self, subscriber: &AnySubscriber);\n\n    /// Remove all subscribers from this source's list of dependencies.\n    fn clear_subscribers(&self);\n}\n\n/// A weak reference to any reactive source node.\n#[derive(Clone)]\npub struct AnySource(\n    pub(crate) usize,\n    pub(crate) Weak<dyn Source + Send + Sync>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate)  &'static Location<'static>,\n);\n\nimpl DefinedAt for AnySource {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.2)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl Debug for AnySource {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_tuple(\"AnySource\").field(&self.0).finish()\n    }\n}\n\nimpl Hash for AnySource {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.0.hash(state);\n    }\n}\n\nimpl PartialEq for AnySource {\n    fn eq(&self, other: &Self) -> bool {\n        self.0 == other.0\n    }\n}\n\nimpl Eq for AnySource {}\n\nimpl IsDisposed for AnySource {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl ToAnySource for AnySource {\n    fn to_any_source(&self) -> AnySource {\n        self.clone()\n    }\n}\n\nimpl Source for AnySource {\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.add_subscriber(subscriber)\n        }\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.remove_subscriber(subscriber)\n        }\n    }\n\n    fn clear_subscribers(&self) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.clear_subscribers();\n        }\n    }\n}\n\nimpl ReactiveNode for AnySource {\n    fn mark_dirty(&self) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.mark_dirty()\n        }\n    }\n\n    fn mark_subscribers_check(&self) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.mark_subscribers_check()\n        }\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        if let Some(inner) = self.1.upgrade() {\n            inner.update_if_necessary()\n        } else {\n            false\n        }\n    }\n\n    fn mark_check(&self) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.mark_check()\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/graph/subscriber.rs",
    "content": "use super::{node::ReactiveNode, AnySource};\n#[cfg(debug_assertions)]\nuse crate::diagnostics::SpecialNonReactiveZone;\nuse core::{fmt::Debug, hash::Hash};\nuse std::{cell::RefCell, mem, sync::Weak};\n\nthread_local! {\n    static OBSERVER: RefCell<Option<ObserverState>> = const { RefCell::new(None) };\n}\n\n#[derive(Debug)]\nstruct ObserverState {\n    subscriber: AnySubscriber,\n    untracked: bool,\n}\n\n/// The current reactive observer.\n///\n/// The observer is whatever reactive node is currently listening for signals that need to be\n/// tracked. For example, if an effect is running, that effect is the observer, which means it will\n/// subscribe to changes in any signals that are read.\npub struct Observer;\n\n#[derive(Debug)]\nstruct SetObserverOnDrop(Option<AnySubscriber>);\n\nimpl Drop for SetObserverOnDrop {\n    fn drop(&mut self) {\n        Observer::set(self.0.take());\n    }\n}\n\nimpl Observer {\n    /// Returns the current observer, if any.\n    pub fn get() -> Option<AnySubscriber> {\n        OBSERVER.with_borrow(|obs| {\n            obs.as_ref().and_then(|obs| {\n                if obs.untracked {\n                    None\n                } else {\n                    Some(obs.subscriber.clone())\n                }\n            })\n        })\n    }\n\n    pub(crate) fn is(observer: &AnySubscriber) -> bool {\n        OBSERVER.with_borrow(|o| {\n            o.as_ref().map(|o| &o.subscriber) == Some(observer)\n        })\n    }\n\n    fn take() -> SetObserverOnDrop {\n        SetObserverOnDrop(\n            OBSERVER.with_borrow_mut(Option::take).map(|o| o.subscriber),\n        )\n    }\n\n    fn set(observer: Option<AnySubscriber>) {\n        OBSERVER.with_borrow_mut(|o| {\n            *o = observer.map(|subscriber| ObserverState {\n                subscriber,\n                untracked: false,\n            })\n        });\n    }\n\n    fn replace(observer: Option<AnySubscriber>) -> SetObserverOnDrop {\n        SetObserverOnDrop(\n            OBSERVER\n                .with_borrow_mut(|o| {\n                    mem::replace(\n                        o,\n                        observer.map(|subscriber| ObserverState {\n                            subscriber,\n                            untracked: false,\n                        }),\n                    )\n                })\n                .map(|o| o.subscriber),\n        )\n    }\n\n    fn replace_untracked(observer: Option<AnySubscriber>) -> SetObserverOnDrop {\n        SetObserverOnDrop(\n            OBSERVER\n                .with_borrow_mut(|o| {\n                    mem::replace(\n                        o,\n                        observer.map(|subscriber| ObserverState {\n                            subscriber,\n                            untracked: true,\n                        }),\n                    )\n                })\n                .map(|o| o.subscriber),\n        )\n    }\n}\n\n/// Suspends reactive tracking while running the given function.\n///\n/// This can be used to isolate parts of the reactive graph from one another.\n///\n/// ```rust\n/// # use reactive_graph::computed::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::graph::untrack;\n/// # tokio_test::block_on(async move {\n/// # any_spawner::Executor::init_tokio(); let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let (a, set_a) = signal(0);\n/// let (b, set_b) = signal(0);\n/// let c = Memo::new(move |_| {\n///     // this memo will *only* update when `a` changes\n///     a.get() + untrack(move || b.get())\n/// });\n///\n/// assert_eq!(c.get(), 0);\n/// set_a.set(1);\n/// assert_eq!(c.get(), 1);\n/// set_b.set(1);\n/// // hasn't updated, because we untracked before reading b\n/// assert_eq!(c.get(), 1);\n/// set_a.set(2);\n/// assert_eq!(c.get(), 3);\n/// # });\n/// ```\n#[track_caller]\npub fn untrack<T>(fun: impl FnOnce() -> T) -> T {\n    #[cfg(debug_assertions)]\n    let _warning_guard = crate::diagnostics::SpecialNonReactiveZone::enter();\n\n    let _prev = Observer::take();\n    fun()\n}\n\n#[doc(hidden)]\n#[track_caller]\npub fn untrack_with_diagnostics<T>(fun: impl FnOnce() -> T) -> T {\n    let _prev = Observer::take();\n    fun()\n}\n\n/// Converts a [`Subscriber`] to a type-erased [`AnySubscriber`].\npub trait ToAnySubscriber {\n    /// Converts this type to its type-erased equivalent.\n    fn to_any_subscriber(&self) -> AnySubscriber;\n}\n\n/// Any type that can track reactive values (like an effect or a memo).\npub trait Subscriber: ReactiveNode {\n    /// Adds a subscriber to this subscriber's list of dependencies.\n    fn add_source(&self, source: AnySource);\n\n    /// Clears the set of sources for this subscriber.\n    fn clear_sources(&self, subscriber: &AnySubscriber);\n}\n\n/// A type-erased subscriber.\n#[derive(Clone)]\npub struct AnySubscriber(pub usize, pub Weak<dyn Subscriber + Send + Sync>);\n\nimpl ToAnySubscriber for AnySubscriber {\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        self.clone()\n    }\n}\n\nimpl Subscriber for AnySubscriber {\n    fn add_source(&self, source: AnySource) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.add_source(source);\n        }\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.clear_sources(subscriber);\n        }\n    }\n}\n\nimpl ReactiveNode for AnySubscriber {\n    fn mark_dirty(&self) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.mark_dirty()\n        }\n    }\n\n    fn mark_subscribers_check(&self) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.mark_subscribers_check()\n        }\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        if let Some(inner) = self.1.upgrade() {\n            inner.update_if_necessary()\n        } else {\n            false\n        }\n    }\n\n    fn mark_check(&self) {\n        if let Some(inner) = self.1.upgrade() {\n            inner.mark_check()\n        }\n    }\n}\n\n/// Runs code with some subscriber as the thread-local [`Observer`].\npub trait WithObserver {\n    /// Runs the given function with this subscriber as the thread-local [`Observer`].\n    fn with_observer<T>(&self, fun: impl FnOnce() -> T) -> T;\n\n    /// Runs the given function with this subscriber as the thread-local [`Observer`],\n    /// but without tracking dependencies.\n    fn with_observer_untracked<T>(&self, fun: impl FnOnce() -> T) -> T;\n}\n\nimpl WithObserver for AnySubscriber {\n    fn with_observer<T>(&self, fun: impl FnOnce() -> T) -> T {\n        let _prev = Observer::replace(Some(self.clone()));\n        fun()\n    }\n\n    fn with_observer_untracked<T>(&self, fun: impl FnOnce() -> T) -> T {\n        #[cfg(debug_assertions)]\n        let _guard = SpecialNonReactiveZone::enter();\n        let _prev = Observer::replace_untracked(Some(self.clone()));\n        fun()\n    }\n}\n\nimpl WithObserver for Option<AnySubscriber> {\n    fn with_observer<T>(&self, fun: impl FnOnce() -> T) -> T {\n        let _prev = Observer::replace(self.clone());\n        fun()\n    }\n\n    fn with_observer_untracked<T>(&self, fun: impl FnOnce() -> T) -> T {\n        #[cfg(debug_assertions)]\n        let _guard = SpecialNonReactiveZone::enter();\n        let _prev = Observer::replace_untracked(self.clone());\n        fun()\n    }\n}\n\nimpl Debug for AnySubscriber {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_tuple(\"AnySubscriber\").field(&self.0).finish()\n    }\n}\n\nimpl Hash for AnySubscriber {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.0.hash(state);\n    }\n}\n\nimpl PartialEq for AnySubscriber {\n    fn eq(&self, other: &Self) -> bool {\n        self.0 == other.0\n    }\n}\n\nimpl Eq for AnySubscriber {}\n"
  },
  {
    "path": "reactive_graph/src/graph.rs",
    "content": "//! Types that define the reactive graph itself. These are mostly internal, but can be used to\n//! create custom reactive primitives.\n\nmod node;\nmod sets;\nmod source;\nmod subscriber;\n\npub use node::*;\npub(crate) use sets::*;\npub use source::*;\npub use subscriber::*;\n"
  },
  {
    "path": "reactive_graph/src/into_reactive_value.rs",
    "content": "#[doc(hidden)]\npub struct __IntoReactiveValueMarkerBaseCase;\n\n/// A helper trait that works like `Into<T>` but uses a marker generic\n/// to allow more `From` implementations than would be allowed with just `Into<T>`.\npub trait IntoReactiveValue<T, M> {\n    /// Converts `self` into a `T`.\n    fn into_reactive_value(self) -> T;\n}\n\n// The base case, which allows anything which implements .into() to work:\nimpl<T, I> IntoReactiveValue<T, __IntoReactiveValueMarkerBaseCase> for I\nwhere\n    I: Into<T>,\n{\n    fn into_reactive_value(self) -> T {\n        self.into()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n\n    use crate::{\n        into_reactive_value::IntoReactiveValue,\n        owner::{LocalStorage, Owner},\n        traits::GetUntracked,\n        wrappers::read::Signal,\n    };\n    use typed_builder::TypedBuilder;\n\n    #[test]\n    fn test_into_signal_compiles() {\n        let owner = Owner::new();\n        owner.set();\n\n        #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n        let _: Signal<usize> = (|| 2).into_reactive_value();\n        let _: Signal<usize, LocalStorage> = 2.into_reactive_value();\n        #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n        let _: Signal<usize, LocalStorage> = (|| 2).into_reactive_value();\n        let _: Signal<String> = \"str\".into_reactive_value();\n        let _: Signal<String, LocalStorage> = \"str\".into_reactive_value();\n\n        #[derive(TypedBuilder)]\n        struct Foo {\n            #[builder(setter(\n                fn transform<M>(value: impl IntoReactiveValue<Signal<usize>, M>) {\n                    value.into_reactive_value()\n                }\n            ))]\n            sig: Signal<usize>,\n        }\n\n        assert_eq!(Foo::builder().sig(2).build().sig.get_untracked(), 2);\n        #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n        assert_eq!(Foo::builder().sig(|| 2).build().sig.get_untracked(), 2);\n        assert_eq!(\n            Foo::builder()\n                .sig(Signal::stored(2))\n                .build()\n                .sig\n                .get_untracked(),\n            2\n        );\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/lib.rs",
    "content": "//! An implementation of a fine-grained reactive system.\n//!\n//! Fine-grained reactivity is an approach to modeling the flow of data through an interactive\n//! application by composing together three categories of reactive primitives:\n//! 1. **Signals**: atomic units of state, which can be directly mutated.\n//! 2. **Computations**: derived values, which cannot be mutated directly but update whenever the signals\n//!    they depend on change. These include both synchronous and asynchronous derived values.\n//! 3. **Effects**: side effects that synchronize the reactive system with the non-reactive world\n//!    outside it.\n//!\n//! Signals and computations are \"source\" nodes in the reactive graph, because an observer can\n//! subscribe to them to respond to changes in their values. Effects and computations are \"subscriber\"\n//! nodes, because they can listen to changes in other values.\n//!\n//! ```rust\n//! # any_spawner::Executor::init_futures_executor();\n//! # let owner = reactive_graph::owner::Owner::new(); owner.set();\n//! use reactive_graph::{\n//!     computed::ArcMemo,\n//!     effect::Effect,\n//!     prelude::{Read, Set},\n//!     signal::ArcRwSignal,\n//! };\n//!\n//! let count = ArcRwSignal::new(1);\n//! let double_count = ArcMemo::new({\n//!     let count = count.clone();\n//!     move |_| *count.read() * 2\n//! });\n//!\n//! // the effect will run once initially\n//! Effect::new(move |_| {\n//!     println!(\"double_count = {}\", *double_count.read());\n//! });\n//!\n//! // updating `count` will propagate changes to the dependencies,\n//! // causing the effect to run again\n//! count.set(2);\n//! ```\n//!\n//! This reactivity is called \"fine grained\" because updating the value of a signal only affects\n//! the effects and computations that depend on its value, without requiring any diffing or update\n//! calculations for other values.\n//!\n//! This model is especially suitable for building user interfaces, i.e., long-lived systems in\n//! which changes can begin from many different entry points. It is not particularly useful in\n//! \"run-once\" programs like a CLI.\n//!\n//! ## Design Principles and Assumptions\n//! - **Effects are expensive.** The library is built on the assumption that the side effects\n//!   (making a network request, rendering something to the DOM, writing to disk) are orders of\n//!   magnitude more expensive than propagating signal updates. As a result, the algorithm is\n//!   designed to avoid re-running side effects unnecessarily, and is willing to sacrifice a small\n//!   amount of raw update speed to that goal.\n//! - **Automatic dependency tracking.** Dependencies are not specified as a compile-time list, but\n//!   tracked at runtime. This in turn enables **dynamic dependency tracking**: subscribers\n//!   unsubscribe from their sources between runs, which means that a subscriber that contains a\n//!   condition branch will not re-run when dependencies update that are only used in the inactive\n//!   branch.\n//! - **Asynchronous effect scheduling.** Effects are spawned as asynchronous tasks. This means\n//!   that while updating a signal will immediately update its value, effects that depend on it\n//!   will not run until the next \"tick\" of the async runtime. (This in turn means that the\n//!   reactive system is *async runtime agnostic*: it can be used in the browser with\n//!   `wasm-bindgen-futures`, in a native binary with `tokio`, in a GTK application with `glib`,\n//!   etc.)\n//!\n//! The reactive-graph algorithm used in this crate is based on that of\n//! [Reactively](https://github.com/modderme123/reactively), as described\n//! [in this article](https://dev.to/modderme123/super-charging-fine-grained-reactive-performance-47ph).\n\n#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(unboxed_closures))]\n#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(fn_traits))]\n#![deny(missing_docs)]\n\nuse std::{fmt::Arguments, future::Future};\n\npub mod actions;\npub(crate) mod channel;\npub mod computed;\npub mod diagnostics;\npub mod effect;\npub mod graph;\npub mod owner;\npub mod send_wrapper_ext;\n#[cfg(feature = \"serde\")]\nmod serde;\npub mod signal;\nmod trait_options;\npub mod traits;\npub mod transition;\npub mod wrappers;\n\nmod into_reactive_value;\npub use into_reactive_value::*;\n\n/// A standard way to wrap functions and closures to pass them to components.\npub mod callback;\n\nuse computed::ScopedFuture;\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\nmod nightly;\n\n/// Reexports frequently-used traits.\npub mod prelude {\n    pub use crate::{\n        into_reactive_value::IntoReactiveValue, owner::FromLocal, traits::*,\n    };\n}\n\n// TODO remove this, it's just useful while developing\n#[allow(unused)]\n#[doc(hidden)]\npub fn log_warning(text: Arguments) {\n    #[cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))]\n    {\n        web_sys::console::warn_1(&text.to_string().into());\n    }\n    #[cfg(all(\n        not(feature = \"tracing\"),\n        not(all(target_arch = \"wasm32\", target_os = \"unknown\"))\n    ))]\n    {\n        eprintln!(\"{text}\");\n    }\n}\n\n/// Calls [`Executor::spawn`](any_spawner::Executor::spawn) on non-wasm targets and [`Executor::spawn_local`](any_spawner::Executor::spawn_local) on wasm targets, but ensures that the task also runs in the current arena, if\n/// multithreaded arena sandboxing is enabled.\npub fn spawn(task: impl Future<Output = ()> + Send + 'static) {\n    #[cfg(feature = \"sandboxed-arenas\")]\n    let task = owner::Sandboxed::new(task);\n\n    #[cfg(not(target_family = \"wasm\"))]\n    any_spawner::Executor::spawn(task);\n\n    #[cfg(target_family = \"wasm\")]\n    any_spawner::Executor::spawn_local(task);\n}\n\n/// Calls [`Executor::spawn_local`](any_spawner::Executor::spawn_local), but ensures that the task also runs in the current arena, if\n/// multithreaded arena sandboxing is enabled.\npub fn spawn_local(task: impl Future<Output = ()> + 'static) {\n    #[cfg(feature = \"sandboxed-arenas\")]\n    let task = owner::Sandboxed::new(task);\n\n    any_spawner::Executor::spawn_local(task);\n}\n\n/// Calls [`Executor::spawn_local`](any_spawner::Executor), but ensures that the task runs under the current reactive [`Owner`](crate::owner::Owner) and observer.\n///\n/// Does not cancel the task if the owner is cleaned up.\npub fn spawn_local_scoped(task: impl Future<Output = ()> + 'static) {\n    let task = ScopedFuture::new(task);\n\n    #[cfg(feature = \"sandboxed-arenas\")]\n    let task = owner::Sandboxed::new(task);\n\n    any_spawner::Executor::spawn_local(task);\n}\n\n/// Calls [`Executor::spawn_local`](any_spawner::Executor), but ensures that the task runs under the current reactive [`Owner`](crate::owner::Owner) and observer.\n///\n/// Cancels the task if the owner is cleaned up.\npub fn spawn_local_scoped_with_cancellation(\n    task: impl Future<Output = ()> + 'static,\n) {\n    use crate::owner::on_cleanup;\n    use futures::future::{AbortHandle, Abortable};\n\n    let (abort_handle, abort_registration) = AbortHandle::new_pair();\n    on_cleanup(move || abort_handle.abort());\n\n    let task = Abortable::new(task, abort_registration);\n    let task = ScopedFuture::new(task);\n\n    #[cfg(feature = \"sandboxed-arenas\")]\n    let task = owner::Sandboxed::new(task);\n\n    any_spawner::Executor::spawn_local(async move {\n        _ = task.await;\n    });\n}\n"
  },
  {
    "path": "reactive_graph/src/nightly.rs",
    "content": "#[allow(deprecated)]\nuse crate::wrappers::read::{MaybeProp, MaybeSignal};\nuse crate::{\n    computed::{ArcMemo, Memo},\n    owner::Storage,\n    signal::{\n        ArcReadSignal, ArcRwSignal, ArcWriteSignal, ReadSignal, RwSignal,\n        WriteSignal,\n    },\n    traits::{Get, Set},\n    wrappers::{\n        read::{ArcSignal, Signal, SignalTypes},\n        write::SignalSetter,\n    },\n};\n\nmacro_rules! impl_set_fn_traits {\n    ($($ty:ident),*) => {\n        $(\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T> FnOnce<(T,)> for $ty<T> where $ty<T>: Set<Value = T> {\n                type Output = ();\n\n                #[inline(always)]\n                extern \"rust-call\" fn call_once(self, args: (T,)) -> Self::Output {\n                    self.set(args.0);\n                }\n            }\n\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T> FnMut<(T,)> for $ty<T> where $ty<T>: Set<Value = T> {\n                #[inline(always)]\n                extern \"rust-call\" fn call_mut(&mut self, args: (T,)) -> Self::Output {\n                    self.set(args.0);\n                }\n            }\n\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T> Fn<(T,)> for $ty<T> where $ty<T>: Set<Value = T> {\n                #[inline(always)]\n                extern \"rust-call\" fn call(&self, args: (T,)) -> Self::Output {\n                    self.set(args.0);\n                }\n            }\n        )*\n    };\n}\n\nmacro_rules! impl_set_fn_traits_arena {\n    ($($ty:ident),*) => {\n        $(\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T, S> FnOnce<(T,)> for $ty<T, S> where $ty<T, S>: Set<Value = T> {\n                type Output = ();\n\n                #[inline(always)]\n                extern \"rust-call\" fn call_once(self, args: (T,)) -> Self::Output {\n                    self.set(args.0);\n                }\n            }\n\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T, S> FnMut<(T,)> for $ty<T, S> where $ty<T, S>: Set<Value = T> {\n                #[inline(always)]\n                extern \"rust-call\" fn call_mut(&mut self, args: (T,)) -> Self::Output {\n                    self.set(args.0);\n                }\n            }\n\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T, S> Fn<(T,)> for $ty<T, S> where $ty<T, S>: Set<Value = T> {\n                #[inline(always)]\n                extern \"rust-call\" fn call(&self, args: (T,)) -> Self::Output {\n                    self.set(args.0);\n                }\n            }\n        )*\n    };\n}\n\nmacro_rules! impl_get_fn_traits_get {\n    ($($ty:ident),*) => {\n        $(\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T> FnOnce<()> for $ty<T> where $ty<T>: Get {\n                type Output = <Self as Get>::Value;\n\n                #[inline(always)]\n                extern \"rust-call\" fn call_once(self, _args: ()) -> Self::Output {\n                    self.get()\n                }\n            }\n\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T> FnMut<()> for $ty<T> where $ty<T>: Get {\n                #[inline(always)]\n                extern \"rust-call\" fn call_mut(&mut self, _args: ()) -> Self::Output {\n                    self.get()\n                }\n            }\n\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            impl<T> Fn<()> for $ty<T> where $ty<T>: Get {\n                #[inline(always)]\n                extern \"rust-call\" fn call(&self, _args: ()) -> Self::Output {\n                    self.get()\n                }\n            }\n        )*\n    };\n}\n\nmacro_rules! impl_get_fn_traits_get_arena {\n    ($($ty:ident),*) => {\n        $(\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            #[allow(deprecated)]\n            impl<T, S> FnOnce<()> for $ty<T, S> where $ty<T, S>: Get, S: Storage<T> + Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>> {\n                type Output = <Self as Get>::Value;\n\n                #[inline(always)]\n                extern \"rust-call\" fn call_once(self, _args: ()) -> Self::Output {\n                    self.get()\n                }\n            }\n\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            #[allow(deprecated)]\n            impl<T, S> FnMut<()> for $ty<T, S> where $ty<T, S>: Get, S: Storage<T> + Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>> {\n                #[inline(always)]\n                extern \"rust-call\" fn call_mut(&mut self, _args: ()) -> Self::Output {\n                    self.get()\n                }\n            }\n\n            #[cfg(all(feature = \"nightly\", rustc_nightly))]\n            #[allow(deprecated)]\n            impl<T, S> Fn<()> for $ty<T, S> where $ty<T, S>: Get, S: Storage<T> + Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>> {\n                #[inline(always)]\n                extern \"rust-call\" fn call(&self, _args: ()) -> Self::Output {\n                    self.get()\n                }\n            }\n        )*\n    };\n}\n\nimpl_get_fn_traits_get![ArcReadSignal, ArcRwSignal];\nimpl_get_fn_traits_get_arena![\n    ReadSignal,\n    RwSignal,\n    ArcMemo,\n    ArcSignal,\n    Signal,\n    MaybeSignal,\n    Memo,\n    MaybeProp\n];\nimpl_set_fn_traits![ArcRwSignal, ArcWriteSignal];\nimpl_set_fn_traits_arena![RwSignal, WriteSignal, SignalSetter];\n"
  },
  {
    "path": "reactive_graph/src/owner/arc_stored_value.rs",
    "content": "use crate::{\n    signal::guards::{Plain, ReadGuard, UntrackedWriteGuard},\n    traits::{DefinedAt, IntoInner, IsDisposed, ReadValue, WriteValue},\n};\nuse std::{\n    fmt::{Debug, Formatter},\n    hash::Hash,\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\n/// A reference-counted getter for any value non-reactively.\n///\n/// This is a reference-counted value, which is `Clone` but not `Copy`.\n/// For arena-allocated `Copy` values, use [`StoredValue`](super::StoredValue).\n///\n/// This allows you to create a stable reference for any value by storing it within\n/// the reactive system. Unlike e.g. [`ArcRwSignal`](crate::signal::ArcRwSignal), it is not reactive;\n/// accessing it does not cause effects to subscribe, and\n/// updating it does not notify anything else.\npub struct ArcStoredValue<T> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    value: Arc<RwLock<T>>,\n}\n\nimpl<T> Clone for ArcStoredValue<T> {\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            value: Arc::clone(&self.value),\n        }\n    }\n}\n\nimpl<T> Debug for ArcStoredValue<T> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"ArcStoredValue\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"value\", &Arc::as_ptr(&self.value))\n            .finish()\n    }\n}\n\nimpl<T: Default> Default for ArcStoredValue<T> {\n    #[track_caller]\n    fn default() -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: Arc::new(RwLock::new(T::default())),\n        }\n    }\n}\n\nimpl<T> PartialEq for ArcStoredValue<T> {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.value, &other.value)\n    }\n}\n\nimpl<T> Eq for ArcStoredValue<T> {}\n\nimpl<T> Hash for ArcStoredValue<T> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        std::ptr::hash(&Arc::as_ptr(&self.value), state);\n    }\n}\n\nimpl<T> DefinedAt for ArcStoredValue<T> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> ArcStoredValue<T> {\n    /// Creates a new stored value, taking the initial value as its argument.\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    #[track_caller]\n    pub fn new(value: T) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: Arc::new(RwLock::new(value)),\n        }\n    }\n}\n\nimpl<T> ReadValue for ArcStoredValue<T>\nwhere\n    T: 'static,\n{\n    type Value = ReadGuard<T, Plain<T>>;\n\n    fn try_read_value(&self) -> Option<ReadGuard<T, Plain<T>>> {\n        Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)\n    }\n}\n\nimpl<T> WriteValue for ArcStoredValue<T>\nwhere\n    T: 'static,\n{\n    type Value = T;\n\n    fn try_write_value(&self) -> Option<UntrackedWriteGuard<T>> {\n        UntrackedWriteGuard::try_new(self.value.clone())\n    }\n}\n\nimpl<T> IsDisposed for ArcStoredValue<T> {\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T> IntoInner for ArcStoredValue<T> {\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        Some(Arc::into_inner(self.value)?.into_inner().unwrap())\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/owner/arena.rs",
    "content": "use or_poisoned::OrPoisoned;\nuse slotmap::{new_key_type, SlotMap};\n#[cfg(feature = \"sandboxed-arenas\")]\nuse std::cell::RefCell;\n#[cfg(not(feature = \"sandboxed-arenas\"))]\nuse std::sync::OnceLock;\n#[cfg(feature = \"sandboxed-arenas\")]\nuse std::sync::Weak;\nuse std::{\n    any::Any,\n    hash::Hash,\n    sync::{Arc, RwLock},\n};\n\nnew_key_type! {\n    /// Unique identifier for an item stored in the arena.\n    pub struct NodeId;\n}\n\npub struct Arena;\n\npub type ArenaMap = SlotMap<NodeId, Box<dyn Any + Send + Sync>>;\n\n#[cfg(not(feature = \"sandboxed-arenas\"))]\nstatic MAP: OnceLock<RwLock<ArenaMap>> = OnceLock::new();\n#[cfg(feature = \"sandboxed-arenas\")]\nthread_local! {\n    pub(crate) static MAP: RefCell<Option<Weak<RwLock<ArenaMap>>>> = RefCell::new(Some(Default::default()));\n}\n\nimpl Arena {\n    #[inline(always)]\n    #[allow(unused)]\n    pub fn set(arena: &Arc<RwLock<ArenaMap>>) {\n        #[cfg(feature = \"sandboxed-arenas\")]\n        {\n            let new_arena = Arc::downgrade(arena);\n            MAP.with_borrow_mut(|arena| {\n                *arena = Some(new_arena);\n            })\n        }\n    }\n\n    #[track_caller]\n    pub fn with<U>(fun: impl FnOnce(&ArenaMap) -> U) -> U {\n        #[cfg(not(feature = \"sandboxed-arenas\"))]\n        {\n            fun(&MAP.get_or_init(Default::default).read().or_poisoned())\n        }\n        #[cfg(feature = \"sandboxed-arenas\")]\n        {\n            Arena::try_with(fun).unwrap_or_else(|| {\n                panic!(\n                    \"at {}, the `sandboxed-arenas` feature is active, but no \\\n                     Arena is active\",\n                    std::panic::Location::caller()\n                )\n            })\n        }\n    }\n\n    #[track_caller]\n    pub fn try_with<U>(fun: impl FnOnce(&ArenaMap) -> U) -> Option<U> {\n        #[cfg(not(feature = \"sandboxed-arenas\"))]\n        {\n            Some(fun(&MAP.get_or_init(Default::default).read().or_poisoned()))\n        }\n        #[cfg(feature = \"sandboxed-arenas\")]\n        {\n            MAP.with_borrow(|arena| {\n                arena\n                    .as_ref()\n                    .and_then(Weak::upgrade)\n                    .map(|n| fun(&n.read().or_poisoned()))\n            })\n        }\n    }\n\n    #[track_caller]\n    pub fn with_mut<U>(fun: impl FnOnce(&mut ArenaMap) -> U) -> U {\n        #[cfg(not(feature = \"sandboxed-arenas\"))]\n        {\n            fun(&mut MAP.get_or_init(Default::default).write().or_poisoned())\n        }\n        #[cfg(feature = \"sandboxed-arenas\")]\n        {\n            Arena::try_with_mut(fun).unwrap_or_else(|| {\n                panic!(\n                    \"at {}, the `sandboxed-arenas` feature is active, but no \\\n                     Arena is active\",\n                    std::panic::Location::caller()\n                )\n            })\n        }\n    }\n\n    #[track_caller]\n    pub fn try_with_mut<U>(fun: impl FnOnce(&mut ArenaMap) -> U) -> Option<U> {\n        #[cfg(not(feature = \"sandboxed-arenas\"))]\n        {\n            Some(fun(&mut MAP\n                .get_or_init(Default::default)\n                .write()\n                .or_poisoned()))\n        }\n        #[cfg(feature = \"sandboxed-arenas\")]\n        {\n            MAP.with_borrow(|arena| {\n                arena\n                    .as_ref()\n                    .and_then(Weak::upgrade)\n                    .map(|n| fun(&mut n.write().or_poisoned()))\n            })\n        }\n    }\n}\n\n#[cfg(feature = \"sandboxed-arenas\")]\npub mod sandboxed {\n    use super::{Arena, ArenaMap, MAP};\n    use futures::Stream;\n    use pin_project_lite::pin_project;\n    use std::{\n        future::Future,\n        pin::Pin,\n        sync::{Arc, RwLock, Weak},\n        task::{Context, Poll},\n    };\n\n    pin_project! {\n        /// A [`Future`] that restores its associated arena as the current arena whenever it is\n        /// polled.\n        ///\n        /// Sandboxed arenas are used to ensure that data created in response to e.g., different\n        /// HTTP requests can be handled separately, while providing stable identifiers for their\n        /// stored values. Wrapping a `Future` in `Sandboxed` ensures that it will always use the\n        /// same arena that it was created under.\n        pub struct Sandboxed<T> {\n            arena: Option<Arc<RwLock<ArenaMap>>>,\n            #[pin]\n            inner: T,\n        }\n    }\n\n    impl<T> Sandboxed<T> {\n        /// Wraps the given [`Future`], ensuring that any [`ArenaItem`][item] created while it is\n        /// being polled will be associated with the same arena that was active when this was\n        /// called.\n        ///\n        /// [item]:[crate::owner::ArenaItem]\n        #[track_caller]\n        pub fn new(inner: T) -> Self {\n            let arena = MAP.with_borrow(|n| n.as_ref().and_then(Weak::upgrade));\n            Self { arena, inner }\n        }\n    }\n\n    impl<Fut> Future for Sandboxed<Fut>\n    where\n        Fut: Future,\n    {\n        type Output = Fut::Output;\n\n        fn poll(\n            self: Pin<&mut Self>,\n            cx: &mut Context<'_>,\n        ) -> Poll<Self::Output> {\n            if let Some(arena) = self.arena.as_ref() {\n                Arena::set(arena);\n            }\n            let this = self.project();\n            this.inner.poll(cx)\n        }\n    }\n\n    impl<T> Stream for Sandboxed<T>\n    where\n        T: Stream,\n    {\n        type Item = T::Item;\n\n        fn poll_next(\n            self: Pin<&mut Self>,\n            cx: &mut Context<'_>,\n        ) -> Poll<Option<Self::Item>> {\n            if let Some(arena) = self.arena.as_ref() {\n                Arena::set(arena);\n            }\n            let this = self.project();\n            this.inner.poll_next(cx)\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/owner/arena_item.rs",
    "content": "use super::{\n    arena::{Arena, NodeId},\n    LocalStorage, Storage, SyncStorage, OWNER,\n};\nuse crate::traits::{Dispose, IntoInner, IsDisposed};\nuse send_wrapper::SendWrapper;\nuse std::{any::Any, hash::Hash, marker::PhantomData};\n\n/// A copyable, stable reference for any value, stored on the arena whose ownership is managed by the\n/// reactive ownership tree.\n#[derive(Debug)]\npub struct ArenaItem<T, S = SyncStorage> {\n    node: NodeId,\n    #[allow(clippy::type_complexity)]\n    ty: PhantomData<fn() -> (SendWrapper<T>, S)>,\n}\n\nimpl<T, S> Copy for ArenaItem<T, S> {}\n\nimpl<T, S> Clone for ArenaItem<T, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> PartialEq for ArenaItem<T, S> {\n    fn eq(&self, other: &Self) -> bool {\n        self.node == other.node\n    }\n}\n\nimpl<T, S> Eq for ArenaItem<T, S> {}\n\nimpl<T, S> Hash for ArenaItem<T, S> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.node.hash(state);\n    }\n}\n\nimpl<T, S> ArenaItem<T, S>\nwhere\n    T: 'static,\n    S: Storage<T>,\n{\n    /// Stores the given value in the arena allocator.\n    #[track_caller]\n    pub fn new_with_storage(value: T) -> Self {\n        let node = {\n            Arena::with_mut(|arena| {\n                arena.insert(\n                    Box::new(S::wrap(value)) as Box<dyn Any + Send + Sync>\n                )\n            })\n        };\n        OWNER.with(|o| {\n            if let Some(owner) = o.borrow().as_ref().and_then(|o| o.upgrade()) {\n                owner.register(node);\n            }\n        });\n\n        Self {\n            node,\n            ty: PhantomData,\n        }\n    }\n}\n\nimpl<T, S> Default for ArenaItem<T, S>\nwhere\n    T: Default + 'static,\n    S: Storage<T>,\n{\n    #[track_caller] // Default trait is not annotated with #[track_caller]\n    fn default() -> Self {\n        Self::new_with_storage(Default::default())\n    }\n}\n\nimpl<T> ArenaItem<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    /// Stores the given value in the arena allocator.\n    #[track_caller]\n    pub fn new(value: T) -> Self {\n        ArenaItem::new_with_storage(value)\n    }\n}\n\nimpl<T> ArenaItem<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    /// Stores the given value in the arena allocator.\n    #[track_caller]\n    pub fn new_local(value: T) -> Self {\n        ArenaItem::new_with_storage(value)\n    }\n}\n\nimpl<T, S: Storage<T>> ArenaItem<T, S> {\n    /// Applies a function to a reference to the stored value and returns the result, or `None` if it has already been disposed.\n    #[track_caller]\n    pub fn try_with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> Option<U> {\n        S::try_with(self.node, fun)\n    }\n\n    /// Applies a function to a mutable reference to the stored value and returns the result, or `None` if it has already been disposed.\n    #[track_caller]\n    pub fn try_update_value<U>(\n        &self,\n        fun: impl FnOnce(&mut T) -> U,\n    ) -> Option<U> {\n        S::try_with_mut(self.node, fun)\n    }\n}\n\nimpl<T: Clone, S: Storage<T>> ArenaItem<T, S> {\n    /// Returns a clone of the stored value, or `None` if it has already been disposed.\n    #[track_caller]\n    pub fn try_get_value(&self) -> Option<T> {\n        S::try_with(self.node, Clone::clone)\n    }\n}\n\nimpl<T, S> IsDisposed for ArenaItem<T, S> {\n    fn is_disposed(&self) -> bool {\n        Arena::with(|arena| !arena.contains_key(self.node))\n    }\n}\n\nimpl<T, S> Dispose for ArenaItem<T, S> {\n    fn dispose(self) {\n        Arena::with_mut(|arena| arena.remove(self.node));\n    }\n}\n\nimpl<T, S: Storage<T>> IntoInner for ArenaItem<T, S> {\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        S::take(self.node)\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/owner/context.rs",
    "content": "use crate::owner::Owner;\nuse or_poisoned::OrPoisoned;\nuse std::{\n    any::{Any, TypeId},\n    collections::VecDeque,\n};\n\nimpl Owner {\n    fn provide_context<T: Send + Sync + 'static>(&self, value: T) {\n        self.inner\n            .write()\n            .or_poisoned()\n            .contexts\n            .insert(value.type_id(), Box::new(value));\n    }\n\n    fn use_context<T: Clone + 'static>(&self) -> Option<T> {\n        self.with_context(Clone::clone)\n    }\n\n    fn take_context<T: 'static>(&self) -> Option<T> {\n        let ty = TypeId::of::<T>();\n        let mut inner = self.inner.write().or_poisoned();\n        let contexts = &mut inner.contexts;\n        if let Some(context) = contexts.remove(&ty) {\n            context.downcast::<T>().ok().map(|n| *n)\n        } else {\n            let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());\n            while let Some(ref this_parent) = parent.clone() {\n                let mut this_parent = this_parent.write().or_poisoned();\n                let contexts = &mut this_parent.contexts;\n                let value = contexts.remove(&ty);\n                let downcast =\n                    value.and_then(|context| context.downcast::<T>().ok());\n                if let Some(value) = downcast {\n                    return Some(*value);\n                } else {\n                    parent =\n                        this_parent.parent.as_ref().and_then(|p| p.upgrade());\n                }\n            }\n            None\n        }\n    }\n\n    fn with_context<T: 'static, R>(\n        &self,\n        cb: impl FnOnce(&T) -> R,\n    ) -> Option<R> {\n        let ty = TypeId::of::<T>();\n        let inner = self.inner.read().or_poisoned();\n        let contexts = &inner.contexts;\n        let reference = if let Some(context) = contexts.get(&ty) {\n            context.downcast_ref::<T>()\n        } else {\n            let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());\n            while let Some(ref this_parent) = parent.clone() {\n                let this_parent = this_parent.read().or_poisoned();\n                let contexts = &this_parent.contexts;\n                let value = contexts.get(&ty);\n                let downcast =\n                    value.and_then(|context| context.downcast_ref::<T>());\n                if let Some(value) = downcast {\n                    return Some(cb(value));\n                } else {\n                    parent =\n                        this_parent.parent.as_ref().and_then(|p| p.upgrade());\n                }\n            }\n\n            None\n        };\n        reference.map(cb)\n    }\n\n    fn update_context<T: 'static, R>(\n        &self,\n        cb: impl FnOnce(&mut T) -> R,\n    ) -> Option<R> {\n        let ty = TypeId::of::<T>();\n        let mut inner = self.inner.write().or_poisoned();\n        let contexts = &mut inner.contexts;\n        let reference = if let Some(context) = contexts.get_mut(&ty) {\n            context.downcast_mut::<T>()\n        } else {\n            let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());\n            while let Some(ref this_parent) = parent.clone() {\n                let mut this_parent = this_parent.write().or_poisoned();\n                let contexts = &mut this_parent.contexts;\n                let value = contexts.get_mut(&ty);\n                let downcast =\n                    value.and_then(|context| context.downcast_mut::<T>());\n                if let Some(value) = downcast {\n                    return Some(cb(value));\n                } else {\n                    parent =\n                        this_parent.parent.as_ref().and_then(|p| p.upgrade());\n                }\n            }\n            None\n        };\n        reference.map(cb)\n    }\n\n    /// Searches for items stored in context in either direction, either among parents or among\n    /// descendants.\n    pub fn use_context_bidirectional<T: Clone + 'static>(&self) -> Option<T> {\n        self.use_context()\n            .unwrap_or_else(|| self.find_context_in_children())\n    }\n\n    fn find_context_in_children<T: Clone + 'static>(&self) -> Option<T> {\n        let ty = TypeId::of::<T>();\n        let inner = self.inner.read().or_poisoned();\n        let mut to_search = VecDeque::new();\n        to_search.extend(inner.children.clone());\n        drop(inner);\n\n        while let Some(next) = to_search.pop_front() {\n            if let Some(child) = next.upgrade() {\n                let child = child.read().or_poisoned();\n                let contexts = &child.contexts;\n                if let Some(context) = contexts.get(&ty) {\n                    return context.downcast_ref::<T>().cloned();\n                }\n\n                to_search.extend(child.children.clone());\n            }\n        }\n\n        None\n    }\n}\n\n/// Provides a context value of type `T` to the current reactive [`Owner`]\n/// and all of its descendants. This can be accessed using [`use_context`].\n///\n/// This is useful for passing values down to components or functions lower in a\n/// hierarchy without needs to “prop drill” by passing them through each layer as\n/// arguments to a function or properties of a component.\n///\n/// Context works similarly to variable scope: a context that is provided higher in\n/// the reactive graph can be used lower down, but a context that is provided lower\n/// down cannot be used higher up.\n///\n/// ```rust\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::owner::*;\n/// # let owner = Owner::new(); owner.set();\n/// # use reactive_graph::effect::Effect;\n/// # futures::executor::block_on(async move {\n/// # any_spawner::Executor::init_futures_executor();\n/// Effect::new(move |_| {\n///     println!(\"Provider\");\n///     provide_context(42i32); // provide an i32\n///\n///     Effect::new(move |_| {\n///         println!(\"intermediate node\");\n///\n///         Effect::new(move |_| {\n///             let value = use_context::<i32>()\n///                 .expect(\"could not find i32 in context\");\n///             assert_eq!(value, 42);\n///         });\n///     });\n/// });\n/// # });\n/// ```\n///\n/// ## Context Shadowing\n///\n/// Only a single value of any type can be provided via context. If you need to provide multiple\n/// values of the same type, wrap each one in a \"newtype\" struct wrapper so that each one is a\n/// distinct type.\n///\n/// Providing a second value of the same type \"lower\" in the ownership tree will shadow the value,\n/// just as a second `let` declaration with the same variable name will shadow that variable.\n///\n/// ```rust\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::owner::*;\n/// # let owner = Owner::new(); owner.set();\n/// # use reactive_graph::effect::Effect;\n/// # futures::executor::block_on(async move {\n/// # any_spawner::Executor::init_futures_executor();\n/// Effect::new(move |_| {\n///     println!(\"Provider\");\n///     provide_context(\"foo\"); // provide a &'static str\n///\n///     Effect::new(move |_| {\n///         // before we provide another value of the same type, we can access the old one\n///         assert_eq!(use_context::<&'static str>(), Some(\"foo\"));\n///         // but providing another value of the same type shadows it\n///         provide_context(\"bar\");\n///\n///         Effect::new(move |_| {\n///             assert_eq!(use_context::<&'static str>(), Some(\"bar\"));\n///         });\n///     });\n/// });\n/// # });\n/// ```\npub fn provide_context<T: Send + Sync + 'static>(value: T) {\n    if let Some(owner) = Owner::current() {\n        owner.provide_context(value);\n    }\n}\n\n/// Extracts a context value of type `T` from the reactive system.\n///\n/// This traverses the reactive ownership graph, beginning from the current reactive\n/// [`Owner`] and iterating through its parents, if any. When the value is found, it is cloned.\n///\n/// The context value should have been provided elsewhere using\n/// [`provide_context`](provide_context).\n///\n/// This is useful for passing values down to components or functions lower in a\n/// hierarchy without needs to “prop drill” by passing them through each layer as\n/// arguments to a function or properties of a component.\n///\n/// Context works similarly to variable scope: a context that is provided higher in\n/// the reactive graph can be used lower down, but a context that is provided lower\n/// in the tree cannot be used higher up.\n///\n/// While the term “consume” is sometimes used, note that [`use_context`] clones the value, rather\n/// than removing it; it is still accessible to other users.\n///\n/// ```rust\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::owner::*;\n/// # let owner = Owner::new(); owner.set();\n/// # use reactive_graph::effect::Effect;\n/// # futures::executor::block_on(async move {\n/// # any_spawner::Executor::init_futures_executor();\n/// Effect::new(move |_| {\n///     provide_context(String::from(\"foo\"));\n///\n///     Effect::new(move |_| {\n///         // each use_context clones the value\n///         let value = use_context::<String>()\n///             .expect(\"could not find String in context\");\n///         assert_eq!(value, \"foo\");\n///         let value2 = use_context::<String>()\n///             .expect(\"could not find String in context\");\n///         assert_eq!(value2, \"foo\");\n///     });\n/// });\n/// # });\n/// ```\npub fn use_context<T: Clone + 'static>() -> Option<T> {\n    Owner::current().and_then(|owner| owner.use_context())\n}\n\n/// Extracts a context value of type `T` from the reactive system, and\n/// panics if it can't be found.\n///\n/// This traverses the reactive ownership graph, beginning from the current reactive\n/// [`Owner`] and iterating through its parents, if any. When the value is found, it is cloned.\n///\n/// Panics if no value is found.\n///\n/// The context value should have been provided elsewhere using\n/// [`provide_context`](provide_context).\n///\n/// This is useful for passing values down to components or functions lower in a\n/// hierarchy without needs to “prop drill” by passing them through each layer as\n/// arguments to a function or properties of a component.\n///\n/// Context works similarly to variable scope: a context that is provided higher in\n/// the reactive graph can be used lower down, but a context that is provided lower\n/// in the tree cannot be used higher up.\n///\n/// While the term “consume” is sometimes used, note that [`use_context`] clones the value, rather\n/// than removing it; it is still accessible to other users.\n///\n/// ```rust\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::owner::*;\n/// # let owner = Owner::new(); owner.set();\n/// # use reactive_graph::effect::Effect;\n/// # futures::executor::block_on(async move {\n/// # any_spawner::Executor::init_futures_executor();\n/// Effect::new(move |_| {\n///     provide_context(String::from(\"foo\"));\n///\n///     Effect::new(move |_| {\n///         // each use_context clones the value\n///         let value = use_context::<String>()\n///             .expect(\"could not find String in context\");\n///         assert_eq!(value, \"foo\");\n///         let value2 = use_context::<String>()\n///             .expect(\"could not find String in context\");\n///         assert_eq!(value2, \"foo\");\n///     });\n/// });\n/// # });\n/// ```\n/// ## Panics\n/// Panics if a context of this type is not found in the current reactive\n/// owner or its ancestors.\n#[track_caller]\npub fn expect_context<T: Clone + 'static>() -> T {\n    let location = std::panic::Location::caller();\n\n    use_context().unwrap_or_else(|| {\n        panic!(\n            \"{:?} expected context of type {:?} to be present\",\n            location,\n            std::any::type_name::<T>()\n        )\n    })\n}\n\n/// Extracts a context value of type `T` from the reactive system, and takes ownership,\n/// removing it from the context system.\n///\n/// This traverses the reactive ownership graph, beginning from the current reactive\n/// [`Owner`] and iterating through its parents, if any. When the value is found, it is removed,\n/// and is not available to any other [`use_context`] or [`take_context`] calls.\n///\n/// If the value is `Clone`, use [`use_context`] instead.\n///\n/// The context value should have been provided elsewhere using\n/// [`provide_context`](provide_context).\n///\n/// This is useful for passing values down to components or functions lower in a\n/// hierarchy without needs to “prop drill” by passing them through each layer as\n/// arguments to a function or properties of a component.\n///\n/// Context works similarly to variable scope: a context that is provided higher in\n/// the reactive graph can be used lower down, but a context that is provided lower\n/// in the tree cannot be used higher up.\n/// ```rust\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::owner::*;\n/// # let owner = Owner::new(); owner.set();\n/// # use reactive_graph::effect::Effect;\n/// # futures::executor::block_on(async move {\n/// # any_spawner::Executor::init_futures_executor();\n///\n/// #[derive(Debug, PartialEq)]\n/// struct NotClone(String);\n///\n/// Effect::new(move |_| {\n///     provide_context(NotClone(String::from(\"foo\")));\n///\n///     Effect::new(move |_| {\n///         // take_context removes the value from context without needing to clone\n///         let value = take_context::<NotClone>();\n///         assert_eq!(value, Some(NotClone(String::from(\"foo\"))));\n///         let value2 = take_context::<NotClone>();\n///         assert_eq!(value2, None);\n///     });\n/// });\n/// # });\n/// ```\npub fn take_context<T: 'static>() -> Option<T> {\n    Owner::current().and_then(|owner| owner.take_context())\n}\n\n/// Access a reference to a context value of type `T` in the reactive system.\n///\n/// This traverses the reactive ownership graph, beginning from the current reactive\n/// [`Owner`] and iterating through its parents, if any. When the value is found,\n/// the function that you pass is applied to an immutable reference to it.\n///\n/// The context value should have been provided elsewhere using\n/// [`provide_context`](provide_context).\n///\n/// This is useful for passing values down to components or functions lower in a\n/// hierarchy without needs to “prop drill” by passing them through each layer as\n/// arguments to a function or properties of a component.\n///\n/// Context works similarly to variable scope: a context that is provided higher in\n/// the reactive graph can be used lower down, but a context that is provided lower\n/// in the tree cannot be used higher up.\n///\n/// ```rust\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::owner::*;\n/// # let owner = Owner::new(); owner.set();\n/// # use reactive_graph::effect::Effect;\n/// # futures::executor::block_on(async move {\n/// # any_spawner::Executor::init_futures_executor();\n/// Effect::new(move |_| {\n///     provide_context(String::from(\"foo\"));\n///\n///     Effect::new(move |_| {\n///         let value = with_context::<String, _>(|val| val.to_string())\n///             .expect(\"could not find String in context\");\n///         assert_eq!(value, \"foo\");\n///     });\n/// });\n/// # });\n/// ```\npub fn with_context<T: 'static, R>(cb: impl FnOnce(&T) -> R) -> Option<R> {\n    Owner::current().and_then(|owner| owner.with_context(cb))\n}\n\n/// Update a context value of type `T` in the reactive system.\n///\n/// This traverses the reactive ownership graph, beginning from the current reactive\n/// [`Owner`] and iterating through its parents, if any. When the value is found,\n/// the function that you pass is applied to a mutable reference to it.\n///\n/// The context value should have been provided elsewhere using\n/// [`provide_context`](provide_context).\n///\n/// This is useful for passing values down to components or functions lower in a\n/// hierarchy without needs to “prop drill” by passing them through each layer as\n/// arguments to a function or properties of a component.\n///\n/// Context works similarly to variable scope: a context that is provided higher in\n/// the reactive graph can be used lower down, but a context that is provided lower\n/// in the tree cannot be used higher up.\n///\n/// ```rust\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::owner::*;\n/// # let owner = Owner::new(); owner.set();\n/// # use reactive_graph::effect::Effect;\n/// # futures::executor::block_on(async move {\n/// # any_spawner::Executor::init_futures_executor();\n/// Effect::new(move |_| {\n///     provide_context(String::from(\"foo\"));\n///\n///     Effect::new(move |_| {\n///         let value = update_context::<String, _>(|val| {\n///             std::mem::replace(val, \"bar\".to_string())\n///         })\n///         .expect(\"could not find String in context\");\n///         assert_eq!(value, \"foo\");\n///         assert_eq!(expect_context::<String>(), \"bar\");\n///     });\n/// });\n/// # });\n/// ```\npub fn update_context<T: 'static, R>(\n    cb: impl FnOnce(&mut T) -> R,\n) -> Option<R> {\n    Owner::current().and_then(|owner| owner.update_context(cb))\n}\n"
  },
  {
    "path": "reactive_graph/src/owner/storage.rs",
    "content": "use super::arena::{Arena, NodeId};\nuse send_wrapper::SendWrapper;\n\n/// A trait for borrowing and taking data.\npub trait StorageAccess<T> {\n    /// Borrows the value.\n    fn as_borrowed(&self) -> &T;\n\n    /// Takes the value.\n    fn into_taken(self) -> T;\n}\n\nimpl<T> StorageAccess<T> for T {\n    fn as_borrowed(&self) -> &T {\n        self\n    }\n\n    fn into_taken(self) -> T {\n        self\n    }\n}\n\nimpl<T> StorageAccess<T> for SendWrapper<T> {\n    fn as_borrowed(&self) -> &T {\n        self\n    }\n\n    fn into_taken(self) -> T {\n        self.take()\n    }\n}\n\n/// A way of storing an [`ArenaItem`](super::arena_item::ArenaItem), either as itself or with a wrapper to make it threadsafe.\n///\n/// This exists because all items stored in the arena must be `Send + Sync`, but in single-threaded\n/// environments you might want or need to use thread-unsafe types.\npub trait Storage<T>: Send + Sync + 'static {\n    /// The type being stored, once it has been wrapped.\n    type Wrapped: StorageAccess<T> + Send + Sync + 'static;\n\n    /// Adds any needed wrapper to the type.\n    fn wrap(value: T) -> Self::Wrapped;\n\n    /// Applies the given function to the stored value, if it exists and can be accessed from this\n    /// thread.\n    fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U>;\n\n    /// Applies the given function to a mutable reference to the stored value, if it exists and can be accessed from this\n    /// thread.\n    fn try_with_mut<U>(\n        node: NodeId,\n        fun: impl FnOnce(&mut T) -> U,\n    ) -> Option<U>;\n\n    /// Sets a new value for the stored value. If it has been disposed, returns `Some(T)`.\n    fn try_set(node: NodeId, value: T) -> Option<T>;\n\n    /// Takes an item from the arena if it exists and can be accessed from this thread.\n    /// If it cannot be casted, it will still be removed from the arena.\n    fn take(node: NodeId) -> Option<T>;\n}\n\n/// A form of [`Storage`] that stores the type as itself, with no wrapper.\n#[derive(Debug, Copy, Clone)]\npub struct SyncStorage;\n\nimpl<T> Storage<T> for SyncStorage\nwhere\n    T: Send + Sync + 'static,\n{\n    type Wrapped = T;\n\n    #[inline(always)]\n    fn wrap(value: T) -> Self::Wrapped {\n        value\n    }\n\n    fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U> {\n        Arena::try_with(|arena| {\n            let m = arena.get(node);\n            m.and_then(|n| n.downcast_ref::<T>()).map(fun)\n        })\n        .flatten()\n    }\n\n    fn try_with_mut<U>(\n        node: NodeId,\n        fun: impl FnOnce(&mut T) -> U,\n    ) -> Option<U> {\n        Arena::try_with_mut(|arena| {\n            let m = arena.get_mut(node);\n            m.and_then(|n| n.downcast_mut::<T>()).map(fun)\n        })\n        .flatten()\n    }\n\n    fn try_set(node: NodeId, value: T) -> Option<T> {\n        Arena::try_with_mut(|arena| {\n            let m = arena.get_mut(node);\n            match m.and_then(|n| n.downcast_mut::<T>()) {\n                Some(inner) => {\n                    *inner = value;\n                    None\n                }\n                None => Some(value),\n            }\n        })\n        .flatten()\n    }\n\n    fn take(node: NodeId) -> Option<T> {\n        Arena::with_mut(|arena| {\n            let m = arena.remove(node)?;\n            match m.downcast::<T>() {\n                Ok(inner) => Some(*inner),\n                Err(_) => None,\n            }\n        })\n    }\n}\n\n/// A form of [`Storage`] that stores the type with a wrapper that makes it `Send + Sync`, but only\n/// allows it to be accessed from the thread on which it was created.\n#[derive(Debug, Copy, Clone)]\npub struct LocalStorage;\n\nimpl<T> Storage<T> for LocalStorage\nwhere\n    T: 'static,\n{\n    type Wrapped = SendWrapper<T>;\n\n    fn wrap(value: T) -> Self::Wrapped {\n        SendWrapper::new(value)\n    }\n\n    fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U> {\n        Arena::with(|arena| {\n            let m = arena.get(node);\n            m.and_then(|n| n.downcast_ref::<SendWrapper<T>>())\n                .map(|inner| fun(inner))\n        })\n    }\n\n    fn try_with_mut<U>(\n        node: NodeId,\n        fun: impl FnOnce(&mut T) -> U,\n    ) -> Option<U> {\n        Arena::with_mut(|arena| {\n            let m = arena.get_mut(node);\n            m.and_then(|n| n.downcast_mut::<SendWrapper<T>>())\n                .map(|inner| fun(&mut *inner))\n        })\n    }\n\n    fn try_set(node: NodeId, value: T) -> Option<T> {\n        Arena::with_mut(|arena| {\n            let m = arena.get_mut(node);\n            match m.and_then(|n| n.downcast_mut::<SendWrapper<T>>()) {\n                Some(inner) => {\n                    *inner = SendWrapper::new(value);\n                    None\n                }\n                None => Some(value),\n            }\n        })\n    }\n\n    fn take(node: NodeId) -> Option<T> {\n        Arena::with_mut(|arena| {\n            let m = arena.remove(node)?;\n            match m.downcast::<SendWrapper<T>>() {\n                Ok(inner) => Some(inner.take()),\n                Err(_) => None,\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/owner/stored_value.rs",
    "content": "use super::{\n    arc_stored_value::ArcStoredValue, ArenaItem, LocalStorage, Storage,\n    SyncStorage,\n};\nuse crate::{\n    signal::guards::{Plain, ReadGuard, UntrackedWriteGuard},\n    traits::{\n        DefinedAt, Dispose, IntoInner, IsDisposed, ReadValue, WriteValue,\n    },\n    unwrap_signal,\n};\nuse std::{\n    fmt::{Debug, Formatter},\n    hash::Hash,\n    panic::Location,\n};\n\n/// A **non-reactive**, `Copy` handle for any value.\n///\n/// This allows you to create a stable reference for any value by storing it within\n/// the reactive system. Like the signal types (e.g., [`ReadSignal`](crate::signal::ReadSignal)\n/// and [`RwSignal`](crate::signal::RwSignal)), it is `Copy` and `'static`. Unlike the signal\n/// types, it is not reactive; accessing it does not cause effects to subscribe, and\n/// updating it does not notify anything else.\npub struct StoredValue<T, S = SyncStorage> {\n    value: ArenaItem<ArcStoredValue<T>, S>,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<T, S> Copy for StoredValue<T, S> {}\n\nimpl<T, S> Clone for StoredValue<T, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> Debug for StoredValue<T, S>\nwhere\n    S: Debug,\n{\n    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"StoredValue\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"value\", &self.value)\n            .finish()\n    }\n}\n\nimpl<T, S> PartialEq for StoredValue<T, S> {\n    fn eq(&self, other: &Self) -> bool {\n        self.value == other.value\n    }\n}\n\nimpl<T, S> Eq for StoredValue<T, S> {}\n\nimpl<T, S> Hash for StoredValue<T, S> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.value.hash(state);\n    }\n}\n\nimpl<T, S> DefinedAt for StoredValue<T, S> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T, S> StoredValue<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStoredValue<T>>,\n{\n    /// Stores the given value in the arena allocator.\n    #[track_caller]\n    pub fn new_with_storage(value: T) -> Self {\n        Self {\n            value: ArenaItem::new_with_storage(ArcStoredValue::new(value)),\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n        }\n    }\n}\n\nimpl<T, S> Default for StoredValue<T, S>\nwhere\n    T: Default + 'static,\n    S: Storage<ArcStoredValue<T>>,\n{\n    #[track_caller] // Default trait is not annotated with #[track_caller]\n    fn default() -> Self {\n        Self::new_with_storage(Default::default())\n    }\n}\n\nimpl<T> StoredValue<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    /// Stores the given value in the arena allocator.\n    #[track_caller]\n    pub fn new(value: T) -> Self {\n        StoredValue::new_with_storage(value)\n    }\n}\n\nimpl<T> StoredValue<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    /// Stores the given value in the arena allocator.\n    #[track_caller]\n    pub fn new_local(value: T) -> Self {\n        StoredValue::new_with_storage(value)\n    }\n}\n\nimpl<T, S> ReadValue for StoredValue<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStoredValue<T>>,\n{\n    type Value = ReadGuard<T, Plain<T>>;\n\n    fn try_read_value(&self) -> Option<ReadGuard<T, Plain<T>>> {\n        self.value\n            .try_get_value()\n            .and_then(|inner| inner.try_read_value())\n    }\n}\n\nimpl<T, S> WriteValue for StoredValue<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStoredValue<T>>,\n{\n    type Value = T;\n\n    fn try_write_value(&self) -> Option<UntrackedWriteGuard<T>> {\n        self.value\n            .try_get_value()\n            .and_then(|inner| inner.try_write_value())\n    }\n}\n\nimpl<T, S> IsDisposed for StoredValue<T, S> {\n    fn is_disposed(&self) -> bool {\n        self.value.is_disposed()\n    }\n}\n\nimpl<T, S> Dispose for StoredValue<T, S> {\n    fn dispose(self) {\n        self.value.dispose();\n    }\n}\n\nimpl<T, S> IntoInner for StoredValue<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStoredValue<T>>,\n{\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        self.value.into_inner()?.into_inner()\n    }\n}\n\nimpl<T> From<ArcStoredValue<T>> for StoredValue<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcStoredValue<T>) -> Self {\n        StoredValue {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: ArenaItem::new(value),\n        }\n    }\n}\n\nimpl<T, S> From<StoredValue<T, S>> for ArcStoredValue<T>\nwhere\n    S: Storage<ArcStoredValue<T>>,\n{\n    #[track_caller]\n    fn from(value: StoredValue<T, S>) -> Self {\n        value\n            .value\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(value))\n    }\n}\n\n/// Creates a new [`StoredValue`].\n#[inline(always)]\n#[track_caller]\n#[deprecated(\n    since = \"0.7.0-beta5\",\n    note = \"This function is being removed to conform to Rust idioms. Please \\\n            use `StoredValue::new()` or `StoredValue::new_local()` instead.\"\n)]\npub fn store_value<T>(value: T) -> StoredValue<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    StoredValue::new(value)\n}\n\n/// Converts some value into a locally-stored type, using [`LocalStorage`].\n///\n/// This is modeled on [`From`] but special-cased for this thread-local storage method, which\n/// allows for better type inference for the default case.\npub trait FromLocal<T> {\n    /// Converts between the types.\n    fn from_local(value: T) -> Self;\n}\n"
  },
  {
    "path": "reactive_graph/src/owner.rs",
    "content": "//! The reactive ownership model, which manages effect cancellation, cleanups, and arena allocation.\n\n#[cfg(feature = \"hydration\")]\nuse hydration_context::SharedContext;\nuse or_poisoned::OrPoisoned;\nuse rustc_hash::FxHashMap;\nuse std::{\n    any::{Any, TypeId},\n    cell::RefCell,\n    fmt::Debug,\n    mem,\n    sync::{Arc, RwLock, Weak},\n};\n\nmod arc_stored_value;\nmod arena;\nmod arena_item;\nmod context;\nmod storage;\nmod stored_value;\nuse self::arena::Arena;\npub use arc_stored_value::ArcStoredValue;\n#[cfg(feature = \"sandboxed-arenas\")]\npub use arena::sandboxed::Sandboxed;\n#[cfg(feature = \"sandboxed-arenas\")]\nuse arena::ArenaMap;\nuse arena::NodeId;\npub use arena_item::*;\npub use context::*;\npub use storage::*;\n#[allow(deprecated)] // allow exporting deprecated fn\npub use stored_value::{store_value, FromLocal, StoredValue};\n\n/// A reactive owner, which manages\n/// 1) the cancellation of [`Effect`](crate::effect::Effect)s,\n/// 2) providing and accessing environment data via [`provide_context`] and [`use_context`],\n/// 3) running cleanup functions defined via [`Owner::on_cleanup`], and\n/// 4) an arena storage system to provide `Copy` handles via [`ArenaItem`], which is what allows\n///    types like [`RwSignal`](crate::signal::RwSignal), [`Memo`](crate::computed::Memo), and so on to be `Copy`.\n///\n/// Every effect and computed reactive value has an associated `Owner`. While it is running, this\n/// is marked as the current `Owner`. Whenever it re-runs, this `Owner` is cleared by calling\n/// [`Owner::with_cleanup`]. This runs cleanup functions, cancels any [`Effect`](crate::effect::Effect)s created during the\n/// last run, drops signals stored in the arena, and so on, because those effects and signals will\n/// be re-created as needed during the next run.\n///\n/// When the owner is ultimately dropped, it will clean up its owned resources in the same way.\n///\n/// The \"current owner\" is set on the thread-local basis: whenever one of these reactive nodes is\n/// running, it will set the current owner on its thread with [`Owner::with`] or [`Owner::set`],\n/// allowing other reactive nodes implicitly to access the fact that it is currently the owner.\n///\n/// For a longer discussion of the ownership model, [see\n/// here](https://book.leptos.dev/appendix_life_cycle.html).\n#[derive(Debug, Clone, Default)]\n#[must_use]\npub struct Owner {\n    pub(crate) inner: Arc<RwLock<OwnerInner>>,\n    #[cfg(feature = \"hydration\")]\n    pub(crate) shared_context: Option<Arc<dyn SharedContext + Send + Sync>>,\n}\n\nimpl Owner {\n    /// Creates a [`WeakOwner`] reference to this owner that does not prevent cleanup.\n    pub fn downgrade(&self) -> WeakOwner {\n        WeakOwner {\n            inner: Arc::downgrade(&self.inner),\n            #[cfg(feature = \"hydration\")]\n            shared_context: self.shared_context.as_ref().map(Arc::downgrade),\n        }\n    }\n}\n\n/// A weak reference to an [`Owner`] that does not prevent cleanup.\n///\n/// This is useful for capturing an owner reference without creating reference\n/// cycles that would prevent the owner from being dropped and cleaned up.\n#[derive(Clone)]\npub struct WeakOwner {\n    inner: Weak<RwLock<OwnerInner>>,\n    #[cfg(feature = \"hydration\")]\n    shared_context: Option<Weak<dyn SharedContext + Send + Sync>>,\n}\n\nimpl WeakOwner {\n    /// Attempts to upgrade this weak reference to a strong [`Owner`].\n    ///\n    /// Returns `None` if the owner has already been dropped.\n    pub fn upgrade(&self) -> Option<Owner> {\n        self.inner.upgrade().map(|inner| {\n            #[cfg(feature = \"hydration\")]\n            let shared_context =\n                self.shared_context.as_ref().and_then(|sc| sc.upgrade());\n            Owner {\n                inner,\n                #[cfg(feature = \"hydration\")]\n                shared_context,\n            }\n        })\n    }\n}\n\nimpl PartialEq for Owner {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.inner, &other.inner)\n    }\n}\n\nthread_local! {\n    static OWNER: RefCell<Option<WeakOwner>> = Default::default();\n}\n\nimpl Owner {\n    /// Returns a unique identifier for this owner, which can be used to identify it for debugging\n    /// purposes.\n    ///\n    /// Intended for debugging only; this is not guaranteed to be stable between runs.\n    pub fn debug_id(&self) -> usize {\n        Arc::as_ptr(&self.inner) as usize\n    }\n\n    /// Returns the list of parents, grandparents, and ancestors, with values corresponding to\n    /// [`Owner::debug_id`] for each.\n    ///\n    /// Intended for debugging only; this is not guaranteed to be stable between runs.\n    pub fn ancestry(&self) -> Vec<usize> {\n        let mut ancestors = Vec::new();\n        let mut curr_parent = self\n            .inner\n            .read()\n            .or_poisoned()\n            .parent\n            .as_ref()\n            .and_then(|n| n.upgrade());\n        while let Some(parent) = curr_parent {\n            ancestors.push(Arc::as_ptr(&parent) as usize);\n            curr_parent = parent\n                .read()\n                .or_poisoned()\n                .parent\n                .as_ref()\n                .and_then(|n| n.upgrade());\n        }\n        ancestors\n    }\n\n    /// Creates a new `Owner` and registers it as a child of the current `Owner`, if there is one.\n    pub fn new() -> Self {\n        #[cfg(not(feature = \"hydration\"))]\n        let parent = OWNER.with(|o| {\n            o.borrow()\n                .as_ref()\n                .and_then(|o| o.upgrade())\n                .map(|o| Arc::downgrade(&o.inner))\n        });\n        #[cfg(feature = \"hydration\")]\n        let (parent, shared_context) = OWNER\n            .with(|o| {\n                o.borrow().as_ref().and_then(|o| o.upgrade()).map(|o| {\n                    (Some(Arc::downgrade(&o.inner)), o.shared_context.clone())\n                })\n            })\n            .unwrap_or((None, None));\n        let this = Self {\n            inner: Arc::new(RwLock::new(OwnerInner {\n                parent: parent.clone(),\n                nodes: Default::default(),\n                contexts: Default::default(),\n                cleanups: Default::default(),\n                children: Default::default(),\n                #[cfg(feature = \"sandboxed-arenas\")]\n                arena: parent\n                    .as_ref()\n                    .and_then(|parent| parent.upgrade())\n                    .map(|parent| parent.read().or_poisoned().arena.clone())\n                    .unwrap_or_default(),\n                paused: false,\n            })),\n            #[cfg(feature = \"hydration\")]\n            shared_context,\n        };\n        if let Some(parent) = parent.and_then(|n| n.upgrade()) {\n            parent\n                .write()\n                .or_poisoned()\n                .children\n                .push(Arc::downgrade(&this.inner));\n        }\n        this\n    }\n\n    /// Creates a new \"root\" context with the given [`SharedContext`], which allows sharing data\n    /// between the server and client.\n    ///\n    /// Only one `SharedContext` needs to be created per request, and will be automatically shared\n    /// by any other `Owner`s created under this one.\n    #[cfg(feature = \"hydration\")]\n    #[track_caller]\n    pub fn new_root(\n        shared_context: Option<Arc<dyn SharedContext + Send + Sync>>,\n    ) -> Self {\n        let this = Self {\n            inner: Arc::new(RwLock::new(OwnerInner {\n                parent: None,\n                nodes: Default::default(),\n                contexts: Default::default(),\n                cleanups: Default::default(),\n                children: Default::default(),\n                #[cfg(feature = \"sandboxed-arenas\")]\n                arena: Default::default(),\n                paused: false,\n            })),\n            #[cfg(feature = \"hydration\")]\n            shared_context,\n        };\n        this.set();\n        this\n    }\n\n    /// Returns the parent of this `Owner`, if any.\n    ///\n    /// None when:\n    /// - This is a root owner\n    /// - The parent has been dropped\n    pub fn parent(&self) -> Option<Owner> {\n        self.inner\n            .read()\n            .or_poisoned()\n            .parent\n            .as_ref()\n            .and_then(|p| p.upgrade())\n            .map(|inner| Owner {\n                inner,\n                #[cfg(feature = \"hydration\")]\n                shared_context: self.shared_context.clone(),\n            })\n    }\n\n    /// Creates a new `Owner` that is the child of the current `Owner`, if any.\n    pub fn child(&self) -> Self {\n        let parent = Some(Arc::downgrade(&self.inner));\n        let mut inner = self.inner.write().or_poisoned();\n        #[cfg(feature = \"sandboxed-arenas\")]\n        let arena = inner.arena.clone();\n        let paused = inner.paused;\n        let child = Self {\n            inner: Arc::new(RwLock::new(OwnerInner {\n                parent,\n                nodes: Default::default(),\n                contexts: Default::default(),\n                cleanups: Default::default(),\n                children: Default::default(),\n                #[cfg(feature = \"sandboxed-arenas\")]\n                arena,\n                paused,\n            })),\n            #[cfg(feature = \"hydration\")]\n            shared_context: self.shared_context.clone(),\n        };\n        inner.children.push(Arc::downgrade(&child.inner));\n        child\n    }\n\n    /// Sets this as the current `Owner`.\n    pub fn set(&self) {\n        OWNER.with_borrow_mut(|owner| *owner = Some(self.downgrade()));\n        #[cfg(feature = \"sandboxed-arenas\")]\n        Arena::set(&self.inner.read().or_poisoned().arena);\n    }\n\n    /// Runs the given function with this as the current `Owner`.\n    pub fn with<T>(&self, fun: impl FnOnce() -> T) -> T {\n        // codegen optimisation:\n        fn inner_1(self_: &Owner) -> Option<WeakOwner> {\n            let prev = OWNER.with_borrow_mut(|o| o.replace(self_.downgrade()));\n            #[cfg(feature = \"sandboxed-arenas\")]\n            Arena::set(&self_.inner.read().or_poisoned().arena);\n            prev\n        }\n        let prev = inner_1(self);\n\n        let val = fun();\n\n        // monomorphisation optimisation:\n        fn inner_2(prev: Option<WeakOwner>) {\n            OWNER.with_borrow_mut(|o| *o = prev);\n        }\n        inner_2(prev);\n\n        val\n    }\n\n    /// Cleans up this owner, the given function with this as the current `Owner`.\n    pub fn with_cleanup<T>(&self, fun: impl FnOnce() -> T) -> T {\n        self.cleanup();\n        self.with(fun)\n    }\n\n    /// Cleans up this owner in the following order:\n    /// 1) Runs `cleanup` on all children,\n    /// 2) Runs all cleanup functions registered with [`Owner::on_cleanup`],\n    /// 3) Drops the values of any arena-allocated [`ArenaItem`]s.\n    pub fn cleanup(&self) {\n        self.inner.cleanup();\n    }\n\n    /// Registers a function to be run the next time the current owner is cleaned up.\n    ///\n    /// Because the ownership model is associated with reactive nodes, each \"decision point\" in an\n    /// application tends to have a separate `Owner`: as a result, these cleanup functions often\n    /// fill the same need as an \"on unmount\" function in other UI approaches, etc.\n    pub fn on_cleanup(fun: impl FnOnce() + Send + Sync + 'static) {\n        if let Some(owner) = Owner::current() {\n            let mut inner = owner.inner.write().or_poisoned();\n\n            #[cfg(feature = \"sandboxed-arenas\")]\n            let fun = {\n                let arena = Arc::clone(&inner.arena);\n                move || {\n                    Arena::set(&arena);\n                    fun()\n                }\n            };\n\n            inner.cleanups.push(Box::new(fun));\n        }\n    }\n\n    fn register(&self, node: NodeId) {\n        self.inner.write().or_poisoned().nodes.push(node);\n    }\n\n    /// Returns the current `Owner`, if any.\n    pub fn current() -> Option<Owner> {\n        OWNER.with(|o| o.borrow().as_ref().and_then(|n| n.upgrade()))\n    }\n\n    /// Returns the [`SharedContext`] associated with this owner, if any.\n    #[cfg(feature = \"hydration\")]\n    pub fn shared_context(\n        &self,\n    ) -> Option<Arc<dyn SharedContext + Send + Sync>> {\n        self.shared_context.clone()\n    }\n\n    /// Removes this from its state as the thread-local owner and drops it.\n    /// If there are other holders of this owner, it may not cleanup, if always cleaning up is required,\n    /// see [`Owner::unset_with_forced_cleanup`].\n    pub fn unset(self) {\n        OWNER.with_borrow_mut(|owner| {\n            if owner.as_ref().and_then(|n| n.upgrade()) == Some(self) {\n                mem::take(owner);\n            }\n        })\n    }\n\n    /// Removes this from its state as the thread-local owner and drops it.\n    /// Unlike [`Owner::unset`], this will always run cleanup on this owner,\n    /// even if there are other holders of this owner.\n    pub fn unset_with_forced_cleanup(self) {\n        OWNER.with_borrow_mut(|owner| {\n            if owner\n                .as_ref()\n                .and_then(|n| n.upgrade())\n                .map(|o| o == self)\n                .unwrap_or(false)\n            {\n                mem::take(owner);\n            }\n        });\n        self.cleanup();\n    }\n\n    /// Returns the current [`SharedContext`], if any.\n    #[cfg(feature = \"hydration\")]\n    pub fn current_shared_context(\n    ) -> Option<Arc<dyn SharedContext + Send + Sync>> {\n        OWNER.with(|o| {\n            o.borrow()\n                .as_ref()\n                .and_then(|o| o.upgrade())\n                .and_then(|current| current.shared_context.clone())\n        })\n    }\n\n    /// Runs the given function, after indicating that the current [`SharedContext`] should be\n    /// prepared to handle any data created in the function.\n    #[cfg(feature = \"hydration\")]\n    pub fn with_hydration<T>(fun: impl FnOnce() -> T + 'static) -> T {\n        fn inner<T>(fun: Box<dyn FnOnce() -> T>) -> T {\n            provide_context(IsHydrating(true));\n\n            let sc = OWNER.with_borrow(|o| {\n                o.as_ref()\n                    .and_then(|o| o.upgrade())\n                    .and_then(|current| current.shared_context.clone())\n            });\n            match sc {\n                None => fun(),\n                Some(sc) => {\n                    let prev = sc.get_is_hydrating();\n                    sc.set_is_hydrating(true);\n                    let value = fun();\n                    sc.set_is_hydrating(prev);\n                    value\n                }\n            }\n        }\n\n        inner(Box::new(fun))\n    }\n\n    /// Runs the given function, after indicating that the current [`SharedContext`] should /// not handle data created in this function.\n    #[cfg(feature = \"hydration\")]\n    pub fn with_no_hydration<T>(fun: impl FnOnce() -> T + 'static) -> T {\n        fn inner<T>(fun: Box<dyn FnOnce() -> T>) -> T {\n            provide_context(IsHydrating(false));\n\n            let sc = OWNER.with_borrow(|o| {\n                o.as_ref()\n                    .and_then(|o| o.upgrade())\n                    .and_then(|current| current.shared_context.clone())\n            });\n            match sc {\n                None => fun(),\n                Some(sc) => {\n                    let prev = sc.get_is_hydrating();\n                    sc.set_is_hydrating(false);\n                    let value = fun();\n                    sc.set_is_hydrating(prev);\n                    value\n                }\n            }\n        }\n\n        inner(Box::new(fun))\n    }\n\n    /// Pauses the execution of side effects for this owner, and any of its descendants.\n    ///\n    /// If this owner is the owner for an [`Effect`](crate::effect::Effect) or [`RenderEffect`](crate::effect::RenderEffect), this effect will not run until [`Owner::resume`] is called. All children of this effects are also paused.\n    ///\n    /// Any notifications will be ignored; effects that are notified will paused will not run when\n    /// resumed, until they are notified again by a source after being resumed.\n    pub fn pause(&self) {\n        let mut stack = Vec::with_capacity(16);\n        stack.push(Arc::downgrade(&self.inner));\n        while let Some(curr) = stack.pop() {\n            if let Some(curr) = curr.upgrade() {\n                let mut curr = curr.write().or_poisoned();\n                curr.paused = true;\n                stack.extend(curr.children.iter().map(Weak::clone));\n            }\n        }\n    }\n\n    /// Whether this owner has been paused by [`Owner::pause`].\n    pub fn paused(&self) -> bool {\n        self.inner.read().or_poisoned().paused\n    }\n\n    /// Resumes side effects that have been paused by [`Owner::pause`].\n    ///\n    /// All children will also be resumed.\n    ///\n    /// This will *not* cause side effects that were notified while paused to run, until they are\n    /// notified again by a source after being resumed.\n    pub fn resume(&self) {\n        let mut stack = Vec::with_capacity(16);\n        stack.push(Arc::downgrade(&self.inner));\n        while let Some(curr) = stack.pop() {\n            if let Some(curr) = curr.upgrade() {\n                let mut curr = curr.write().or_poisoned();\n                curr.paused = false;\n                stack.extend(curr.children.iter().map(Weak::clone));\n            }\n        }\n    }\n}\n\n#[doc(hidden)]\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct IsHydrating(pub bool);\n\n/// Registers a function to be run the next time the current owner is cleaned up.\n///\n/// Because the ownership model is associated with reactive nodes, each \"decision point\" in an\n/// application tends to have a separate `Owner`: as a result, these cleanup functions often\n/// fill the same need as an \"on unmount\" function in other UI approaches, etc.\n///\n/// This is an alias for [`Owner::on_cleanup`].\npub fn on_cleanup(fun: impl FnOnce() + Send + Sync + 'static) {\n    Owner::on_cleanup(fun)\n}\n\n#[derive(Default)]\npub(crate) struct OwnerInner {\n    pub parent: Option<Weak<RwLock<OwnerInner>>>,\n    nodes: Vec<NodeId>,\n    pub contexts: FxHashMap<TypeId, Box<dyn Any + Send + Sync>>,\n    pub cleanups: Vec<Box<dyn FnOnce() + Send + Sync>>,\n    pub children: Vec<Weak<RwLock<OwnerInner>>>,\n    #[cfg(feature = \"sandboxed-arenas\")]\n    arena: Arc<RwLock<ArenaMap>>,\n    paused: bool,\n}\n\nimpl Debug for OwnerInner {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"OwnerInner\")\n            .field(\"parent\", &self.parent)\n            .field(\"nodes\", &self.nodes)\n            .field(\"contexts\", &self.contexts)\n            .field(\"cleanups\", &self.cleanups.len())\n            .finish()\n    }\n}\n\nimpl Drop for OwnerInner {\n    fn drop(&mut self) {\n        for child in std::mem::take(&mut self.children) {\n            if let Some(child) = child.upgrade() {\n                child.cleanup();\n            }\n        }\n\n        for cleanup in mem::take(&mut self.cleanups) {\n            cleanup();\n        }\n\n        let nodes = mem::take(&mut self.nodes);\n        if !nodes.is_empty() {\n            #[cfg(not(feature = \"sandboxed-arenas\"))]\n            Arena::with_mut(|arena| {\n                for node in nodes {\n                    _ = arena.remove(node);\n                }\n            });\n            #[cfg(feature = \"sandboxed-arenas\")]\n            {\n                let mut arena = self.arena.write().or_poisoned();\n                for node in nodes {\n                    _ = arena.remove(node);\n                }\n            }\n        }\n    }\n}\n\ntrait Cleanup {\n    fn cleanup(&self);\n}\n\nimpl Cleanup for RwLock<OwnerInner> {\n    fn cleanup(&self) {\n        let (cleanups, nodes, children) = {\n            let mut lock = self.write().or_poisoned();\n            (\n                mem::take(&mut lock.cleanups),\n                mem::take(&mut lock.nodes),\n                mem::take(&mut lock.children),\n            )\n        };\n        for child in children {\n            if let Some(child) = child.upgrade() {\n                child.cleanup();\n            }\n        }\n        for cleanup in cleanups {\n            cleanup();\n        }\n\n        if !nodes.is_empty() {\n            #[cfg(not(feature = \"sandboxed-arenas\"))]\n            Arena::with_mut(|arena| {\n                for node in nodes {\n                    _ = arena.remove(node);\n                }\n            });\n            #[cfg(feature = \"sandboxed-arenas\")]\n            {\n                let arena = self.read().or_poisoned().arena.clone();\n                let mut arena = arena.write().or_poisoned();\n                for node in nodes {\n                    _ = arena.remove(node);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/send_wrapper_ext.rs",
    "content": "//! Additional wrapper utilities for [`send_wrapper::SendWrapper`].\n\nuse send_wrapper::SendWrapper;\nuse std::{\n    fmt::{Debug, Formatter},\n    hash,\n    ops::{Deref, DerefMut},\n};\n/// An optional value that can always be sent between threads, even if its inner value\n/// in the `Some(_)` case would not be threadsafe.\n///\n/// This struct can be dereferenced to `Option<T>`.\n///\n/// If it has been given a local (`!Send`) value, that value is wrapped in a [`SendWrapper`], which\n/// allows sending it between threads but will panic if it is accessed or updated from a  \n/// thread other than the one on which it was created.\n///\n/// If it is created with `None` for a local (`!Send`) type, no `SendWrapper` is created until a\n/// value is provided via [`DerefMut`] or [`update`](SendOption::update).\n///\n/// ### Use Case\n/// This is useful for cases like browser-only types, which are `!Send` but cannot be constructed\n/// on the server anyway, and are only created in a single-threaded browser environment. The local\n/// `SendOption` can be created with its `None` variant and sent between threads without causing issues\n/// when it is dropped.\n///\n/// ### Panics\n/// Dereferencing or dropping `SendOption` panics under the following conditions:\n/// 1) It is created via [`new_local`](SendOption::new_local) (signifying a `!Send` inner type),\n/// 2) It has `Some(_)` value, and\n/// 3) It has been sent to a thread other than the one on which it was created.\npub struct SendOption<T> {\n    inner: Inner<T>,\n}\n\n// SAFETY: `SendOption` can *only* be given a T in four ways\n// 1) via new(), which requires T: Send + Sync\n// 2) via new_local(), which wraps T in a SendWrapper if given Some(T)\n// 3) via deref_mut(), which creates a SendWrapper<Option<T>> as needed\n// 4) via update(), which either dereferences an existing SendWrapper\n//    or creates a new SendWrapper as needed\nunsafe impl<T> Send for SendOption<T> {}\nunsafe impl<T> Sync for SendOption<T> {}\n\nimpl<T> PartialEq for SendOption<T>\nwhere\n    T: PartialEq,\n{\n    fn eq(&self, other: &Self) -> bool {\n        self.deref() == other.deref()\n    }\n}\n\nimpl<T> Eq for SendOption<T> where T: Eq {}\n\nimpl<T> PartialOrd for SendOption<T>\nwhere\n    T: PartialOrd,\n{\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        self.deref().partial_cmp(other.deref())\n    }\n}\n\nimpl<T> hash::Hash for SendOption<T>\nwhere\n    T: hash::Hash,\n{\n    fn hash<H: hash::Hasher>(&self, state: &mut H) {\n        self.deref().hash(state);\n    }\n}\n\nenum Inner<T> {\n    /// A threadsafe value.\n    Threadsafe(Option<T>),\n    /// A non-threadsafe value. If accessed/dropped from a different thread in the Some() variant, it will panic.\n    Local(Option<SendWrapper<Option<T>>>),\n}\n\nimpl<T> SendOption<T>\nwhere\n    T: Send + Sync,\n{\n    /// Create a new threadsafe value.\n    pub fn new(value: Option<T>) -> Self {\n        Self {\n            inner: Inner::Threadsafe(value),\n        }\n    }\n}\n\nimpl<T> From<Option<T>> for SendOption<T>\nwhere\n    T: Send + Sync,\n{\n    fn from(value: Option<T>) -> Self {\n        Self::new(value)\n    }\n}\n\nimpl<T> SendOption<T> {\n    /// Create a new non-threadsafe value.\n    pub fn new_local(value: Option<T>) -> Self {\n        Self {\n            inner: if let Some(value) = value {\n                Inner::Local(Some(SendWrapper::new(Some(value))))\n            } else {\n                Inner::Local(None)\n            },\n        }\n    }\n\n    /// Update a value in place with a callback.\n    ///\n    /// # Panics\n    /// If the value is [`Inner::Local`] and it is called from a different thread than the one the instance has been created with, it will panic.\n    pub fn update(&mut self, cb: impl FnOnce(&mut Option<T>)) {\n        match &mut self.inner {\n            Inner::Threadsafe(value) => cb(value),\n            Inner::Local(value) => match value {\n                Some(sw) => {\n                    cb(sw.deref_mut());\n                    if sw.is_none() {\n                        *value = None;\n                    }\n                }\n                None => {\n                    let mut inner = None;\n                    cb(&mut inner);\n                    if let Some(inner) = inner {\n                        *value = Some(SendWrapper::new(Some(inner)));\n                    }\n                }\n            },\n        }\n    }\n\n    /// Consume the value.\n    ///\n    /// # Panics\n    /// Panics if the [`Inner::Local`] variant and it is called from a different thread than the one the instance has been created with.\n    pub fn take(self) -> Option<T> {\n        match self.inner {\n            Inner::Threadsafe(value) => value,\n            Inner::Local(value) => value.and_then(|value| value.take()),\n        }\n    }\n}\n\nimpl<T> Deref for SendOption<T> {\n    type Target = Option<T>;\n\n    fn deref(&self) -> &Self::Target {\n        match &self.inner {\n            Inner::Threadsafe(value) => value,\n            Inner::Local(value) => match value {\n                Some(value) => value.deref(),\n                None => &None,\n            },\n        }\n    }\n}\n\nimpl<T> DerefMut for SendOption<T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        match &mut self.inner {\n            Inner::Threadsafe(value) => value,\n            Inner::Local(value) => match value {\n                Some(value) => value.deref_mut(),\n                None => {\n                    *value = Some(SendWrapper::new(None));\n                    value.as_mut().unwrap().deref_mut()\n                }\n            },\n        }\n    }\n}\n\nimpl<T: Debug> Debug for SendOption<T> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        match &self.inner {\n            Inner::Threadsafe(value) => {\n                write!(f, \"SendOption::Threadsafe({value:?})\")\n            }\n            Inner::Local(value) => {\n                write!(f, \"SendOption::Local({value:?})\")\n            }\n        }\n    }\n}\n\nimpl<T: Clone> Clone for SendOption<T> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: match &self.inner {\n                Inner::Threadsafe(value) => Inner::Threadsafe(value.clone()),\n                Inner::Local(value) => Inner::Local(value.clone()),\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/serde.rs",
    "content": "#[allow(deprecated)]\nuse crate::wrappers::read::{MaybeProp, MaybeSignal};\nuse crate::{\n    computed::{ArcMemo, Memo},\n    owner::Storage,\n    signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},\n    traits::With,\n    wrappers::read::{Signal, SignalTypes},\n};\nuse serde::{Deserialize, Serialize};\n\nimpl<T, St> Serialize for ReadSignal<T, St>\nwhere\n    T: Serialize + 'static,\n    St: Storage<ArcReadSignal<T>>,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.with(|value| value.serialize(serializer))\n    }\n}\n\nimpl<T, St> Serialize for RwSignal<T, St>\nwhere\n    T: Serialize + 'static,\n    St: Storage<ArcRwSignal<T>>,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.with(|value| value.serialize(serializer))\n    }\n}\n\nimpl<T, St> Serialize for Memo<T, St>\nwhere\n    T: Serialize + 'static,\n    St: Storage<ArcMemo<T, St>> + Storage<T>,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.with(|value| value.serialize(serializer))\n    }\n}\n\nimpl<T: Serialize + 'static> Serialize for ArcReadSignal<T> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.with(|value| value.serialize(serializer))\n    }\n}\n\nimpl<T: Serialize + 'static> Serialize for ArcRwSignal<T> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.with(|value| value.serialize(serializer))\n    }\n}\n\nimpl<T: Serialize + 'static, St: Storage<T>> Serialize for ArcMemo<T, St> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.with(|value| value.serialize(serializer))\n    }\n}\n\n#[allow(deprecated)]\nimpl<T, St> Serialize for MaybeSignal<T, St>\nwhere\n    T: Clone + Send + Sync + Serialize,\n    St: Storage<SignalTypes<T, St>> + Storage<T>,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.with(|value| value.serialize(serializer))\n    }\n}\n\nimpl<T, St> Serialize for MaybeProp<T, St>\nwhere\n    T: Send + Sync + Serialize,\n    St: Storage<SignalTypes<Option<T>, St>> + Storage<Option<T>>,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        match &self.0 {\n            None => None::<T>.serialize(serializer),\n            Some(signal) => signal.with(|value| value.serialize(serializer)),\n        }\n    }\n}\n\nimpl<T, St> Serialize for Signal<T, St>\nwhere\n    T: Send + Sync + Serialize + 'static,\n    St: Storage<SignalTypes<T, St>> + Storage<T>,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.with(|value| value.serialize(serializer))\n    }\n}\n\n/* Deserialization for signal types */\n\nimpl<'de, T, S> Deserialize<'de> for RwSignal<T, S>\nwhere\n    T: Send + Sync + Deserialize<'de> + 'static,\n    S: Storage<ArcRwSignal<T>>,\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        T::deserialize(deserializer).map(RwSignal::new_with_storage)\n    }\n}\n\nimpl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcRwSignal<T> {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        T::deserialize(deserializer).map(ArcRwSignal::new)\n    }\n}\n\n#[allow(deprecated)]\nimpl<'de, T: Deserialize<'de>, St> Deserialize<'de> for MaybeSignal<T, St>\nwhere\n    St: Storage<T>,\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        T::deserialize(deserializer).map(MaybeSignal::Static)\n    }\n}\n\n#[allow(deprecated)]\nimpl<'de, T: Deserialize<'de>> Deserialize<'de> for Signal<T>\nwhere\n    T: Send + Sync + Serialize + 'static,\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        T::deserialize(deserializer).map(Signal::stored)\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/arc_read.rs",
    "content": "use super::{\n    guards::{Plain, ReadGuard},\n    subscriber_traits::AsSubscriberSet,\n};\nuse crate::{\n    graph::SubscriberSet,\n    traits::{DefinedAt, IntoInner, IsDisposed, ReadUntracked},\n};\nuse core::fmt::{Debug, Formatter, Result};\nuse std::{\n    hash::Hash,\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\n/// A reference-counted getter for a reactive signal.\n///\n/// A signal is a piece of data that may change over time,\n/// and notifies other code when it has changed.\n///\n/// This is a reference-counted signal, which is `Clone` but not `Copy`.\n/// For arena-allocated `Copy` signals, use [`ReadSignal`](super::ReadSignal).\n///\n/// ## Core Trait Implementations\n/// - [`.get()`](crate::traits::Get) clones the current value of the signal.\n///   If you call it within an effect, it will cause that effect to subscribe\n///   to the signal, and to re-run whenever the value of the signal changes.\n///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of\n///     the signal without reactively tracking it.\n/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the\n///   value of the signal by reference. If you call it within an effect, it will\n///   cause that effect to subscribe to the signal, and to re-run whenever the\n///   value of the signal changes.\n///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the\n///     current value of the signal without reactively tracking it.\n/// - [`.with()`](crate::traits::With) allows you to reactively access the signal’s\n///   value without cloning by applying a callback function.\n///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access\n///     the signal’s value by applying a callback function without reactively\n///     tracking it.\n/// - [`.to_stream()`](crate::traits::ToStream) converts the signal to an `async`\n///   stream of values.\n/// - [`::from_stream()`](crate::traits::FromStream) converts an `async` stream\n///   of values into a signal containing the latest value.\n///\n/// ## Examples\n/// ```\n/// # use reactive_graph::prelude::*; use reactive_graph::signal::*;\n/// let (count, set_count) = arc_signal(0);\n///\n/// // calling .get() clones and returns the value\n/// assert_eq!(count.get(), 0);\n/// // calling .read() accesses the value by reference\n/// assert_eq!(count.read(), 0);\n/// ```\npub struct ArcReadSignal<T> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    pub(crate) value: Arc<RwLock<T>>,\n    pub(crate) inner: Arc<RwLock<SubscriberSet>>,\n}\n\nimpl<T> Clone for ArcReadSignal<T> {\n    #[track_caller]\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            value: Arc::clone(&self.value),\n            inner: Arc::clone(&self.inner),\n        }\n    }\n}\n\nimpl<T> Debug for ArcReadSignal<T> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        f.debug_struct(\"ArcReadSignal\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"value\", &Arc::as_ptr(&self.value))\n            .finish()\n    }\n}\n\nimpl<T: Default> Default for ArcReadSignal<T> {\n    #[track_caller]\n    fn default() -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: Arc::new(RwLock::new(T::default())),\n            inner: Arc::new(RwLock::new(SubscriberSet::new())),\n        }\n    }\n}\n\nimpl<T> PartialEq for ArcReadSignal<T> {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.value, &other.value)\n    }\n}\n\nimpl<T> Eq for ArcReadSignal<T> {}\n\nimpl<T> Hash for ArcReadSignal<T> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        std::ptr::hash(&Arc::as_ptr(&self.value), state);\n    }\n}\n\nimpl<T> DefinedAt for ArcReadSignal<T> {\n    #[inline(always)]\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> IsDisposed for ArcReadSignal<T> {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T> IntoInner for ArcReadSignal<T> {\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        Some(Arc::into_inner(self.value)?.into_inner().unwrap())\n    }\n}\n\nimpl<T> AsSubscriberSet for ArcReadSignal<T> {\n    type Output = Arc<RwLock<SubscriberSet>>;\n\n    #[inline(always)]\n    fn as_subscriber_set(&self) -> Option<Self::Output> {\n        Some(Arc::clone(&self.inner))\n    }\n}\n\nimpl<T: 'static> ReadUntracked for ArcReadSignal<T> {\n    type Value = ReadGuard<T, Plain<T>>;\n\n    #[track_caller]\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/arc_rw.rs",
    "content": "use super::{\n    guards::{Plain, ReadGuard, UntrackedWriteGuard, WriteGuard},\n    subscriber_traits::AsSubscriberSet,\n    ArcReadSignal, ArcWriteSignal,\n};\nuse crate::{\n    graph::{ReactiveNode, SubscriberSet},\n    prelude::{IsDisposed, Notify},\n    traits::{DefinedAt, IntoInner, ReadUntracked, UntrackableGuard, Write},\n};\nuse core::fmt::{Debug, Formatter, Result};\nuse std::{\n    hash::Hash,\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\n/// A reference-counted signal that can be read from or written to.\n///\n/// A signal is a piece of data that may change over time, and notifies other\n/// code when it has changed. This is the atomic unit of reactivity, which begins all other\n/// processes of reactive updates.\n///\n/// This is a reference-counted signal, which is `Clone` but not `Copy`.\n/// For arena-allocated `Copy` signals, use [`RwSignal`](super::RwSignal).\n///\n/// ## Core Trait Implementations\n///\n/// ### Reading the Value\n/// - [`.get()`](crate::traits::Get) clones the current value of the signal.\n///   If you call it within an effect, it will cause that effect to subscribe\n///   to the signal, and to re-run whenever the value of the signal changes.\n///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of\n///     the signal without reactively tracking it.\n/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the\n///   value of the signal by reference. If you call it within an effect, it will\n///   cause that effect to subscribe to the signal, and to re-run whenever the\n///   value of the signal changes.\n///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the\n///     current value of the signal without reactively tracking it.\n/// - [`.with()`](crate::traits::With) allows you to reactively access the signal’s\n///   value without cloning by applying a callback function.\n///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access\n///     the signal’s value by applying a callback function without reactively\n///     tracking it.\n/// - [`.to_stream()`](crate::traits::ToStream) converts the signal to an `async`\n///   stream of values.\n///\n/// ### Updating the Value\n/// - [`.set()`](crate::traits::Set) sets the signal to a new value.\n/// - [`.update()`](crate::traits::Update) updates the value of the signal by\n///   applying a closure that takes a mutable reference.\n/// - [`.write()`](crate::traits::Write) returns a guard through which the signal\n///   can be mutated, and which notifies subscribers when it is dropped.\n///\n/// > Each of these has a related `_untracked()` method, which updates the signal\n/// > without notifying subscribers. Untracked updates are not desirable in most\n/// > cases, as they cause “tearing” between the signal’s value and its observed\n/// > value. If you want a non-reactive container, used [`ArenaItem`](crate::owner::ArenaItem)\n/// > instead.\n///\n/// ## Examples\n///\n/// ```\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let count = ArcRwSignal::new(0);\n///\n/// // ✅ calling the getter clones and returns the value\n/// //    this can be `count()` on nightly\n/// assert_eq!(count.get(), 0);\n///\n/// // ✅ calling the setter sets the value\n/// //    this can be `set_count(1)` on nightly\n/// count.set(1);\n/// assert_eq!(count.get(), 1);\n///\n/// // ❌ you could call the getter within the setter\n/// // set_count.set(count.get() + 1);\n///\n/// // ✅ however it's more efficient to use .update() and mutate the value in place\n/// count.update(|count: &mut i32| *count += 1);\n/// assert_eq!(count.get(), 2);\n///\n/// // ✅ you can create \"derived signals\" with a Fn() -> T interface\n/// let double_count = {\n///   // clone before moving into the closure because we use it below\n///   let count = count.clone();\n///   move || count.get() * 2\n/// };\n/// count.set(0);\n/// assert_eq!(double_count(), 0);\n/// count.set(1);\n/// assert_eq!(double_count(), 2);\n/// ```\npub struct ArcRwSignal<T> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    pub(crate) value: Arc<RwLock<T>>,\n    pub(crate) inner: Arc<RwLock<SubscriberSet>>,\n}\n\nimpl<T> Clone for ArcRwSignal<T> {\n    #[track_caller]\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            value: Arc::clone(&self.value),\n            inner: Arc::clone(&self.inner),\n        }\n    }\n}\n\nimpl<T> Debug for ArcRwSignal<T> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        f.debug_struct(\"ArcRwSignal\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"value\", &Arc::as_ptr(&self.value))\n            .finish()\n    }\n}\n\nimpl<T> PartialEq for ArcRwSignal<T> {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.value, &other.value)\n    }\n}\n\nimpl<T> Eq for ArcRwSignal<T> {}\n\nimpl<T> Hash for ArcRwSignal<T> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        std::ptr::hash(&Arc::as_ptr(&self.value), state);\n    }\n}\n\nimpl<T> Default for ArcRwSignal<T>\nwhere\n    T: Default,\n{\n    #[track_caller]\n    fn default() -> Self {\n        Self::new(T::default())\n    }\n}\n\nimpl<T> ArcRwSignal<T> {\n    /// Creates a new signal, taking the initial value as its argument.\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    #[track_caller]\n    pub fn new(value: T) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: Arc::new(RwLock::new(value)),\n            inner: Arc::new(RwLock::new(SubscriberSet::new())),\n        }\n    }\n\n    /// Returns a read-only handle to the signal.\n    #[track_caller]\n    pub fn read_only(&self) -> ArcReadSignal<T> {\n        ArcReadSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: Arc::clone(&self.value),\n            inner: Arc::clone(&self.inner),\n        }\n    }\n\n    /// Returns a write-only handle to the signal.\n    #[track_caller]\n    pub fn write_only(&self) -> ArcWriteSignal<T> {\n        ArcWriteSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: Arc::clone(&self.value),\n            inner: Arc::clone(&self.inner),\n        }\n    }\n\n    /// Splits the signal into its readable and writable halves.\n    #[track_caller]\n    pub fn split(&self) -> (ArcReadSignal<T>, ArcWriteSignal<T>) {\n        (self.read_only(), self.write_only())\n    }\n\n    /// Reunites the two halves of a signal. Returns `None` if the two signals\n    /// provided were not created from the same signal.\n    #[track_caller]\n    pub fn unite(\n        read: ArcReadSignal<T>,\n        write: ArcWriteSignal<T>,\n    ) -> Option<Self> {\n        if Arc::ptr_eq(&read.inner, &write.inner) {\n            Some(Self {\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: Location::caller(),\n                value: read.value,\n                inner: read.inner,\n            })\n        } else {\n            None\n        }\n    }\n}\n\nimpl<T> DefinedAt for ArcRwSignal<T> {\n    #[inline(always)]\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> IsDisposed for ArcRwSignal<T> {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T> IntoInner for ArcRwSignal<T> {\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        Some(Arc::into_inner(self.value)?.into_inner().unwrap())\n    }\n}\n\nimpl<T> AsSubscriberSet for ArcRwSignal<T> {\n    type Output = Arc<RwLock<SubscriberSet>>;\n\n    #[inline(always)]\n    fn as_subscriber_set(&self) -> Option<Self::Output> {\n        Some(Arc::clone(&self.inner))\n    }\n}\n\nimpl<T: 'static> ReadUntracked for ArcRwSignal<T> {\n    type Value = ReadGuard<T, Plain<T>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)\n    }\n}\n\nimpl<T> Notify for ArcRwSignal<T> {\n    fn notify(&self) {\n        self.mark_dirty();\n    }\n}\n\nimpl<T: 'static> Write for ArcRwSignal<T> {\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.value\n            .write()\n            .ok()\n            .map(|guard| WriteGuard::new(self.clone(), guard))\n    }\n\n    #[allow(refining_impl_trait)]\n    fn try_write_untracked(&self) -> Option<UntrackedWriteGuard<Self::Value>> {\n        UntrackedWriteGuard::try_new(Arc::clone(&self.value))\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/arc_trigger.rs",
    "content": "use super::subscriber_traits::AsSubscriberSet;\nuse crate::{\n    graph::{ReactiveNode, SubscriberSet},\n    traits::{DefinedAt, IsDisposed, Notify, Track},\n};\nuse std::{\n    fmt::{Debug, Formatter, Result},\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\n/// A trigger is a data-less signal with the sole purpose of notifying other reactive code of a change.\n///\n/// This can be useful for when using external data not stored in signals, for example.\npub struct ArcTrigger {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    pub(crate) inner: Arc<RwLock<SubscriberSet>>,\n}\n\nimpl ArcTrigger {\n    /// Creates a new trigger.\n    #[track_caller]\n    pub fn new() -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: Default::default(),\n        }\n    }\n}\n\nimpl Default for ArcTrigger {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Clone for ArcTrigger {\n    #[track_caller]\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            inner: Arc::clone(&self.inner),\n        }\n    }\n}\n\nimpl Debug for ArcTrigger {\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        f.debug_struct(\"ArcTrigger\").finish()\n    }\n}\n\nimpl IsDisposed for ArcTrigger {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl AsSubscriberSet for ArcTrigger {\n    type Output = Arc<RwLock<SubscriberSet>>;\n\n    #[inline(always)]\n    fn as_subscriber_set(&self) -> Option<Self::Output> {\n        Some(Arc::clone(&self.inner))\n    }\n}\n\nimpl Notify for Vec<ArcTrigger> {\n    fn notify(&self) {\n        for trigger in self {\n            trigger.notify();\n        }\n    }\n}\n\nimpl Track for Vec<ArcTrigger> {\n    fn track(&self) {\n        for trigger in self {\n            trigger.track();\n        }\n    }\n}\n\nimpl DefinedAt for ArcTrigger {\n    #[inline(always)]\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl Notify for ArcTrigger {\n    fn notify(&self) {\n        self.inner.mark_dirty();\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/arc_write.rs",
    "content": "use super::guards::{UntrackedWriteGuard, WriteGuard};\nuse crate::{\n    graph::{ReactiveNode, SubscriberSet},\n    prelude::{IsDisposed, Notify},\n    traits::{DefinedAt, IntoInner, UntrackableGuard, Write},\n};\nuse core::fmt::{Debug, Formatter, Result};\nuse std::{\n    hash::Hash,\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\n/// A reference-counted setter for a reactive signal.\n///\n/// A signal is a piece of data that may change over time,\n/// and notifies other code when it has changed.\n///\n/// This is a reference-counted signal, which is `Clone` but not `Copy`.\n/// For arena-allocated `Copy` signals, use [`WriteSignal`](super::WriteSignal).\n///\n/// ## Core Trait Implementations\n/// - [`.set()`](crate::traits::Set) sets the signal to a new value.\n/// - [`.update()`](crate::traits::Update) updates the value of the signal by\n///   applying a closure that takes a mutable reference.\n/// - [`.write()`](crate::traits::Write) returns a guard through which the signal\n///   can be mutated, and which notifies subscribers when it is dropped.\n///\n/// > Each of these has a related `_untracked()` method, which updates the signal\n/// > without notifying subscribers. Untracked updates are not desirable in most\n/// > cases, as they cause “tearing” between the signal’s value and its observed\n/// > value. If you want a non-reactive container, used [`ArenaItem`](crate::owner::ArenaItem)\n/// > instead.\n///\n/// ## Examples\n/// ```\n/// # use reactive_graph::prelude::*; use reactive_graph::signal::*;\n/// let (count, set_count) = arc_signal(0);\n///\n/// // ✅ calling the setter sets the value\n/// //    `set_count(1)` on nightly\n/// set_count.set(1);\n/// assert_eq!(count.get(), 1);\n///\n/// // ❌ you could call the getter within the setter\n/// // set_count.set(count.get() + 1);\n///\n/// // ✅ however it's more efficient to use .update() and mutate the value in place\n/// set_count.update(|count: &mut i32| *count += 1);\n/// assert_eq!(count.get(), 2);\n///\n/// // ✅ `.write()` returns a guard that implements `DerefMut` and will notify when dropped\n/// *set_count.write() += 1;\n/// assert_eq!(count.get(), 3);\n/// ```\npub struct ArcWriteSignal<T> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    pub(crate) value: Arc<RwLock<T>>,\n    pub(crate) inner: Arc<RwLock<SubscriberSet>>,\n}\n\nimpl<T> Clone for ArcWriteSignal<T> {\n    #[track_caller]\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            value: Arc::clone(&self.value),\n            inner: Arc::clone(&self.inner),\n        }\n    }\n}\n\nimpl<T> Debug for ArcWriteSignal<T> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        f.debug_struct(\"ArcWriteSignal\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"value\", &Arc::as_ptr(&self.value))\n            .finish()\n    }\n}\n\nimpl<T> PartialEq for ArcWriteSignal<T> {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.value, &other.value)\n    }\n}\n\nimpl<T> Eq for ArcWriteSignal<T> {}\n\nimpl<T> Hash for ArcWriteSignal<T> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        std::ptr::hash(&Arc::as_ptr(&self.value), state);\n    }\n}\n\nimpl<T> DefinedAt for ArcWriteSignal<T> {\n    #[inline(always)]\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> IsDisposed for ArcWriteSignal<T> {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T> IntoInner for ArcWriteSignal<T> {\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        Some(Arc::into_inner(self.value)?.into_inner().unwrap())\n    }\n}\n\nimpl<T> Notify for ArcWriteSignal<T> {\n    fn notify(&self) {\n        self.inner.mark_dirty();\n    }\n}\n\nimpl<T: 'static> Write for ArcWriteSignal<T> {\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.value\n            .write()\n            .ok()\n            .map(|guard| WriteGuard::new(self.clone(), guard))\n    }\n\n    #[allow(refining_impl_trait)]\n    fn try_write_untracked(&self) -> Option<UntrackedWriteGuard<Self::Value>> {\n        UntrackedWriteGuard::try_new(Arc::clone(&self.value))\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/guards.rs",
    "content": "//! Guards that integrate with the reactive system, wrapping references to the values of signals.\n\nuse crate::{\n    computed::BlockingLock,\n    traits::{Notify, UntrackableGuard},\n};\nuse core::fmt::Debug;\nuse guardian::{ArcRwLockReadGuardian, ArcRwLockWriteGuardian};\nuse std::{\n    borrow::Borrow,\n    fmt::Display,\n    marker::PhantomData,\n    ops::{Deref, DerefMut},\n    sync::{Arc, RwLock},\n};\n\n/// A wrapper type for any kind of guard returned by [`Read`](crate::traits::Read).\n///\n/// If `Inner` implements `Deref`, so does `ReadGuard<_, Inner>`.\n#[derive(Debug)]\npub struct ReadGuard<T, Inner> {\n    ty: PhantomData<T>,\n    inner: Inner,\n}\n\nimpl<T, Inner> ReadGuard<T, Inner> {\n    /// Creates a new wrapper around another guard type.\n    pub fn new(inner: Inner) -> Self {\n        Self {\n            inner,\n            ty: PhantomData,\n        }\n    }\n\n    /// Returns the inner guard type.\n    pub fn into_inner(self) -> Inner {\n        self.inner\n    }\n}\n\nimpl<T, Inner> Clone for ReadGuard<T, Inner>\nwhere\n    Inner: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            ty: self.ty,\n            inner: self.inner.clone(),\n        }\n    }\n}\n\nimpl<T, Inner> Deref for ReadGuard<T, Inner>\nwhere\n    Inner: Deref<Target = T>,\n{\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.deref()\n    }\n}\n\nimpl<T, Inner> Borrow<T> for ReadGuard<T, Inner>\nwhere\n    Inner: Deref<Target = T>,\n{\n    fn borrow(&self) -> &T {\n        self.deref()\n    }\n}\n\nimpl<T, Inner> PartialEq<T> for ReadGuard<T, Inner>\nwhere\n    Inner: Deref<Target = T>,\n    T: PartialEq,\n{\n    fn eq(&self, other: &Inner::Target) -> bool {\n        self.deref() == other\n    }\n}\n\nimpl<T, Inner> Display for ReadGuard<T, Inner>\nwhere\n    Inner: Deref<Target = T>,\n    T: Display,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\n/// A guard that provides access to a signal's inner value.\npub struct Plain<T: 'static> {\n    guard: ArcRwLockReadGuardian<T>,\n}\n\nimpl<T: 'static> Debug for Plain<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Plain\").finish()\n    }\n}\n\nimpl<T: 'static> Plain<T> {\n    /// Takes a reference-counted read guard on the given lock.\n    pub fn try_new(inner: Arc<RwLock<T>>) -> Option<Self> {\n        ArcRwLockReadGuardian::try_take(inner)?\n            .ok()\n            .map(|guard| Plain { guard })\n    }\n}\n\nimpl<T> Deref for Plain<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.guard.deref()\n    }\n}\n\nimpl<T: PartialEq> PartialEq for Plain<T> {\n    fn eq(&self, other: &Self) -> bool {\n        **self == **other\n    }\n}\n\nimpl<T: PartialEq> PartialEq<T> for Plain<T> {\n    fn eq(&self, other: &T) -> bool {\n        **self == *other\n    }\n}\n\nimpl<T: Display> Display for Plain<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\n/// A guard that provides access to an async signal's value.\npub struct AsyncPlain<T: 'static> {\n    pub(crate) guard: async_lock::RwLockReadGuardArc<T>,\n}\n\nimpl<T: 'static> Debug for AsyncPlain<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AsyncPlain\").finish()\n    }\n}\n\nimpl<T: 'static> AsyncPlain<T> {\n    /// Takes a reference-counted async read guard on the given lock.\n    pub fn try_new(inner: &Arc<async_lock::RwLock<T>>) -> Option<Self> {\n        Some(Self {\n            guard: inner.blocking_read_arc(),\n        })\n    }\n}\n\nimpl<T> Deref for AsyncPlain<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.guard.deref()\n    }\n}\n\nimpl<T: PartialEq> PartialEq for AsyncPlain<T> {\n    fn eq(&self, other: &Self) -> bool {\n        **self == **other\n    }\n}\n\nimpl<T: PartialEq> PartialEq<T> for AsyncPlain<T> {\n    fn eq(&self, other: &T) -> bool {\n        **self == *other\n    }\n}\n\nimpl<T: Display> Display for AsyncPlain<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\n/// A guard that maps over another guard.\n#[derive(Debug)]\npub struct Mapped<Inner, U>\nwhere\n    Inner: Deref,\n{\n    inner: Inner,\n    map_fn: fn(&Inner::Target) -> &U,\n}\n\nimpl<T: 'static, U> Mapped<Plain<T>, U> {\n    /// Creates a mapped read guard from the inner lock.\n    pub fn try_new(\n        inner: Arc<RwLock<T>>,\n        map_fn: fn(&T) -> &U,\n    ) -> Option<Self> {\n        let inner = Plain::try_new(inner)?;\n        Some(Self { inner, map_fn })\n    }\n}\n\nimpl<Inner, U> Mapped<Inner, U>\nwhere\n    Inner: Deref,\n{\n    /// Creates a mapped read guard from the inner guard.\n    pub fn new_with_guard(\n        inner: Inner,\n        map_fn: fn(&Inner::Target) -> &U,\n    ) -> Self {\n        Self { inner, map_fn }\n    }\n}\n\nimpl<Inner, U> Deref for Mapped<Inner, U>\nwhere\n    Inner: Deref,\n{\n    type Target = U;\n\n    fn deref(&self) -> &Self::Target {\n        (self.map_fn)(self.inner.deref())\n    }\n}\n\nimpl<Inner, U: PartialEq> PartialEq for Mapped<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn eq(&self, other: &Self) -> bool {\n        **self == **other\n    }\n}\n\nimpl<Inner, U: PartialEq> PartialEq<U> for Mapped<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn eq(&self, other: &U) -> bool {\n        **self == *other\n    }\n}\n\nimpl<Inner, U: Display> Display for Mapped<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\n/// A guard that provides mutable access to a signal's value, triggering some reactive change\n/// when it is dropped.\n#[derive(Debug)]\npub struct WriteGuard<S, G>\nwhere\n    S: Notify,\n{\n    pub(crate) triggerable: Option<S>,\n    pub(crate) guard: Option<G>,\n}\n\nimpl<S, G> WriteGuard<S, G>\nwhere\n    S: Notify,\n{\n    /// Creates a new guard from the inner mutable guard type, and the signal that should be\n    /// triggered on drop.\n    pub fn new(triggerable: S, guard: G) -> Self {\n        Self {\n            triggerable: Some(triggerable),\n            guard: Some(guard),\n        }\n    }\n}\n\nimpl<S, G> UntrackableGuard for WriteGuard<S, G>\nwhere\n    S: Notify,\n    G: DerefMut,\n{\n    /// Removes the triggerable type, so that it is no longer notifies when dropped.\n    fn untrack(&mut self) {\n        self.triggerable.take();\n    }\n}\n\nimpl<S, G> Deref for WriteGuard<S, G>\nwhere\n    S: Notify,\n    G: Deref,\n{\n    type Target = G::Target;\n\n    fn deref(&self) -> &Self::Target {\n        self.guard\n            .as_ref()\n            .expect(\n                \"the guard should always be in place until the Drop \\\n                 implementation\",\n            )\n            .deref()\n    }\n}\n\nimpl<S, G> DerefMut for WriteGuard<S, G>\nwhere\n    S: Notify,\n    G: DerefMut,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.guard\n            .as_mut()\n            .expect(\n                \"the guard should always be in place until the Drop \\\n                 implementation\",\n            )\n            .deref_mut()\n    }\n}\n\n/// A guard that provides mutable access to a signal's inner value, but does not notify of any\n/// changes.\npub struct UntrackedWriteGuard<T: 'static>(ArcRwLockWriteGuardian<T>);\n\nimpl<T: 'static> UntrackedWriteGuard<T> {\n    /// Creates a write guard from the given lock.\n    pub fn try_new(inner: Arc<RwLock<T>>) -> Option<Self> {\n        ArcRwLockWriteGuardian::try_take(inner)?\n            .ok()\n            .map(UntrackedWriteGuard)\n    }\n}\n\nimpl<T> Deref for UntrackedWriteGuard<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.0.deref()\n    }\n}\n\nimpl<T> DerefMut for UntrackedWriteGuard<T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.0.deref_mut()\n    }\n}\n\n// Dropping the write guard will notify dependencies.\nimpl<S, T> Drop for WriteGuard<S, T>\nwhere\n    S: Notify,\n{\n    fn drop(&mut self) {\n        // first, drop the inner guard\n        drop(self.guard.take());\n\n        // then, notify about a change\n        if let Some(triggerable) = self.triggerable.as_ref() {\n            triggerable.notify();\n        }\n    }\n}\n\n/// A mutable guard that maps over an inner mutable guard.\n#[derive(Debug)]\npub struct MappedMut<Inner, U>\nwhere\n    Inner: Deref,\n{\n    inner: Inner,\n    map_fn: fn(&Inner::Target) -> &U,\n    map_fn_mut: fn(&mut Inner::Target) -> &mut U,\n}\n\nimpl<Inner, U> UntrackableGuard for MappedMut<Inner, U>\nwhere\n    Inner: UntrackableGuard,\n{\n    fn untrack(&mut self) {\n        self.inner.untrack();\n    }\n}\n\nimpl<Inner, U> MappedMut<Inner, U>\nwhere\n    Inner: DerefMut,\n{\n    /// Creates a new writable guard from the inner guard.\n    pub fn new(\n        inner: Inner,\n        map_fn: fn(&Inner::Target) -> &U,\n        map_fn_mut: fn(&mut Inner::Target) -> &mut U,\n    ) -> Self {\n        Self {\n            inner,\n            map_fn,\n            map_fn_mut,\n        }\n    }\n}\n\nimpl<Inner, U> Deref for MappedMut<Inner, U>\nwhere\n    Inner: Deref,\n{\n    type Target = U;\n\n    fn deref(&self) -> &Self::Target {\n        (self.map_fn)(self.inner.deref())\n    }\n}\n\nimpl<Inner, U> DerefMut for MappedMut<Inner, U>\nwhere\n    Inner: DerefMut,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        (self.map_fn_mut)(self.inner.deref_mut())\n    }\n}\n\nimpl<Inner, U: PartialEq> PartialEq for MappedMut<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn eq(&self, other: &Self) -> bool {\n        **self == **other\n    }\n}\n\nimpl<Inner, U: Display> Display for MappedMut<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\n/// A mapped read guard in which the mapping function is a closure. If the mapping function is a\n/// function pointer, use [`Mapped`].\npub struct MappedArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    inner: Inner,\n    #[allow(clippy::type_complexity)]\n    map_fn: Arc<dyn Fn(&Inner::Target) -> &U>,\n}\n\nimpl<Inner, U> Clone for MappedArc<Inner, U>\nwhere\n    Inner: Clone + Deref,\n{\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            map_fn: self.map_fn.clone(),\n        }\n    }\n}\n\nimpl<Inner, U> Debug for MappedArc<Inner, U>\nwhere\n    Inner: Debug + Deref,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"MappedArc\")\n            .field(\"inner\", &self.inner)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl<Inner, U> MappedArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    /// Creates a new mapped guard from the inner guard and the map function.\n    pub fn new(\n        inner: Inner,\n        map_fn: impl Fn(&Inner::Target) -> &U + 'static,\n    ) -> Self {\n        Self {\n            inner,\n            map_fn: Arc::new(map_fn),\n        }\n    }\n}\n\nimpl<Inner, U> Deref for MappedArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    type Target = U;\n\n    fn deref(&self) -> &Self::Target {\n        (self.map_fn)(self.inner.deref())\n    }\n}\n\nimpl<Inner, U: PartialEq> PartialEq for MappedArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn eq(&self, other: &Self) -> bool {\n        **self == **other\n    }\n}\n\nimpl<Inner, U: Display> Display for MappedArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\n/// A mapped write guard in which the mapping function is a closure. If the mapping function is a\n/// function pointer, use [`MappedMut`].\npub struct MappedMutArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    inner: Inner,\n    #[allow(clippy::type_complexity)]\n    map_fn: Arc<dyn Fn(&Inner::Target) -> &U>,\n    #[allow(clippy::type_complexity)]\n    map_fn_mut: Arc<dyn Fn(&mut Inner::Target) -> &mut U>,\n}\n\nimpl<Inner, U> Clone for MappedMutArc<Inner, U>\nwhere\n    Inner: Clone + Deref,\n{\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            map_fn: self.map_fn.clone(),\n            map_fn_mut: self.map_fn_mut.clone(),\n        }\n    }\n}\n\nimpl<Inner, U> Debug for MappedMutArc<Inner, U>\nwhere\n    Inner: Debug + Deref,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"MappedMutArc\")\n            .field(\"inner\", &self.inner)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl<Inner, U> UntrackableGuard for MappedMutArc<Inner, U>\nwhere\n    Inner: UntrackableGuard,\n{\n    fn untrack(&mut self) {\n        self.inner.untrack();\n    }\n}\n\nimpl<Inner, U> MappedMutArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    /// Creates the new mapped mutable guard from the inner guard and mapping functions.\n    pub fn new(\n        inner: Inner,\n        map_fn: impl Fn(&Inner::Target) -> &U + 'static,\n        map_fn_mut: impl Fn(&mut Inner::Target) -> &mut U + 'static,\n    ) -> Self {\n        Self {\n            inner,\n            map_fn: Arc::new(map_fn),\n            map_fn_mut: Arc::new(map_fn_mut),\n        }\n    }\n}\n\nimpl<Inner, U> Deref for MappedMutArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    type Target = U;\n\n    fn deref(&self) -> &Self::Target {\n        (self.map_fn)(self.inner.deref())\n    }\n}\n\nimpl<Inner, U> DerefMut for MappedMutArc<Inner, U>\nwhere\n    Inner: DerefMut,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        (self.map_fn_mut)(self.inner.deref_mut())\n    }\n}\n\nimpl<Inner, U: PartialEq> PartialEq for MappedMutArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn eq(&self, other: &Self) -> bool {\n        **self == **other\n    }\n}\n\nimpl<Inner, U: Display> Display for MappedMutArc<Inner, U>\nwhere\n    Inner: Deref,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\n/// A wrapper that implements [`Deref`] and [`Borrow`] for itself.\npub struct Derefable<T>(pub T);\n\nimpl<T> Clone for Derefable<T>\nwhere\n    T: Clone,\n{\n    fn clone(&self) -> Self {\n        Derefable(self.0.clone())\n    }\n}\n\nimpl<T> std::ops::Deref for Derefable<T> {\n    type Target = T;\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<T> Borrow<T> for Derefable<T> {\n    fn borrow(&self) -> &T {\n        self.deref()\n    }\n}\n\nimpl<T> PartialEq<T> for Derefable<T>\nwhere\n    T: PartialEq,\n{\n    fn eq(&self, other: &T) -> bool {\n        self.deref() == other\n    }\n}\n\nimpl<T> Display for Derefable<T>\nwhere\n    T: Display,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/mapped.rs",
    "content": "use super::{\n    guards::{Mapped, MappedMutArc},\n    ArcRwSignal, RwSignal,\n};\nuse crate::{\n    owner::{StoredValue, SyncStorage},\n    signal::guards::WriteGuard,\n    traits::{\n        DefinedAt, GetValue, IsDisposed, Notify, ReadUntracked, Track,\n        UntrackableGuard, Write,\n    },\n};\nuse guardian::ArcRwLockWriteGuardian;\nuse std::{\n    fmt::Debug,\n    ops::{Deref, DerefMut},\n    panic::Location,\n    sync::Arc,\n};\n\n/// A derived signal type that wraps an [`ArcRwSignal`] with a mapping function,\n///  allowing you to read or write directly to one of its field.\n///\n/// Tracking the mapped signal tracks changes to *any* part of the signal, and updating the signal notifies\n/// and notifies *all* dependencies of the signal. This is not a mechanism for fine-grained reactive updates\n/// to more complex data structures. Instead, it allows you to provide a signal-like API for wrapped types\n/// without exposing the original type directly to users.\npub struct ArcMappedSignal<T> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    #[allow(clippy::type_complexity)]\n    try_read_untracked: Arc<\n        dyn Fn() -> Option<DoubleDeref<Box<dyn Deref<Target = T>>>>\n            + Send\n            + Sync,\n    >,\n    try_write: Arc<\n        dyn Fn() -> Option<Box<dyn UntrackableGuard<Target = T>>> + Send + Sync,\n    >,\n    notify: Arc<dyn Fn() + Send + Sync>,\n    track: Arc<dyn Fn() + Send + Sync>,\n}\n\nimpl<T> Clone for ArcMappedSignal<T> {\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            try_read_untracked: self.try_read_untracked.clone(),\n            try_write: self.try_write.clone(),\n            notify: self.notify.clone(),\n            track: self.track.clone(),\n        }\n    }\n}\n\nimpl<T> ArcMappedSignal<T> {\n    /// Wraps a signal with the given mapping functions for shared and exclusive references.\n    #[track_caller]\n    pub fn new<U>(\n        inner: ArcRwSignal<U>,\n        map: fn(&U) -> &T,\n        map_mut: fn(&mut U) -> &mut T,\n    ) -> Self\n    where\n        T: 'static,\n        U: Send + Sync + 'static,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            try_read_untracked: {\n                let this = inner.clone();\n                Arc::new(move || {\n                    this.try_read_untracked().map(|guard| DoubleDeref {\n                        inner: Box::new(Mapped::new_with_guard(guard, map))\n                            as Box<dyn Deref<Target = T>>,\n                    })\n                })\n            },\n            try_write: {\n                let this = inner.clone();\n                Arc::new(move || {\n                    let guard = ArcRwLockWriteGuardian::try_take(Arc::clone(\n                        &this.value,\n                    ))?\n                    .ok()?;\n                    let mapped = WriteGuard::new(\n                        this.clone(),\n                        MappedMutArc::new(guard, map, map_mut),\n                    );\n                    Some(Box::new(mapped))\n                })\n            },\n            notify: {\n                let this = inner.clone();\n                Arc::new(move || {\n                    this.notify();\n                })\n            },\n            track: {\n                Arc::new(move || {\n                    inner.track();\n                })\n            },\n        }\n    }\n}\n\nimpl<T> Debug for ArcMappedSignal<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut partial = f.debug_struct(\"ArcMappedSignal\");\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        partial.field(\"defined_at\", &self.defined_at);\n        partial.finish()\n    }\n}\n\nimpl<T> DefinedAt for ArcMappedSignal<T> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> Notify for ArcMappedSignal<T> {\n    fn notify(&self) {\n        (self.notify)()\n    }\n}\n\nimpl<T> Track for ArcMappedSignal<T> {\n    fn track(&self) {\n        (self.track)()\n    }\n}\n\nimpl<T> ReadUntracked for ArcMappedSignal<T> {\n    type Value = DoubleDeref<Box<dyn Deref<Target = T>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        (self.try_read_untracked)()\n    }\n}\n\nimpl<T> IsDisposed for ArcMappedSignal<T> {\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T> Write for ArcMappedSignal<T>\nwhere\n    T: 'static,\n{\n    type Value = T;\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        let mut guard = self.try_write()?;\n        guard.untrack();\n        Some(guard)\n    }\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        let inner = (self.try_write)()?;\n        let inner = DoubleDeref { inner };\n        Some(inner)\n    }\n}\n\n/// A wrapper for a smart pointer that implements [`Deref`] and [`DerefMut`]\n/// by dereferencing the type *inside* the smart pointer.\n///\n/// This is quite obscure and mostly useful for situations in which we want\n/// a wrapper for `Box<dyn Deref<Target = T>>` that dereferences to `T` rather\n/// than dereferencing to `dyn Deref<Target = T>`.\n///\n/// This is used internally in [`MappedSignal`] and [`ArcMappedSignal`].\npub struct DoubleDeref<T> {\n    inner: T,\n}\n\nimpl<T> Deref for DoubleDeref<T>\nwhere\n    T: Deref,\n    T::Target: Deref,\n{\n    type Target = <T::Target as Deref>::Target;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.deref().deref()\n    }\n}\n\nimpl<T> DerefMut for DoubleDeref<T>\nwhere\n    T: DerefMut,\n    T::Target: DerefMut,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.inner.deref_mut().deref_mut()\n    }\n}\n\nimpl<T> UntrackableGuard for DoubleDeref<T>\nwhere\n    T: UntrackableGuard,\n    T::Target: DerefMut,\n{\n    fn untrack(&mut self) {\n        self.inner.untrack();\n    }\n}\n\n/// A derived signal type that wraps an [`RwSignal`] with a mapping function,\n///  allowing you to read or write directly to one of its field.\n///\n/// Tracking the mapped signal tracks changes to *any* part of the signal, and updating the signal notifies\n/// and notifies *all* dependencies of the signal. This is not a mechanism for fine-grained reactive updates\n/// to more complex data structures. Instead, it allows you to provide a signal-like API for wrapped types\n/// without exposing the original type directly to users.\npub struct MappedSignal<T, S = SyncStorage> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    inner: StoredValue<ArcMappedSignal<T>, S>,\n}\n\nimpl<T> MappedSignal<T> {\n    /// Wraps a signal with the given mapping functions for shared and exclusive references.\n    #[track_caller]\n    pub fn new<U>(\n        inner: RwSignal<U>,\n        map: fn(&U) -> &T,\n        map_mut: fn(&mut U) -> &mut T,\n    ) -> Self\n    where\n        T: Send + Sync + 'static,\n        U: Send + Sync + 'static,\n    {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: {\n                let this = ArcRwSignal::from(inner);\n                StoredValue::new_with_storage(ArcMappedSignal::new(\n                    this, map, map_mut,\n                ))\n            },\n        }\n    }\n}\n\nimpl<T> Copy for MappedSignal<T> {}\n\nimpl<T> Clone for MappedSignal<T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T> Debug for MappedSignal<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut partial = f.debug_struct(\"MappedSignal\");\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        partial.field(\"defined_at\", &self.defined_at);\n        partial.finish()\n    }\n}\n\nimpl<T> DefinedAt for MappedSignal<T> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> Notify for MappedSignal<T>\nwhere\n    T: 'static,\n{\n    fn notify(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.notify();\n        }\n    }\n}\n\nimpl<T> Track for MappedSignal<T>\nwhere\n    T: 'static,\n{\n    fn track(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.track();\n        }\n    }\n}\n\nimpl<T> ReadUntracked for MappedSignal<T>\nwhere\n    T: 'static,\n{\n    type Value = DoubleDeref<Box<dyn Deref<Target = T>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.inner\n            .try_get_value()\n            .and_then(|inner| inner.try_read_untracked())\n    }\n}\n\nimpl<T> Write for MappedSignal<T>\nwhere\n    T: 'static,\n{\n    type Value = T;\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        let mut guard = self.try_write()?;\n        guard.untrack();\n        Some(guard)\n    }\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        let inner = self.inner.try_get_value()?;\n        let inner = (inner.try_write)()?;\n        let inner = DoubleDeref { inner };\n        Some(inner)\n    }\n}\n\nimpl<T> From<ArcMappedSignal<T>> for MappedSignal<T>\nwhere\n    T: 'static,\n{\n    #[track_caller]\n    fn from(value: ArcMappedSignal<T>) -> Self {\n        MappedSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: StoredValue::new(value),\n        }\n    }\n}\n\nimpl<T> IsDisposed for MappedSignal<T> {\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/read.rs",
    "content": "use super::{\n    guards::{Plain, ReadGuard},\n    subscriber_traits::AsSubscriberSet,\n    ArcReadSignal,\n};\nuse crate::{\n    graph::SubscriberSet,\n    owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},\n    traits::{DefinedAt, Dispose, IntoInner, IsDisposed, ReadUntracked},\n    unwrap_signal,\n};\nuse core::fmt::Debug;\nuse std::{\n    hash::Hash,\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\n/// An arena-allocated getter for a reactive signal.\n///\n/// A signal is a piece of data that may change over time,\n/// and notifies other code when it has changed.\n///\n/// This is an arena-allocated signal, which is `Copy` and is disposed when its reactive\n/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted signal that lives\n/// as long as a reference to it is alive, see [`ArcReadSignal`].\n///\n/// ## Core Trait Implementations\n/// - [`.get()`](crate::traits::Get) clones the current value of the signal.\n///   If you call it within an effect, it will cause that effect to subscribe\n///   to the signal, and to re-run whenever the value of the signal changes.\n///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of\n///     the signal without reactively tracking it.\n/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the\n///   value of the signal by reference. If you call it within an effect, it will\n///   cause that effect to subscribe to the signal, and to re-run whenever the\n///   value of the signal changes.\n///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the\n///     current value of the signal without reactively tracking it.\n/// - [`.with()`](crate::traits::With) allows you to reactively access the signal’s\n///   value without cloning by applying a callback function.\n///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access\n///     the signal’s value by applying a callback function without reactively\n///     tracking it.\n/// - [`.to_stream()`](crate::traits::ToStream) converts the signal to an `async`\n///   stream of values.\n/// - [`::from_stream()`](crate::traits::FromStream) converts an `async` stream\n///   of values into a signal containing the latest value.\n///\n/// ## Examples\n/// ```\n/// # use reactive_graph::prelude::*; use reactive_graph::signal::*;  let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let (count, set_count) = signal(0);\n///\n/// // calling .get() clones and returns the value\n/// assert_eq!(count.get(), 0);\n/// // calling .read() accesses the value by reference\n/// assert_eq!(count.read(), 0);\n/// ```\npub struct ReadSignal<T, S = SyncStorage> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    pub(crate) inner: ArenaItem<ArcReadSignal<T>, S>,\n}\n\nimpl<T, S> Dispose for ReadSignal<T, S> {\n    fn dispose(self) {\n        self.inner.dispose()\n    }\n}\n\nimpl<T, S> Copy for ReadSignal<T, S> {}\n\nimpl<T, S> Clone for ReadSignal<T, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> Debug for ReadSignal<T, S>\nwhere\n    S: Debug,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ReadSignal\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"store\", &self.inner)\n            .finish()\n    }\n}\n\nimpl<T, S> PartialEq for ReadSignal<T, S> {\n    fn eq(&self, other: &Self) -> bool {\n        self.inner == other.inner\n    }\n}\n\nimpl<T, S> Eq for ReadSignal<T, S> {}\n\nimpl<T, S> Hash for ReadSignal<T, S> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.inner.hash(state);\n    }\n}\n\nimpl<T, S> DefinedAt for ReadSignal<T, S> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T, S> IsDisposed for ReadSignal<T, S> {\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<T, S> IntoInner for ReadSignal<T, S>\nwhere\n    S: Storage<ArcReadSignal<T>>,\n{\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        self.inner.into_inner()?.into_inner()\n    }\n}\n\nimpl<T, S> AsSubscriberSet for ReadSignal<T, S>\nwhere\n    S: Storage<ArcReadSignal<T>>,\n{\n    type Output = Arc<RwLock<SubscriberSet>>;\n\n    fn as_subscriber_set(&self) -> Option<Self::Output> {\n        self.inner\n            .try_with_value(|inner| inner.as_subscriber_set())\n            .flatten()\n    }\n}\n\nimpl<T, S> ReadUntracked for ReadSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcReadSignal<T>>,\n{\n    type Value = ReadGuard<T, Plain<T>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.read_untracked())\n    }\n}\n\nimpl<T> From<ArcReadSignal<T>> for ReadSignal<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcReadSignal<T>) -> Self {\n        ReadSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T> FromLocal<ArcReadSignal<T>> for ReadSignal<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    #[track_caller]\n    fn from_local(value: ArcReadSignal<T>) -> Self {\n        ReadSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T, S> From<ReadSignal<T, S>> for ArcReadSignal<T>\nwhere\n    T: 'static,\n    S: Storage<ArcReadSignal<T>>,\n{\n    #[track_caller]\n    fn from(value: ReadSignal<T, S>) -> Self {\n        value\n            .inner\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(value))\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/rw.rs",
    "content": "use super::{\n    guards::{Plain, ReadGuard},\n    subscriber_traits::AsSubscriberSet,\n    ArcReadSignal, ArcRwSignal, ArcWriteSignal, ReadSignal, WriteSignal,\n};\nuse crate::{\n    graph::{ReactiveNode, SubscriberSet},\n    owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},\n    signal::guards::{UntrackedWriteGuard, WriteGuard},\n    traits::{\n        DefinedAt, Dispose, IntoInner, IsDisposed, Notify, ReadUntracked,\n        UntrackableGuard, Write,\n    },\n    unwrap_signal,\n};\nuse core::fmt::Debug;\nuse guardian::ArcRwLockWriteGuardian;\nuse std::{\n    hash::Hash,\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\n/// An arena-allocated signal that can be read from or written to.\n///\n/// A signal is a piece of data that may change over time, and notifies other\n/// code when it has changed. This is the atomic unit of reactivity, which begins all other\n/// processes of reactive updates.\n///\n/// This is an arena-allocated signal, which is `Copy` and is disposed when its reactive\n/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted signal that lives\n/// as long as a reference to it is alive, see [`ArcRwSignal`].\n///\n/// ## Core Trait Implementations\n///\n/// ### Reading the Value\n/// - [`.get()`](crate::traits::Get) clones the current value of the signal.\n///   If you call it within an effect, it will cause that effect to subscribe\n///   to the signal, and to re-run whenever the value of the signal changes.\n///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of\n///     the signal without reactively tracking it.\n/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the\n///   value of the signal by reference. If you call it within an effect, it will\n///   cause that effect to subscribe to the signal, and to re-run whenever the\n///   value of the signal changes.\n///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the\n///     current value of the signal without reactively tracking it.\n/// - [`.with()`](crate::traits::With) allows you to reactively access the signal’s\n///   value without cloning by applying a callback function.\n///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access\n///     the signal’s value by applying a callback function without reactively\n///     tracking it.\n/// - [`.to_stream()`](crate::traits::ToStream) converts the signal to an `async`\n///   stream of values.\n///\n/// ### Updating the Value\n/// - [`.set()`](crate::traits::Set) sets the signal to a new value.\n/// - [`.update()`](crate::traits::Update) updates the value of the signal by\n///   applying a closure that takes a mutable reference.\n/// - [`.write()`](crate::traits::Write) returns a guard through which the signal\n///   can be mutated, and which notifies subscribers when it is dropped.\n///\n/// > Each of these has a related `_untracked()` method, which updates the signal\n/// > without notifying subscribers. Untracked updates are not desirable in most\n/// > cases, as they cause “tearing” between the signal’s value and its observed\n/// > value. If you want a non-reactive container, used [`ArenaItem`] instead.\n///\n/// ## Examples\n///\n/// ```\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let count = ArcRwSignal::new(0);\n///\n/// // ✅ calling the getter clones and returns the value\n/// //    this can be `count()` on nightly\n/// assert_eq!(count.get(), 0);\n///\n/// // ✅ calling the setter sets the value\n/// //    this can be `set_count(1)` on nightly\n/// count.set(1);\n/// assert_eq!(count.get(), 1);\n///\n/// // ❌ you could call the getter within the setter\n/// // set_count.set(count.get() + 1);\n///\n/// // ✅ however it's more efficient to use .update() and mutate the value in place\n/// count.update(|count: &mut i32| *count += 1);\n/// assert_eq!(count.get(), 2);\n///\n/// // ✅ you can create \"derived signals\" with a Fn() -> T interface\n/// let double_count = {\n///   // clone before moving into the closure because we use it below\n///   let count = count.clone();\n///   move || count.get() * 2\n/// };\n/// count.set(0);\n/// assert_eq!(double_count(), 0);\n/// count.set(1);\n/// assert_eq!(double_count(), 2);\n/// ```\npub struct RwSignal<T, S = SyncStorage> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    inner: ArenaItem<ArcRwSignal<T>, S>,\n}\n\nimpl<T, S> Dispose for RwSignal<T, S> {\n    fn dispose(self) {\n        self.inner.dispose()\n    }\n}\n\nimpl<T> RwSignal<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    /// Creates a new signal, taking the initial value as its argument.\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    #[track_caller]\n    pub fn new(value: T) -> Self {\n        Self::new_with_storage(value)\n    }\n}\n\nimpl<T, S> RwSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcRwSignal<T>>,\n{\n    /// Creates a new signal with the given arena storage method.\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    #[track_caller]\n    pub fn new_with_storage(value: T) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcRwSignal::new(value)),\n        }\n    }\n}\n\nimpl<T> RwSignal<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    /// Creates a new signal, taking the initial value as its argument. Unlike [`RwSignal::new`],\n    /// this pins the value to the current thread. Accessing it from any other thread will panic.\n    #[cfg_attr(\n        feature = \"tracing\",\n        tracing::instrument(level = \"trace\", skip_all)\n    )]\n    #[track_caller]\n    pub fn new_local(value: T) -> Self {\n        Self::new_with_storage(value)\n    }\n}\n\nimpl<T, S> RwSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcRwSignal<T>> + Storage<ArcReadSignal<T>>,\n{\n    /// Returns a read-only handle to the signal.\n    #[inline(always)]\n    #[track_caller]\n    pub fn read_only(&self) -> ReadSignal<T, S> {\n        ReadSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(\n                self.inner\n                    .try_get_value()\n                    .map(|inner| inner.read_only())\n                    .unwrap_or_else(unwrap_signal!(self)),\n            ),\n        }\n    }\n}\n\nimpl<T, S> RwSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcRwSignal<T>> + Storage<ArcWriteSignal<T>>,\n{\n    /// Returns a write-only handle to the signal.\n    #[inline(always)]\n    #[track_caller]\n    pub fn write_only(&self) -> WriteSignal<T, S> {\n        WriteSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(\n                self.inner\n                    .try_get_value()\n                    .map(|inner| inner.write_only())\n                    .unwrap_or_else(unwrap_signal!(self)),\n            ),\n        }\n    }\n}\n\nimpl<T, S> RwSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcRwSignal<T>>\n        + Storage<ArcWriteSignal<T>>\n        + Storage<ArcReadSignal<T>>,\n{\n    /// Splits the signal into its readable and writable halves.\n    #[track_caller]\n    #[inline(always)]\n    pub fn split(&self) -> (ReadSignal<T, S>, WriteSignal<T, S>) {\n        (self.read_only(), self.write_only())\n    }\n\n    /// Reunites the two halves of a signal. Returns `None` if the two signals\n    /// provided were not created from the same signal.\n    #[track_caller]\n    pub fn unite(\n        read: ReadSignal<T, S>,\n        write: WriteSignal<T, S>,\n    ) -> Option<Self> {\n        match (read.inner.try_get_value(), write.inner.try_get_value()) {\n            (Some(read), Some(write)) => {\n                if Arc::ptr_eq(&read.inner, &write.inner) {\n                    Some(Self {\n                        #[cfg(any(debug_assertions, leptos_debuginfo))]\n                        defined_at: Location::caller(),\n                        inner: ArenaItem::new_with_storage(ArcRwSignal {\n                            #[cfg(any(debug_assertions, leptos_debuginfo))]\n                            defined_at: Location::caller(),\n                            value: Arc::clone(&read.value),\n                            inner: Arc::clone(&read.inner),\n                        }),\n                    })\n                } else {\n                    None\n                }\n            }\n            _ => None,\n        }\n    }\n}\n\nimpl<T, S> Copy for RwSignal<T, S> {}\n\nimpl<T, S> Clone for RwSignal<T, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> Debug for RwSignal<T, S>\nwhere\n    S: Debug,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"RwSignal\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"store\", &self.inner)\n            .finish()\n    }\n}\n\nimpl<T, S> Default for RwSignal<T, S>\nwhere\n    T: Default + 'static,\n    S: Storage<ArcRwSignal<T>>,\n{\n    #[track_caller]\n    fn default() -> Self {\n        Self::new_with_storage(T::default())\n    }\n}\n\nimpl<T, S> PartialEq for RwSignal<T, S> {\n    fn eq(&self, other: &Self) -> bool {\n        self.inner == other.inner\n    }\n}\n\nimpl<T, S> Eq for RwSignal<T, S> {}\n\nimpl<T, S> Hash for RwSignal<T, S> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.inner.hash(state);\n    }\n}\n\nimpl<T, S> DefinedAt for RwSignal<T, S> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T: 'static, S> IsDisposed for RwSignal<T, S> {\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<T, S> IntoInner for RwSignal<T, S>\nwhere\n    S: Storage<ArcRwSignal<T>>,\n{\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        self.inner.into_inner()?.into_inner()\n    }\n}\n\nimpl<T, S> AsSubscriberSet for RwSignal<T, S>\nwhere\n    S: Storage<ArcRwSignal<T>>,\n{\n    type Output = Arc<RwLock<SubscriberSet>>;\n\n    fn as_subscriber_set(&self) -> Option<Self::Output> {\n        self.inner\n            .try_with_value(|inner| inner.as_subscriber_set())\n            .flatten()\n    }\n}\n\nimpl<T, S> ReadUntracked for RwSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcRwSignal<T>>,\n{\n    type Value = ReadGuard<T, Plain<T>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.read_untracked())\n    }\n}\n\nimpl<T, S> Notify for RwSignal<T, S>\nwhere\n    S: Storage<ArcRwSignal<T>>,\n{\n    fn notify(&self) {\n        self.mark_dirty();\n    }\n}\n\nimpl<T, S> Write for RwSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcRwSignal<T>>,\n{\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        let guard = self.inner.try_with_value(|n| {\n            ArcRwLockWriteGuardian::take(Arc::clone(&n.value)).ok()\n        })??;\n        Some(WriteGuard::new(*self, guard))\n    }\n\n    #[allow(refining_impl_trait)]\n    fn try_write_untracked(&self) -> Option<UntrackedWriteGuard<Self::Value>> {\n        self.inner\n            .try_with_value(|n| n.try_write_untracked())\n            .flatten()\n    }\n}\n\nimpl<T> From<ArcRwSignal<T>> for RwSignal<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcRwSignal<T>) -> Self {\n        RwSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<'a, T> From<&'a ArcRwSignal<T>> for RwSignal<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: &'a ArcRwSignal<T>) -> Self {\n        value.clone().into()\n    }\n}\n\nimpl<T> FromLocal<ArcRwSignal<T>> for RwSignal<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    #[track_caller]\n    fn from_local(value: ArcRwSignal<T>) -> Self {\n        RwSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T, S> From<RwSignal<T, S>> for ArcRwSignal<T>\nwhere\n    T: 'static,\n    S: Storage<ArcRwSignal<T>>,\n{\n    #[track_caller]\n    fn from(value: RwSignal<T, S>) -> Self {\n        value\n            .inner\n            .try_get_value()\n            .unwrap_or_else(unwrap_signal!(value))\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/subscriber_traits.rs",
    "content": "//! Traits to reduce the boilerplate when implementing the [`ReactiveNode`], [`Source`], and\n//! [`ToAnySource`] traits for signal types.\n//!\n//! These traits can be automatically derived for any type that\n//! 1) is a root node in the reactive graph, with no sources (i.e., a signal, not a memo)\n//! 2) contains an `Arc<RwLock<SubscriberSet>>`\n//!\n//! This makes it easy to implement a variety of different signal primitives, as long as they share\n//! these characteristics.\n\nuse crate::{\n    graph::{\n        AnySource, AnySubscriber, ReactiveNode, Source, SubscriberSet,\n        ToAnySource,\n    },\n    traits::{DefinedAt, IsDisposed},\n    unwrap_signal,\n};\nuse or_poisoned::OrPoisoned;\nuse std::{\n    borrow::Borrow,\n    sync::{Arc, RwLock, Weak},\n};\n\npub(crate) trait AsSubscriberSet {\n    type Output: Borrow<RwLock<SubscriberSet>>;\n\n    fn as_subscriber_set(&self) -> Option<Self::Output>;\n}\n\nimpl<'a> AsSubscriberSet for &'a RwLock<SubscriberSet> {\n    type Output = &'a RwLock<SubscriberSet>;\n\n    #[inline(always)]\n    fn as_subscriber_set(&self) -> Option<Self::Output> {\n        Some(self)\n    }\n}\n\nimpl DefinedAt for RwLock<SubscriberSet> {\n    fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {\n        None\n    }\n}\n\n// Implement reactive types for RwLock<SubscriberSet>\n// This is used so that Weak<RwLock<SubscriberSet>> is a Weak<dyn ReactiveNode> and Weak<dyn\n// Source>\nimpl<T: AsSubscriberSet + DefinedAt> ReactiveNode for T {\n    fn mark_dirty(&self) {\n        self.mark_subscribers_check();\n    }\n\n    fn mark_check(&self) {}\n\n    fn mark_subscribers_check(&self) {\n        if let Some(inner) = self.as_subscriber_set() {\n            let subs = inner.borrow().read().unwrap().clone();\n            for sub in subs {\n                sub.mark_dirty();\n            }\n        }\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        // a signal will always mark its dependents Dirty when it runs, so they know\n        // that they may have changed and need to check themselves at least\n        //\n        // however, it's always possible that *another* signal or memo has triggered any\n        // given effect/memo, and so this signal should *not* say that it is dirty, as it\n        // may also be checked but has not changed\n        false\n    }\n}\n\nimpl<T: AsSubscriberSet + DefinedAt> Source for T {\n    fn clear_subscribers(&self) {\n        if let Some(inner) = self.as_subscriber_set() {\n            inner.borrow().write().unwrap().take();\n        }\n    }\n\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        if let Some(inner) = self.as_subscriber_set() {\n            inner.borrow().write().unwrap().subscribe(subscriber)\n        }\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        if let Some(inner) = self.as_subscriber_set() {\n            inner.borrow().write().unwrap().unsubscribe(subscriber)\n        }\n    }\n}\n\nimpl<T: AsSubscriberSet + DefinedAt + IsDisposed> ToAnySource for T\nwhere\n    T::Output: Borrow<Arc<RwLock<SubscriberSet>>>,\n{\n    #[track_caller]\n    fn to_any_source(&self) -> AnySource {\n        self.as_subscriber_set()\n            .map(|subs| {\n                let subs = subs.borrow();\n                AnySource(\n                    Arc::as_ptr(subs) as usize,\n                    Arc::downgrade(subs) as Weak<dyn Source + Send + Sync>,\n                    #[cfg(any(debug_assertions, leptos_debuginfo))]\n                    self.defined_at().expect(\"no DefinedAt in debug mode\"),\n                )\n            })\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl ReactiveNode for RwLock<SubscriberSet> {\n    fn mark_dirty(&self) {\n        self.mark_subscribers_check();\n    }\n\n    fn mark_check(&self) {}\n\n    fn mark_subscribers_check(&self) {\n        let subs = self.write().unwrap().take();\n        for sub in subs {\n            sub.mark_dirty();\n        }\n    }\n\n    fn update_if_necessary(&self) -> bool {\n        // a signal will always mark its dependents Dirty when it runs, so they know\n        // that they may have changed and need to check themselves at least\n        //\n        // however, it's always possible that *another* signal or memo has triggered any\n        // given effect/memo, and so this signal should *not* say that it is dirty, as it\n        // may also be checked but has not changed\n        false\n    }\n}\n\nimpl Source for RwLock<SubscriberSet> {\n    fn clear_subscribers(&self) {\n        self.write().or_poisoned().take();\n    }\n\n    fn add_subscriber(&self, subscriber: AnySubscriber) {\n        self.write().or_poisoned().subscribe(subscriber)\n    }\n\n    fn remove_subscriber(&self, subscriber: &AnySubscriber) {\n        self.write().or_poisoned().unsubscribe(subscriber)\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/trigger.rs",
    "content": "use super::{subscriber_traits::AsSubscriberSet, ArcTrigger};\nuse crate::{\n    graph::{ReactiveNode, SubscriberSet},\n    owner::ArenaItem,\n    traits::{DefinedAt, Dispose, IsDisposed, Notify},\n};\nuse std::{\n    fmt::{Debug, Formatter, Result},\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\n/// A trigger is a data-less signal with the sole purpose of notifying other reactive code of a change.\n///\n/// This can be useful for when using external data not stored in signals, for example.\n///\n/// This is an arena-allocated Trigger, which is `Copy` and is disposed when its reactive\n/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted trigger that lives\n/// as long as a reference to it is alive, see [`ArcTrigger`].\npub struct Trigger {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    pub(crate) inner: ArenaItem<ArcTrigger>,\n}\n\nimpl Trigger {\n    /// Creates a new trigger.\n    #[track_caller]\n    pub fn new() -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new(ArcTrigger::new()),\n        }\n    }\n}\n\nimpl Default for Trigger {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Clone for Trigger {\n    #[track_caller]\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl Copy for Trigger {}\n\nimpl Debug for Trigger {\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        f.debug_struct(\"Trigger\").finish()\n    }\n}\n\nimpl Dispose for Trigger {\n    fn dispose(self) {\n        self.inner.dispose()\n    }\n}\n\nimpl IsDisposed for Trigger {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl AsSubscriberSet for Trigger {\n    type Output = Arc<RwLock<SubscriberSet>>;\n\n    #[inline(always)]\n    fn as_subscriber_set(&self) -> Option<Self::Output> {\n        self.inner\n            .try_get_value()\n            .and_then(|arc_trigger| arc_trigger.as_subscriber_set())\n    }\n}\n\nimpl DefinedAt for Trigger {\n    #[inline(always)]\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl Notify for Trigger {\n    fn notify(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.mark_dirty();\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal/write.rs",
    "content": "use super::{guards::WriteGuard, ArcWriteSignal};\nuse crate::{\n    owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},\n    traits::{\n        DefinedAt, Dispose, IntoInner, IsDisposed, Notify, UntrackableGuard,\n        Write,\n    },\n};\nuse core::fmt::Debug;\nuse guardian::ArcRwLockWriteGuardian;\nuse std::{hash::Hash, ops::DerefMut, panic::Location, sync::Arc};\n\n/// An arena-allocated setter for a reactive signal.\n///\n/// A signal is a piece of data that may change over time,\n/// and notifies other code when it has changed.\n///\n/// This is an arena-allocated signal, which is `Copy` and is disposed when its reactive\n/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted signal that lives\n/// as long as a reference to it is alive, see [`ArcWriteSignal`].\n///\n/// ## Core Trait Implementations\n/// - [`.set()`](crate::traits::Set) sets the signal to a new value.\n/// - [`.update()`](crate::traits::Update) updates the value of the signal by\n///   applying a closure that takes a mutable reference.\n/// - [`.write()`](crate::traits::Write) returns a guard through which the signal\n///   can be mutated, and which notifies subscribers when it is dropped.\n///\n/// > Each of these has a related `_untracked()` method, which updates the signal\n/// > without notifying subscribers. Untracked updates are not desirable in most\n/// > cases, as they cause “tearing” between the signal’s value and its observed\n/// > value. If you want a non-reactive container, use [`ArenaItem`] instead.\n///\n/// ## Examples\n/// ```\n/// # use reactive_graph::prelude::*; use reactive_graph::signal::*;  let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let (count, set_count) = signal(0);\n///\n/// // ✅ calling the setter sets the value\n/// //    `set_count(1)` on nightly\n/// set_count.set(1);\n/// assert_eq!(count.get(), 1);\n///\n/// // ❌ you could call the getter within the setter\n/// // set_count.set(count.get() + 1);\n///\n/// // ✅ however it's more efficient to use .update() and mutate the value in place\n/// set_count.update(|count: &mut i32| *count += 1);\n/// assert_eq!(count.get(), 2);\n///\n/// // ✅ `.write()` returns a guard that implements `DerefMut` and will notify when dropped\n/// *set_count.write() += 1;\n/// assert_eq!(count.get(), 3);\n/// ```\npub struct WriteSignal<T, S = SyncStorage> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    pub(crate) inner: ArenaItem<ArcWriteSignal<T>, S>,\n}\n\nimpl<T, S> Dispose for WriteSignal<T, S> {\n    fn dispose(self) {\n        self.inner.dispose()\n    }\n}\n\nimpl<T, S> Copy for WriteSignal<T, S> {}\n\nimpl<T, S> Clone for WriteSignal<T, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> Debug for WriteSignal<T, S>\nwhere\n    S: Debug,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"WriteSignal\")\n            .field(\"type\", &std::any::type_name::<T>())\n            .field(\"store\", &self.inner)\n            .finish()\n    }\n}\n\nimpl<T, S> PartialEq for WriteSignal<T, S> {\n    fn eq(&self, other: &Self) -> bool {\n        self.inner == other.inner\n    }\n}\n\nimpl<T, S> Eq for WriteSignal<T, S> {}\n\nimpl<T, S> Hash for WriteSignal<T, S> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.inner.hash(state);\n    }\n}\n\nimpl<T, S> DefinedAt for WriteSignal<T, S> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> From<ArcWriteSignal<T>> for WriteSignal<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcWriteSignal<T>) -> Self {\n        WriteSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T> FromLocal<ArcWriteSignal<T>> for WriteSignal<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    #[track_caller]\n    fn from_local(value: ArcWriteSignal<T>) -> Self {\n        WriteSignal {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T, S> IsDisposed for WriteSignal<T, S> {\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<T, S> IntoInner for WriteSignal<T, S>\nwhere\n    S: Storage<ArcWriteSignal<T>>,\n{\n    type Value = T;\n\n    #[inline(always)]\n    fn into_inner(self) -> Option<Self::Value> {\n        self.inner.into_inner()?.into_inner()\n    }\n}\n\nimpl<T, S> Notify for WriteSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcWriteSignal<T>>,\n{\n    fn notify(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.notify();\n        }\n    }\n}\n\nimpl<T, S> Write for WriteSignal<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcWriteSignal<T>>,\n{\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        let guard = self.inner.try_with_value(|n| {\n            ArcRwLockWriteGuardian::take(Arc::clone(&n.value)).ok()\n        })??;\n        Some(WriteGuard::new(*self, guard))\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.inner\n            .try_with_value(|n| n.try_write_untracked())\n            .flatten()\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/signal.rs",
    "content": "//! Reactive primitives for root values that can be changed, notifying other nodes in the reactive\n//! graph.\n\nmod arc_read;\nmod arc_rw;\nmod arc_trigger;\nmod arc_write;\npub mod guards;\nmod mapped;\nmod read;\nmod rw;\nmod subscriber_traits;\nmod trigger;\nmod write;\n\nuse crate::owner::LocalStorage;\npub use arc_read::*;\npub use arc_rw::*;\npub use arc_trigger::*;\npub use arc_write::*;\npub use mapped::*;\npub use read::*;\npub use rw::*;\npub use trigger::*;\npub use write::*;\n\n/// Creates a reference-counted signal.\n///\n/// A signal is a piece of data that may change over time, and notifies other\n/// code when it has changed. This is the atomic unit of reactivity, which begins all other\n/// processes of updating.\n///\n/// Takes the initial value as an argument, and returns a tuple containing an\n/// [`ArcReadSignal`] and an [`ArcWriteSignal`].\n///\n/// This returns reference-counted signals, which are `Clone` but not `Copy`. For arena-allocated\n/// `Copy` signals, use [`signal`].\n///\n/// ```\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let (count, set_count) = arc_signal(0);\n///\n/// // ✅ calling the getter clones and returns the value\n/// //    this can be `count()` on nightly\n/// assert_eq!(count.get(), 0);\n///\n/// // ✅ calling the setter sets the value\n/// //    this can be `set_count(1)` on nightly\n/// set_count.set(1);\n/// assert_eq!(count.get(), 1);\n///\n/// // ❌ you could call the getter within the setter\n/// // set_count.set(count.get() + 1);\n///\n/// // ✅ however it's more efficient to use .update() and mutate the value in place\n/// set_count.update(|count: &mut i32| *count += 1);\n/// assert_eq!(count.get(), 2);\n///\n/// // ✅ you can create \"derived signals\" with a Fn() -> T interface\n/// let double_count = move || count.get() * 2;\n/// set_count.set(0);\n/// assert_eq!(double_count(), 0);\n/// set_count.set(1);\n/// assert_eq!(double_count(), 2);\n/// ```\n#[inline(always)]\n#[track_caller]\npub fn arc_signal<T>(value: T) -> (ArcReadSignal<T>, ArcWriteSignal<T>) {\n    ArcRwSignal::new(value).split()\n}\n\n/// Creates an arena-allocated signal, the basic reactive primitive.\n///\n/// A signal is a piece of data that may change over time, and notifies other\n/// code when it has changed. This is the atomic unit of reactivity, which begins all other\n/// processes of updating.\n///\n/// Takes the initial value as an argument, and returns a tuple containing a\n/// [`ReadSignal`] and a [`WriteSignal`].\n///\n/// This returns an arena-allocated signal, which is `Copy` and is disposed when its reactive\n/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted signal that lives\n/// as long as a reference to it is alive, see [`arc_signal`].\n/// ```\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let (count, set_count) = signal(0);\n///\n/// // ✅ calling the getter clones and returns the value\n/// //    this can be `count()` on nightly\n/// assert_eq!(count.get(), 0);\n///\n/// // ✅ calling the setter sets the value\n/// //    this can be `set_count(1)` on nightly\n/// set_count.set(1);\n/// assert_eq!(count.get(), 1);\n///\n/// // ❌ you could call the getter within the setter\n/// // set_count.set(count.get() + 1);\n///\n/// // ✅ however it's more efficient to use .update() and mutate the value in place\n/// set_count.update(|count: &mut i32| *count += 1);\n/// assert_eq!(count.get(), 2);\n///\n/// // ✅ you can create \"derived signals\" with a Fn() -> T interface\n/// let double_count = move || count.get() * 2; // signals are `Copy` so you can `move` them anywhere\n/// set_count.set(0);\n/// assert_eq!(double_count(), 0);\n/// set_count.set(1);\n/// assert_eq!(double_count(), 2);\n/// ```\n#[inline(always)]\n#[track_caller]\npub fn signal<T: Send + Sync + 'static>(\n    value: T,\n) -> (ReadSignal<T>, WriteSignal<T>) {\n    let (r, w) = arc_signal(value);\n    (r.into(), w.into())\n}\n\n/// Creates an arena-allocated signal.\n///\n/// Unlike [`signal`], this does not require the value to be `Send + Sync`. Instead, it is stored\n/// on a local arena. Accessing either of the returned signals from another thread will panic.\n#[inline(always)]\n#[track_caller]\npub fn signal_local<T: 'static>(\n    value: T,\n) -> (ReadSignal<T, LocalStorage>, WriteSignal<T, LocalStorage>) {\n    RwSignal::new_local(value).split()\n}\n\n/// Creates an arena-allocated signal, the basic reactive primitive.\n///\n/// A signal is a piece of data that may change over time, and notifies other\n/// code when it has changed. This is the atomic unit of reactivity, which begins all other\n/// processes of updating.\n///\n/// Takes the initial value as an argument, and returns a tuple containing a\n/// [`ReadSignal`] and a [`WriteSignal`].\n///\n/// This returns an arena-allocated signal, which is `Copy` and is disposed when its reactive\n/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted signal that lives\n/// as long as a reference to it is alive, see [`arc_signal`].\n/// ```\n/// # use reactive_graph::prelude::*;\n/// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n/// let (count, set_count) = create_signal(0);\n///\n/// // ✅ calling the getter clones and returns the value\n/// //    this can be `count()` on nightly\n/// assert_eq!(count.get(), 0);\n///\n/// // ✅ calling the setter sets the value\n/// //    this can be `set_count(1)` on nightly\n/// set_count.set(1);\n/// assert_eq!(count.get(), 1);\n///\n/// // ❌ you could call the getter within the setter\n/// // set_count.set(count.get() + 1);\n///\n/// // ✅ however it's more efficient to use .update() and mutate the value in place\n/// set_count.update(|count: &mut i32| *count += 1);\n/// assert_eq!(count.get(), 2);\n///\n/// // ✅ you can create \"derived signals\" with a Fn() -> T interface\n/// let double_count = move || count.get() * 2; // signals are `Copy` so you can `move` them anywhere\n/// set_count.set(0);\n/// assert_eq!(double_count(), 0);\n/// set_count.set(1);\n/// assert_eq!(double_count(), 2);\n/// ```\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being renamed to `signal()` to conform to \\\n                Rust idioms.\"]\npub fn create_signal<T: Send + Sync + 'static>(\n    value: T,\n) -> (ReadSignal<T>, WriteSignal<T>) {\n    signal(value)\n}\n\n/// Creates a reactive signal with the getter and setter unified in one value.\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `RwSignal::new()` instead.\"]\npub fn create_rw_signal<T: Send + Sync + 'static>(value: T) -> RwSignal<T> {\n    RwSignal::new(value)\n}\n\n/// A trigger is a data-less signal with the sole purpose of notifying other reactive code of a change.\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `ArcTrigger::new()` instead.\"]\npub fn create_trigger() -> ArcTrigger {\n    ArcTrigger::new()\n}\n"
  },
  {
    "path": "reactive_graph/src/trait_options.rs",
    "content": "use crate::{\n    traits::{\n        DefinedAt, Get, GetUntracked, Read, ReadUntracked, Track, With,\n        WithUntracked,\n    },\n    unwrap_signal,\n};\nuse std::panic::Location;\n\nimpl<T> DefinedAt for Option<T>\nwhere\n    T: DefinedAt,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        self.as_ref().map(DefinedAt::defined_at).unwrap_or(None)\n    }\n}\n\nimpl<T> Track for Option<T>\nwhere\n    T: Track,\n{\n    fn track(&self) {\n        if let Some(signal) = self {\n            signal.track();\n        }\n    }\n}\n\n/// An alternative [`ReadUntracked`](crate) trait that works with `Option<Readable>` types.\npub trait ReadUntrackedOptional: Sized + DefinedAt {\n    /// The guard type that will be returned, which can be dereferenced to the value.\n    type Value;\n\n    /// Returns the guard, or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_read_untracked(&self) -> Option<Self::Value>;\n\n    /// Returns the guard.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn read_untracked(&self) -> Self::Value {\n        self.try_read_untracked()\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> ReadUntrackedOptional for Option<T>\nwhere\n    Self: DefinedAt,\n    T: ReadUntracked,\n{\n    type Value = Option<<T as ReadUntracked>::Value>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        Some(if let Some(signal) = self {\n            Some(signal.try_read_untracked()?)\n        } else {\n            None\n        })\n    }\n}\n\n/// An alternative [`Read`](crate) trait that works with `Option<Readable>` types.\npub trait ReadOptional: DefinedAt {\n    /// The guard type that will be returned, which can be dereferenced to the value.\n    type Value;\n\n    /// Subscribes to the signal, and returns the guard, or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_read(&self) -> Option<Self::Value>;\n\n    /// Subscribes to the signal, and returns the guard.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn read(&self) -> Self::Value {\n        self.try_read().unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> ReadOptional for Option<T>\nwhere\n    Self: DefinedAt,\n    T: Read,\n{\n    type Value = Option<<T as Read>::Value>;\n\n    fn try_read(&self) -> Option<Self::Value> {\n        Some(if let Some(readable) = self {\n            Some(readable.try_read()?)\n        } else {\n            None\n        })\n    }\n}\n\n/// An alternative [`WithUntracked`](crate) trait that works with `Option<Withable>` types.\npub trait WithUntrackedOptional: DefinedAt {\n    /// The type of the value contained in the signal.\n    type Value: ?Sized;\n\n    /// Applies the closure to the value, and returns the result,\n    /// or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_with_untracked<U>(\n        &self,\n        fun: impl FnOnce(Option<&Self::Value>) -> U,\n    ) -> Option<U>;\n\n    /// Applies the closure to the value, and returns the result.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn with_untracked<U>(\n        &self,\n        fun: impl FnOnce(Option<&Self::Value>) -> U,\n    ) -> U {\n        self.try_with_untracked(fun)\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> WithUntrackedOptional for Option<T>\nwhere\n    Self: DefinedAt,\n    T: WithUntracked,\n    <T as WithUntracked>::Value: Sized,\n{\n    type Value = <T as WithUntracked>::Value;\n\n    fn try_with_untracked<U>(\n        &self,\n        fun: impl FnOnce(Option<&Self::Value>) -> U,\n    ) -> Option<U> {\n        if let Some(signal) = self {\n            Some(signal.try_with_untracked(|val| fun(Some(val)))?)\n        } else {\n            Some(fun(None))\n        }\n    }\n}\n\n/// An alternative [`With`](crate) trait that works with `Option<Withable>` types.\npub trait WithOptional: DefinedAt {\n    /// The type of the value contained in the signal.\n    type Value: ?Sized;\n\n    /// Subscribes to the signal, applies the closure to the value, and returns the result,\n    /// or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_with<U>(\n        &self,\n        fun: impl FnOnce(Option<&Self::Value>) -> U,\n    ) -> Option<U>;\n\n    /// Subscribes to the signal, applies the closure to the value, and returns the result.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn with<U>(&self, fun: impl FnOnce(Option<&Self::Value>) -> U) -> U {\n        self.try_with(fun).unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> WithOptional for Option<T>\nwhere\n    Self: DefinedAt,\n    T: With,\n    <T as With>::Value: Sized,\n{\n    type Value = <T as With>::Value;\n\n    fn try_with<U>(\n        &self,\n        fun: impl FnOnce(Option<&Self::Value>) -> U,\n    ) -> Option<U> {\n        if let Some(signal) = self {\n            Some(signal.try_with(|val| fun(Some(val)))?)\n        } else {\n            Some(fun(None))\n        }\n    }\n}\n\nimpl<T> GetUntracked for Option<T>\nwhere\n    Self: DefinedAt,\n    T: GetUntracked,\n{\n    type Value = Option<<T as GetUntracked>::Value>;\n\n    fn try_get_untracked(&self) -> Option<Self::Value> {\n        Some(if let Some(signal) = self {\n            Some(signal.try_get_untracked()?)\n        } else {\n            None\n        })\n    }\n}\n\nimpl<T> Get for Option<T>\nwhere\n    Self: DefinedAt,\n    T: Get,\n{\n    type Value = Option<<T as Get>::Value>;\n\n    fn try_get(&self) -> Option<Self::Value> {\n        Some(if let Some(signal) = self {\n            Some(signal.try_get()?)\n        } else {\n            None\n        })\n    }\n}\n\n/// Helper trait to implement flatten() on `Option<&Option<T>>`.\npub trait FlattenOptionRefOption {\n    /// The type of the value contained in the double option.\n    type Value;\n\n    /// Converts from `Option<&Option<T>>` to `Option<&T>`.\n    fn flatten(&self) -> Option<&Self::Value>;\n}\n\nimpl<'a, T> FlattenOptionRefOption for Option<&'a Option<T>> {\n    type Value = T;\n\n    fn flatten(&self) -> Option<&'a T> {\n        self.map(Option::as_ref).flatten()\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/traits.rs",
    "content": "//! A series of traits to implement the behavior of reactive primitive, especially signals.\n//!\n//! ## Principles\n//! 1. **Composition**: Most of the traits are implemented as combinations of more primitive base traits,\n//!    and blanket implemented for all types that implement those traits.\n//! 2. **Fallibility**: Most traits includes a `try_` variant, which returns `None` if the method\n//!    fails (e.g., if signals are arena allocated and this can't be found, or if an `RwLock` is\n//!    poisoned).\n//!\n//! ## Metadata Traits\n//! - [`DefinedAt`] is used for debugging in the case of errors and should be implemented for all\n//!   signal types.\n//! - [`IsDisposed`] checks whether a signal is currently accessible.\n//!\n//! ## Base Traits\n//! | Trait             | Mode  | Description                                                                           |\n//! |-------------------|-------|---------------------------------------------------------------------------------------|\n//! | [`Track`]         | —     | Tracks changes to this value, adding it as a source of the current reactive observer. |\n//! | [`Notify`]       | —      | Notifies subscribers that this value has changed.                                     |\n//! | [`ReadUntracked`] | Guard | Gives immutable access to the value of this signal.                                   |\n//! | [`Write`]     | Guard | Gives mutable access to the value of this signal.\n//!\n//! ## Derived Traits\n//!\n//! ### Access\n//! | Trait             | Mode          | Composition                   | Description\n//! |-------------------|---------------|-------------------------------|------------\n//! | [`WithUntracked`] | `fn(&T) -> U` | [`ReadUntracked`]                  | Applies closure to the current value of the signal and returns result.\n//! | [`With`]          | `fn(&T) -> U` | [`ReadUntracked`] + [`Track`]      | Applies closure to the current value of the signal and returns result, with reactive tracking.\n//! | [`GetUntracked`]  | `T`           | [`WithUntracked`] + [`Clone`] | Clones the current value of the signal.\n//! | [`Get`]           | `T`           | [`GetUntracked`] + [`Track`]  | Clones the current value of the signal, with reactive tracking.\n//!\n//! ### Update\n//! | Trait               | Mode          | Composition                       | Description\n//! |---------------------|---------------|-----------------------------------|------------\n//! | [`UpdateUntracked`] | `fn(&mut T)`  | [`Write`]                     | Applies closure to the current value to update it, but doesn't notify subscribers.\n//! | [`Update`]          | `fn(&mut T)`  | [`UpdateUntracked`] + [`Notify`] | Applies closure to the current value to update it, and notifies subscribers.\n//! | [`Set`]             | `T`           | [`Update`]                        | Sets the value to a new value, and notifies subscribers.\n//!\n//! ## Using the Traits\n//!\n//! These traits are designed so that you can implement as few as possible, and the rest will be\n//! implemented automatically.\n//!\n//! For example, if you have a struct for which you can implement [`ReadUntracked`] and [`Track`], then\n//! [`WithUntracked`] and [`With`] will be implemented automatically (as will [`GetUntracked`] and\n//! [`Get`] for `Clone` types). But if you cannot implement [`ReadUntracked`] (because, for example,\n//! there isn't an `RwLock` so you can't wrap in a [`ReadGuard`](crate::signal::guards::ReadGuard),\n//! but you can still implement [`WithUntracked`] and [`Track`], the same traits will still be implemented.\n\npub use crate::trait_options::*;\nuse crate::{\n    effect::Effect,\n    graph::{Observer, Source, Subscriber, ToAnySource},\n    owner::Owner,\n    signal::{arc_signal, guards::UntrackedWriteGuard, ArcReadSignal},\n};\nuse any_spawner::Executor;\nuse futures::{Stream, StreamExt};\nuse std::{\n    ops::{Deref, DerefMut},\n    panic::Location,\n};\n\n#[doc(hidden)]\n/// Provides a sensible panic message for accessing disposed signals.\n#[macro_export]\nmacro_rules! unwrap_signal {\n    ($signal:ident) => {{\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let location = std::panic::Location::caller();\n        || {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            {\n                panic!(\n                    \"{}\",\n                    $crate::traits::panic_getting_disposed_signal(\n                        $signal.defined_at(),\n                        location\n                    )\n                );\n            }\n            #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n            {\n                panic!(\n                    \"Tried to access a reactive value that has already been \\\n                     disposed.\"\n                );\n            }\n        }\n    }};\n}\n\n/// Allows disposing an arena-allocated signal before its owner has been disposed.\npub trait Dispose {\n    /// Disposes of the signal. This:\n    /// 1. Detaches the signal from the reactive graph, preventing it from triggering\n    ///    further updates; and\n    /// 2. Drops the value contained in the signal.\n    fn dispose(self);\n}\n\n/// Allows tracking the value of some reactive data.\npub trait Track {\n    /// Subscribes to this signal in the current reactive scope without doing anything with its value.\n    #[track_caller]\n    fn track(&self);\n}\n\nimpl<T: Source + ToAnySource + DefinedAt> Track for T {\n    #[track_caller]\n    fn track(&self) {\n        if self.is_disposed() {\n            return;\n        }\n\n        if let Some(subscriber) = Observer::get() {\n            subscriber.add_source(self.to_any_source());\n            self.add_subscriber(subscriber);\n        } else {\n            #[cfg(all(debug_assertions, feature = \"effects\"))]\n            {\n                use crate::diagnostics::SpecialNonReactiveZone;\n\n                if !SpecialNonReactiveZone::is_inside() {\n                    let called_at = Location::caller();\n                    let ty = std::any::type_name::<T>();\n                    let defined_at = self\n                        .defined_at()\n                        .map(ToString::to_string)\n                        .unwrap_or_else(|| String::from(\"{unknown}\"));\n                    crate::log_warning(format_args!(\n                        \"At {called_at}, you access a {ty} (defined at \\\n                         {defined_at}) outside a reactive tracking context. \\\n                         This might mean your app is not responding to \\\n                         changes in signal values in the way you \\\n                         expect.\\n\\nHere’s how to fix it:\\n\\n1. If this is \\\n                         inside a `view!` macro, make sure you are passing a \\\n                         function, not a value.\\n  ❌ NO  <p>{{x.get() * \\\n                         2}}</p>\\n  ✅ YES <p>{{move || x.get() * \\\n                         2}}</p>\\n\\n2. If it’s in the body of a component, \\\n                         try wrapping this access in a closure: \\n  ❌ NO  \\\n                         let y = x.get() * 2\\n  ✅ YES let y = move || \\\n                         x.get() * 2.\\n\\n3. If you’re *trying* to access the \\\n                         value without tracking, use `.get_untracked()` or \\\n                         `.with_untracked()` instead.\"\n                    ));\n                }\n            }\n        }\n    }\n}\n\n/// Give read-only access to a signal's value by reference through a guard type,\n/// without tracking the value reactively.\npub trait ReadUntracked: Sized + DefinedAt {\n    /// The guard type that will be returned, which can be dereferenced to the value.\n    type Value: Deref;\n\n    /// Returns the guard, or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_read_untracked(&self) -> Option<Self::Value>;\n\n    /// Returns the guard.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn read_untracked(&self) -> Self::Value {\n        self.try_read_untracked()\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n\n    /// This is a backdoor to allow overriding the [`Read::try_read`] implementation despite it being auto implemented.\n    ///\n    /// If your type contains a [`Signal`](crate::wrappers::read::Signal),\n    /// call it's [`ReadUntracked::custom_try_read`] here, else return `None`.\n    #[track_caller]\n    fn custom_try_read(&self) -> Option<Option<Self::Value>> {\n        None\n    }\n}\n\n/// Give read-only access to a signal's value by reference through a guard type,\n/// and subscribes the active reactive observer (an effect or computed) to changes in its value.\npub trait Read: DefinedAt {\n    /// The guard type that will be returned, which can be dereferenced to the value.\n    type Value: Deref;\n\n    /// Subscribes to the signal, and returns the guard, or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_read(&self) -> Option<Self::Value>;\n\n    /// Subscribes to the signal, and returns the guard.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn read(&self) -> Self::Value {\n        self.try_read().unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> Read for T\nwhere\n    T: Track + ReadUntracked,\n{\n    type Value = T::Value;\n\n    fn try_read(&self) -> Option<Self::Value> {\n        // The [`Read`] trait is auto implemented for types that implement [`ReadUntracked`] + [`Track`]. The [`Read`] trait then auto implements the [`With`] and [`Get`] traits too.\n        //\n        // This is a problem for e.g. the [`Signal`](crate::wrappers::read::Signal) type,\n        // this type must use a custom [`Read::try_read`] implementation to avoid an unnecessary clone.\n        //\n        // This is a backdoor to allow overriding the [`Read::try_read`] implementation despite it being auto implemented.\n        if let Some(custom) = self.custom_try_read() {\n            custom\n        } else {\n            self.track();\n            self.try_read_untracked()\n        }\n    }\n}\n\n/// A reactive, mutable guard that can be untracked to prevent it from notifying subscribers when\n/// it is dropped.\npub trait UntrackableGuard: DerefMut {\n    /// Removes the notifier from the guard, such that it will no longer notify subscribers when it is dropped.\n    fn untrack(&mut self);\n}\n\nimpl<T> UntrackableGuard for Box<dyn UntrackableGuard<Target = T>> {\n    fn untrack(&mut self) {\n        (**self).untrack();\n    }\n}\n\n/// Gives mutable access to a signal's value through a guard type. When the guard is dropped, the\n/// signal's subscribers will be notified.\npub trait Write: Sized + DefinedAt + Notify {\n    /// The type of the signal's value.\n    type Value: Sized + 'static;\n\n    /// Returns the guard, or `None` if the signal has already been disposed.\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>>;\n\n    // Returns a guard that will not notify subscribers when dropped,\n    /// or `None` if the signal has already been disposed.\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>>;\n\n    /// Returns the guard.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    fn write(&self) -> impl UntrackableGuard<Target = Self::Value> {\n        self.try_write().unwrap_or_else(unwrap_signal!(self))\n    }\n\n    /// Returns a guard that will not notify subscribers when dropped.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    fn write_untracked(&self) -> impl DerefMut<Target = Self::Value> {\n        self.try_write_untracked()\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\n/// Give read-only access to a signal's value by reference inside a closure,\n/// without tracking the value reactively.\npub trait WithUntracked: DefinedAt {\n    /// The type of the value contained in the signal.\n    type Value: ?Sized;\n\n    /// Applies the closure to the value, and returns the result,\n    /// or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_with_untracked<U>(\n        &self,\n        fun: impl FnOnce(&Self::Value) -> U,\n    ) -> Option<U>;\n\n    /// Applies the closure to the value, and returns the result.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn with_untracked<U>(&self, fun: impl FnOnce(&Self::Value) -> U) -> U {\n        self.try_with_untracked(fun)\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> WithUntracked for T\nwhere\n    T: DefinedAt + ReadUntracked,\n{\n    type Value = <<Self as ReadUntracked>::Value as Deref>::Target;\n\n    fn try_with_untracked<U>(\n        &self,\n        fun: impl FnOnce(&Self::Value) -> U,\n    ) -> Option<U> {\n        self.try_read_untracked().map(|value| fun(&value))\n    }\n}\n\n/// Give read-only access to a signal's value by reference inside a closure,\n/// and subscribes the active reactive observer (an effect or computed) to changes in its value.\npub trait With: DefinedAt {\n    /// The type of the value contained in the signal.\n    type Value: ?Sized;\n\n    /// Subscribes to the signal, applies the closure to the value, and returns the result,\n    /// or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_with<U>(&self, fun: impl FnOnce(&Self::Value) -> U) -> Option<U>;\n\n    /// Subscribes to the signal, applies the closure to the value, and returns the result.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn with<U>(&self, fun: impl FnOnce(&Self::Value) -> U) -> U {\n        self.try_with(fun).unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> With for T\nwhere\n    T: Read,\n{\n    type Value = <<T as Read>::Value as Deref>::Target;\n\n    #[track_caller]\n    fn try_with<U>(&self, fun: impl FnOnce(&Self::Value) -> U) -> Option<U> {\n        self.try_read().map(|val| fun(&val))\n    }\n}\n\n/// Clones the value of the signal, without tracking the value reactively.\npub trait GetUntracked: DefinedAt {\n    /// The type of the value contained in the signal.\n    type Value;\n\n    /// Clones and returns the value of the signal,\n    /// or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_get_untracked(&self) -> Option<Self::Value>;\n\n    /// Clones and returns the value of the signal,\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn get_untracked(&self) -> Self::Value {\n        self.try_get_untracked()\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> GetUntracked for T\nwhere\n    T: WithUntracked,\n    T::Value: Clone,\n{\n    type Value = <Self as WithUntracked>::Value;\n\n    fn try_get_untracked(&self) -> Option<Self::Value> {\n        self.try_with_untracked(Self::Value::clone)\n    }\n}\n\n/// Clones the value of the signal, without tracking the value reactively.\n/// and subscribes the active reactive observer (an effect or computed) to changes in its value.\npub trait Get: DefinedAt {\n    /// The type of the value contained in the signal.\n    type Value: Clone;\n\n    /// Subscribes to the signal, then clones and returns the value of the signal,\n    /// or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_get(&self) -> Option<Self::Value>;\n\n    /// Subscribes to the signal, then clones and returns the value of the signal.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that has been disposed.\n    #[track_caller]\n    fn get(&self) -> Self::Value {\n        self.try_get().unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> Get for T\nwhere\n    T: With,\n    T::Value: Clone,\n{\n    type Value = <T as With>::Value;\n\n    #[track_caller]\n    fn try_get(&self) -> Option<Self::Value> {\n        self.try_with(Self::Value::clone)\n    }\n}\n\n/// Notifies subscribers of a change in this signal.\npub trait Notify {\n    /// Notifies subscribers of a change in this signal.\n    #[track_caller]\n    fn notify(&self);\n}\n\n/// Updates the value of a signal by applying a function that updates it in place,\n/// without notifying subscribers.\npub trait UpdateUntracked: DefinedAt {\n    /// The type of the value contained in the signal.\n    type Value;\n\n    /// Updates the value by applying a function, returning the value returned by that function.\n    /// Does not notify subscribers that the signal has changed.\n    ///\n    /// # Panics\n    /// Panics if you try to update a signal that has been disposed.\n    #[track_caller]\n    fn update_untracked<U>(\n        &self,\n        fun: impl FnOnce(&mut Self::Value) -> U,\n    ) -> U {\n        self.try_update_untracked(fun)\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n\n    /// Updates the value by applying a function, returning the value returned by that function,\n    /// or `None` if the signal has already been disposed.\n    /// Does not notify subscribers that the signal has changed.\n    fn try_update_untracked<U>(\n        &self,\n        fun: impl FnOnce(&mut Self::Value) -> U,\n    ) -> Option<U>;\n}\n\nimpl<T> UpdateUntracked for T\nwhere\n    T: Write,\n{\n    type Value = <Self as Write>::Value;\n\n    #[track_caller]\n    fn try_update_untracked<U>(\n        &self,\n        fun: impl FnOnce(&mut Self::Value) -> U,\n    ) -> Option<U> {\n        let mut guard = self.try_write_untracked()?;\n        Some(fun(&mut *guard))\n    }\n}\n\n/// Updates the value of a signal by applying a function that updates it in place,\n/// notifying its subscribers that the value has changed.\npub trait Update {\n    /// The type of the value contained in the signal.\n    type Value;\n\n    /// Updates the value of the signal and notifies subscribers.\n    #[track_caller]\n    fn update(&self, fun: impl FnOnce(&mut Self::Value)) {\n        self.try_update(fun);\n    }\n\n    /// Updates the value of the signal, but only notifies subscribers if the function\n    /// returns `true`.\n    #[track_caller]\n    fn maybe_update(&self, fun: impl FnOnce(&mut Self::Value) -> bool) {\n        self.try_maybe_update(|val| {\n            let did_update = fun(val);\n            (did_update, ())\n        });\n    }\n\n    /// Updates the value of the signal and notifies subscribers, returning the value that is\n    /// returned by the update function, or `None` if the signal has already been disposed.\n    #[track_caller]\n    fn try_update<U>(\n        &self,\n        fun: impl FnOnce(&mut Self::Value) -> U,\n    ) -> Option<U> {\n        self.try_maybe_update(|val| (true, fun(val)))\n    }\n\n    /// Updates the value of the signal, notifying subscribers if the update function returns\n    /// `(true, _)`, and returns the value returned by the update function,\n    /// or `None` if the signal has already been disposed.\n    fn try_maybe_update<U>(\n        &self,\n        fun: impl FnOnce(&mut Self::Value) -> (bool, U),\n    ) -> Option<U>;\n}\n\nimpl<T> Update for T\nwhere\n    T: Write,\n{\n    type Value = <Self as Write>::Value;\n\n    #[track_caller]\n    fn try_maybe_update<U>(\n        &self,\n        fun: impl FnOnce(&mut Self::Value) -> (bool, U),\n    ) -> Option<U> {\n        let mut lock = self.try_write()?;\n        let (did_update, val) = fun(&mut *lock);\n        if !did_update {\n            lock.untrack();\n        }\n        drop(lock);\n        Some(val)\n    }\n}\n\n/// Updates the value of the signal by replacing it.\npub trait Set {\n    /// The type of the value contained in the signal.\n    type Value;\n\n    /// Updates the value by replacing it, and notifies subscribers that it has changed.\n    fn set(&self, value: Self::Value);\n\n    /// Updates the value by replacing it, and notifies subscribers that it has changed.\n    ///\n    /// If the signal has already been disposed, returns `Some(value)` with the value that was\n    /// passed in. Otherwise, returns `None`.\n    fn try_set(&self, value: Self::Value) -> Option<Self::Value>;\n}\n\nimpl<T> Set for T\nwhere\n    T: Update + IsDisposed,\n{\n    type Value = <Self as Update>::Value;\n\n    #[track_caller]\n    fn set(&self, value: Self::Value) {\n        let failed = self.try_update(|n| *n = value).is_none();\n\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        if failed && !self.is_disposed() {\n            let called_at = Location::caller();\n            let ty = std::any::type_name::<Self::Value>();\n\n            crate::log_warning(format_args!(\n                \"At {called_at}, you tried to update a {ty}, but the update \\\n                 failed. This can happen if a read guard over the value is \\\n                 still alive.\"\n            ));\n        };\n    }\n\n    #[track_caller]\n    fn try_set(&self, value: Self::Value) -> Option<Self::Value> {\n        if self.is_disposed() {\n            Some(value)\n        } else {\n            self.set(value);\n            None\n        }\n    }\n}\n\n/// Allows converting a signal into an async [`Stream`].\npub trait ToStream<T> {\n    /// Generates a [`Stream`] that emits the new value of the signal\n    /// whenever it changes.\n    ///\n    /// # Panics\n    /// Panics if you try to access a signal that is owned by a reactive node that has been disposed.\n    #[track_caller]\n    fn to_stream(&self) -> impl Stream<Item = T> + Send;\n}\n\nimpl<S> ToStream<S::Value> for S\nwhere\n    S: Clone + Get + Send + Sync + 'static,\n    S::Value: Send + 'static,\n{\n    fn to_stream(&self) -> impl Stream<Item = S::Value> + Send {\n        let (tx, rx) = futures::channel::mpsc::unbounded();\n\n        let close_channel = tx.clone();\n\n        Owner::on_cleanup(move || close_channel.close_channel());\n\n        Effect::new_isomorphic({\n            let this = self.clone();\n            move |_| {\n                let _ = tx.unbounded_send(this.get());\n            }\n        });\n\n        rx\n    }\n}\n\n/// Allows creating a signal from an async [`Stream`].\npub trait FromStream<T> {\n    /// Creates a signal that contains the latest value of the stream.\n    #[track_caller]\n    fn from_stream(stream: impl Stream<Item = T> + Send + 'static) -> Self;\n\n    /// Creates a signal that contains the latest value of the stream.\n    #[track_caller]\n    fn from_stream_unsync(stream: impl Stream<Item = T> + 'static) -> Self;\n}\n\nimpl<S, T> FromStream<T> for S\nwhere\n    S: From<ArcReadSignal<Option<T>>> + Send + Sync,\n    T: Send + Sync + 'static,\n{\n    fn from_stream(stream: impl Stream<Item = T> + Send + 'static) -> Self {\n        let (read, write) = arc_signal(None);\n        let mut stream = Box::pin(stream);\n        crate::spawn(async move {\n            while let Some(value) = stream.next().await {\n                write.set(Some(value));\n            }\n        });\n        read.into()\n    }\n\n    fn from_stream_unsync(stream: impl Stream<Item = T> + 'static) -> Self {\n        let (read, write) = arc_signal(None);\n        let mut stream = Box::pin(stream);\n        Executor::spawn_local(async move {\n            while let Some(value) = stream.next().await {\n                write.set(Some(value));\n            }\n        });\n        read.into()\n    }\n}\n\n/// Checks whether a signal has already been disposed.\npub trait IsDisposed {\n    /// If `true`, the signal cannot be accessed without a panic.\n    fn is_disposed(&self) -> bool;\n}\n\n/// Turns a signal back into a raw value.\npub trait IntoInner {\n    /// The type of the value contained in the signal.\n    type Value;\n\n    /// Returns the inner value if this is the only reference to the signal.\n    /// Otherwise, returns `None` and drops this reference.\n    /// # Panics\n    /// Panics if the inner lock is poisoned.\n    fn into_inner(self) -> Option<Self::Value>;\n}\n\n/// Describes where the signal was defined. This is used for diagnostic warnings and is purely a\n/// debug-mode tool.\npub trait DefinedAt {\n    /// Returns the location at which the signal was defined. This is usually simply `None` in\n    /// release mode.\n    fn defined_at(&self) -> Option<&'static Location<'static>>;\n}\n\n#[doc(hidden)]\npub fn panic_getting_disposed_signal(\n    defined_at: Option<&'static Location<'static>>,\n    location: &'static Location<'static>,\n) -> String {\n    if let Some(defined_at) = defined_at {\n        format!(\n            \"At {location}, you tried to access a reactive value which was \\\n             defined at {defined_at}, but it has already been disposed.\"\n        )\n    } else {\n        format!(\n            \"At {location}, you tried to access a reactive value, but it has \\\n             already been disposed.\"\n        )\n    }\n}\n\n/// A variation of the [`Read`] trait that provides a signposted \"always-non-reactive\" API.\n/// E.g. for [`StoredValue`](`crate::owner::StoredValue`).\npub trait ReadValue: Sized + DefinedAt {\n    /// The guard type that will be returned, which can be dereferenced to the value.\n    type Value: Deref;\n\n    /// Returns the non-reactive guard, or `None` if the value has already been disposed.\n    #[track_caller]\n    fn try_read_value(&self) -> Option<Self::Value>;\n\n    /// Returns the non-reactive guard.\n    ///\n    /// # Panics\n    /// Panics if you try to access a value that has been disposed.\n    #[track_caller]\n    fn read_value(&self) -> Self::Value {\n        self.try_read_value().unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\n/// A variation of the [`With`] trait that provides a signposted \"always-non-reactive\" API.\n/// E.g. for [`StoredValue`](`crate::owner::StoredValue`).\npub trait WithValue: DefinedAt {\n    /// The type of the value contained in the value.\n    type Value: ?Sized;\n\n    /// Applies the closure to the value, non-reactively, and returns the result,\n    /// or `None` if the value has already been disposed.\n    #[track_caller]\n    fn try_with_value<U>(\n        &self,\n        fun: impl FnOnce(&Self::Value) -> U,\n    ) -> Option<U>;\n\n    /// Applies the closure to the value, non-reactively, and returns the result.\n    ///\n    /// # Panics\n    /// Panics if you try to access a value that has been disposed.\n    #[track_caller]\n    fn with_value<U>(&self, fun: impl FnOnce(&Self::Value) -> U) -> U {\n        self.try_with_value(fun)\n            .unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> WithValue for T\nwhere\n    T: DefinedAt + ReadValue,\n{\n    type Value = <<Self as ReadValue>::Value as Deref>::Target;\n\n    fn try_with_value<U>(\n        &self,\n        fun: impl FnOnce(&Self::Value) -> U,\n    ) -> Option<U> {\n        self.try_read_value().map(|value| fun(&value))\n    }\n}\n\n/// A variation of the [`Get`] trait that provides a signposted \"always-non-reactive\" API.\n/// E.g. for [`StoredValue`](`crate::owner::StoredValue`).\npub trait GetValue: DefinedAt {\n    /// The type of the value contained in the value.\n    type Value: Clone;\n\n    /// Clones and returns the value of the value, non-reactively,\n    /// or `None` if the value has already been disposed.\n    #[track_caller]\n    fn try_get_value(&self) -> Option<Self::Value>;\n\n    /// Clones and returns the value of the value, non-reactively.\n    ///\n    /// # Panics\n    /// Panics if you try to access a value that has been disposed.\n    #[track_caller]\n    fn get_value(&self) -> Self::Value {\n        self.try_get_value().unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\nimpl<T> GetValue for T\nwhere\n    T: WithValue,\n    T::Value: Clone,\n{\n    type Value = <Self as WithValue>::Value;\n\n    fn try_get_value(&self) -> Option<Self::Value> {\n        self.try_with_value(Self::Value::clone)\n    }\n}\n\n/// A variation of the [`Write`] trait that provides a signposted \"always-non-reactive\" API.\n/// E.g. for [`StoredValue`](`crate::owner::StoredValue`).\npub trait WriteValue: Sized + DefinedAt {\n    /// The type of the value's value.\n    type Value: Sized + 'static;\n\n    /// Returns a non-reactive write guard, or `None` if the value has already been disposed.\n    #[track_caller]\n    fn try_write_value(&self) -> Option<UntrackedWriteGuard<Self::Value>>;\n\n    /// Returns a non-reactive write guard.\n    ///\n    /// # Panics\n    /// Panics if you try to access a value that has been disposed.\n    #[track_caller]\n    fn write_value(&self) -> UntrackedWriteGuard<Self::Value> {\n        self.try_write_value().unwrap_or_else(unwrap_signal!(self))\n    }\n}\n\n/// A variation of the [`Update`] trait that provides a signposted \"always-non-reactive\" API.\n/// E.g. for [`StoredValue`](`crate::owner::StoredValue`).\npub trait UpdateValue: DefinedAt {\n    /// The type of the value contained in the value.\n    type Value;\n\n    /// Updates the value, returning the value that is\n    /// returned by the update function, or `None` if the value has already been disposed.\n    #[track_caller]\n    fn try_update_value<U>(\n        &self,\n        fun: impl FnOnce(&mut Self::Value) -> U,\n    ) -> Option<U>;\n\n    /// Updates the value.\n    #[track_caller]\n    fn update_value(&self, fun: impl FnOnce(&mut Self::Value)) {\n        self.try_update_value(fun);\n    }\n}\n\nimpl<T> UpdateValue for T\nwhere\n    T: WriteValue,\n{\n    type Value = <Self as WriteValue>::Value;\n\n    #[track_caller]\n    fn try_update_value<U>(\n        &self,\n        fun: impl FnOnce(&mut Self::Value) -> U,\n    ) -> Option<U> {\n        let mut guard = self.try_write_value()?;\n        Some(fun(&mut *guard))\n    }\n}\n\n/// A variation of the [`Set`] trait that provides a signposted \"always-non-reactive\" API.\n/// E.g. for [`StoredValue`](`crate::owner::StoredValue`).\npub trait SetValue: DefinedAt {\n    /// The type of the value contained in the value.\n    type Value;\n\n    /// Updates the value by replacing it, non-reactively.\n    ///\n    /// If the value has already been disposed, returns `Some(value)` with the value that was\n    /// passed in. Otherwise, returns `None`.\n    #[track_caller]\n    fn try_set_value(&self, value: Self::Value) -> Option<Self::Value>;\n\n    /// Updates the value by replacing it, non-reactively.\n    #[track_caller]\n    fn set_value(&self, value: Self::Value) {\n        self.try_set_value(value);\n    }\n}\n\nimpl<T> SetValue for T\nwhere\n    T: WriteValue,\n{\n    type Value = <Self as WriteValue>::Value;\n\n    fn try_set_value(&self, value: Self::Value) -> Option<Self::Value> {\n        // Unlike most other traits, for these None actually means success:\n        if let Some(mut guard) = self.try_write_value() {\n            *guard = value;\n            None\n        } else {\n            Some(value)\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/transition.rs",
    "content": "//! Utilities to wait for asynchronous primitives to resolve.\n\nuse futures::{channel::oneshot, future::join_all};\nuse or_poisoned::OrPoisoned;\nuse std::{\n    future::Future,\n    sync::{mpsc, OnceLock, RwLock},\n};\n\nstatic TRANSITION: OnceLock<RwLock<Option<TransitionInner>>> = OnceLock::new();\n\nfn global_transition() -> &'static RwLock<Option<TransitionInner>> {\n    TRANSITION.get_or_init(|| RwLock::new(None))\n}\n\n#[derive(Debug, Clone)]\nstruct TransitionInner {\n    tx: mpsc::Sender<oneshot::Receiver<()>>,\n}\n\n/// Transitions allow you to wait for all asynchronous resources created during them to resolve.\n#[derive(Debug)]\npub struct AsyncTransition;\n\nimpl AsyncTransition {\n    /// Calls the `action` function, and returns a `Future` that resolves when any\n    /// [`AsyncDerived`](crate::computed::AsyncDerived) or\n    /// or [`ArcAsyncDerived`](crate::computed::ArcAsyncDerived) that is read during the action\n    /// has resolved.\n    ///\n    /// This allows for an inversion of control: the caller does not need to know when all the\n    /// resources created inside the `action` will resolve, but can wait for them to notify it.\n    pub async fn run<T, U>(action: impl FnOnce() -> T) -> U\n    where\n        T: Future<Output = U>,\n    {\n        let (tx, rx) = mpsc::channel();\n        let global_transition = global_transition();\n        let inner = TransitionInner { tx };\n        let prev = Option::replace(\n            &mut *global_transition.write().or_poisoned(),\n            inner.clone(),\n        );\n        let value = action().await;\n        _ = std::mem::replace(\n            &mut *global_transition.write().or_poisoned(),\n            prev,\n        );\n        let mut pending = Vec::new();\n        while let Ok(tx) = rx.try_recv() {\n            pending.push(tx);\n        }\n        join_all(pending).await;\n        value\n    }\n\n    pub(crate) fn register(rx: oneshot::Receiver<()>) {\n        if let Some(tx) = global_transition()\n            .read()\n            .or_poisoned()\n            .as_ref()\n            .map(|n| &n.tx)\n        {\n            // if it's an Err, that just means the Receiver was dropped\n            // i.e., the transition is no longer listening, in which case it doesn't matter if we\n            // successfully register with it or not\n            _ = tx.send(rx);\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_graph/src/wrappers.rs",
    "content": "//! Types to abstract over different kinds of readable or writable reactive values.\n\n/// Types that abstract over signals with values that can be read.\npub mod read {\n    use crate::{\n        computed::{ArcMemo, Memo},\n        graph::untrack,\n        owner::{\n            ArcStoredValue, ArenaItem, FromLocal, LocalStorage, Storage,\n            SyncStorage,\n        },\n        signal::{\n            guards::{Mapped, Plain, ReadGuard},\n            ArcMappedSignal, ArcReadSignal, ArcRwSignal, MappedSignal,\n            ReadSignal, RwSignal,\n        },\n        traits::{\n            DefinedAt, Dispose, Get, Read, ReadUntracked, ReadValue, Track,\n        },\n        unwrap_signal,\n    };\n    use send_wrapper::SendWrapper;\n    use std::{\n        borrow::Borrow,\n        fmt::Display,\n        ops::Deref,\n        panic::Location,\n        sync::{Arc, RwLock},\n    };\n\n    /// Possibilities for the inner type of a [`Signal`].\n    pub enum SignalTypes<T, S = SyncStorage>\n    where\n        S: Storage<T>,\n    {\n        /// A readable signal.\n        ReadSignal(ArcReadSignal<T>),\n        /// A memo.\n        Memo(ArcMemo<T, S>),\n        /// A derived signal.\n        DerivedSignal(Arc<dyn Fn() -> T + Send + Sync>),\n        /// A static, stored value.\n        Stored(ArcStoredValue<T>),\n    }\n\n    impl<T, S> Clone for SignalTypes<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn clone(&self) -> Self {\n            match self {\n                Self::ReadSignal(arg0) => Self::ReadSignal(arg0.clone()),\n                Self::Memo(arg0) => Self::Memo(arg0.clone()),\n                Self::DerivedSignal(arg0) => Self::DerivedSignal(arg0.clone()),\n                Self::Stored(arg0) => Self::Stored(arg0.clone()),\n            }\n        }\n    }\n\n    impl<T, S> core::fmt::Debug for SignalTypes<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n            match self {\n                Self::ReadSignal(arg0) => {\n                    f.debug_tuple(\"ReadSignal\").field(arg0).finish()\n                }\n                Self::Memo(arg0) => f.debug_tuple(\"Memo\").field(arg0).finish(),\n                Self::DerivedSignal(_) => {\n                    f.debug_tuple(\"DerivedSignal\").finish()\n                }\n                Self::Stored(arg0) => {\n                    f.debug_tuple(\"Static\").field(arg0).finish()\n                }\n            }\n        }\n    }\n\n    impl<T, S> PartialEq for SignalTypes<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn eq(&self, other: &Self) -> bool {\n            match (self, other) {\n                (Self::ReadSignal(l0), Self::ReadSignal(r0)) => l0 == r0,\n                (Self::Memo(l0), Self::Memo(r0)) => l0 == r0,\n                (Self::DerivedSignal(l0), Self::DerivedSignal(r0)) => {\n                    std::ptr::eq(l0, r0)\n                }\n                _ => false,\n            }\n        }\n    }\n\n    /// A wrapper for any kind of reference-counted reactive signal:\n    /// an [`ArcReadSignal`], [`ArcMemo`], [`ArcRwSignal`], or derived signal closure,\n    /// or a plain value of the same type\n    ///\n    /// This allows you to create APIs that take `T` or any reactive value that returns `T`\n    /// as an argument, rather than adding a generic `F: Fn() -> T`.\n    ///\n    /// Values can be accessed with the same function call, `read()`, `with()`, and `get()`\n    /// APIs as other signals.\n    ///\n    /// ## Important Notes about Derived Signals\n    ///\n    /// `Signal::derive()` is simply a way to box and type-erase a “derived signal,” which\n    /// is a plain closure that accesses one or more signals. It does *not* cache the value\n    /// of that computation. Accessing the value of a `Signal<_>` that is created using `Signal::derive()`\n    /// will run the closure again every time you call `.read()`, `.with()`, or `.get()`.\n    ///\n    /// If you want the closure to run the minimal number of times necessary to update its state,\n    /// and then to cache its value, you should use a [`Memo`] (and convert it into a `Signal<_>`)\n    /// rather than using `Signal::derive()`.\n    ///\n    /// Note that for many computations, it is nevertheless less expensive to use a derived signal\n    /// than to create a separate memo and to cache the value: creating a new reactive node and\n    /// taking the lock on that cached value whenever you access the signal is *more* expensive than\n    /// simply re-running the calculation in many cases.\n    pub struct ArcSignal<T: 'static, S = SyncStorage>\n    where\n        S: Storage<T>,\n    {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        defined_at: &'static Location<'static>,\n        inner: SignalTypes<T, S>,\n    }\n\n    impl<T, S> Clone for ArcSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn clone(&self) -> Self {\n            Self {\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: self.defined_at,\n                inner: self.inner.clone(),\n            }\n        }\n    }\n\n    impl<T, S> core::fmt::Debug for ArcSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n            let mut s = f.debug_struct(\"ArcSignal\");\n            s.field(\"inner\", &self.inner);\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            s.field(\"defined_at\", &self.defined_at);\n            s.finish()\n        }\n    }\n\n    impl<T, S> Eq for ArcSignal<T, S> where S: Storage<T> {}\n\n    impl<T, S> PartialEq for ArcSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn eq(&self, other: &Self) -> bool {\n            self.inner == other.inner\n        }\n    }\n\n    impl<T> ArcSignal<T, SyncStorage>\n    where\n        SyncStorage: Storage<T>,\n    {\n        /// Wraps a derived signal, i.e., any computation that accesses one or more\n        /// reactive signals.\n        /// ```rust\n        /// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n        /// # use reactive_graph::wrappers::read::ArcSignal;\n        /// # use reactive_graph::prelude::*;\n        /// let (count, set_count) = arc_signal(2);\n        /// let double_count = ArcSignal::derive({\n        ///     let count = count.clone();\n        ///     move || count.get() * 2\n        /// });\n        ///\n        /// // this function takes any kind of wrapped signal\n        /// fn above_3(arg: &ArcSignal<i32>) -> bool {\n        ///     arg.get() > 3\n        /// }\n        ///\n        /// assert_eq!(above_3(&count.into()), false);\n        /// assert_eq!(above_3(&double_count), true);\n        /// ```\n        #[track_caller]\n        pub fn derive(\n            derived_signal: impl Fn() -> T + Send + Sync + 'static,\n        ) -> Self {\n            #[cfg(feature = \"tracing\")]\n            let span = ::tracing::Span::current();\n\n            let derived_signal = move || {\n                #[cfg(feature = \"tracing\")]\n                let _guard = span.enter();\n                derived_signal()\n            };\n\n            Self {\n                inner: SignalTypes::DerivedSignal(Arc::new(derived_signal)),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n\n        /// Moves a static, nonreactive value into a signal, backed by [`ArcStoredValue`].\n        #[track_caller]\n        pub fn stored(value: T) -> Self {\n            Self {\n                inner: SignalTypes::Stored(ArcStoredValue::new(value)),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> ArcSignal<T, LocalStorage>\n    where\n        T: 'static,\n    {\n        /// Wraps a derived signal. Works like [`Signal::derive`] but uses [`LocalStorage`].\n        #[track_caller]\n        pub fn derive_local(derived_signal: impl Fn() -> T + 'static) -> Self {\n            Signal::derive_local(derived_signal).into()\n        }\n\n        /// Moves a static, nonreactive value into a signal, backed by [`ArcStoredValue`].\n        /// Works like [`Signal::stored`] but uses [`LocalStorage`].\n        #[track_caller]\n        pub fn stored_local(value: T) -> Self {\n            Signal::stored_local(value).into()\n        }\n    }\n\n    impl<T> Default for ArcSignal<T, SyncStorage>\n    where\n        T: Default + Send + Sync + 'static,\n    {\n        fn default() -> Self {\n            Self::stored(Default::default())\n        }\n    }\n\n    impl<T: Send + Sync> From<ArcReadSignal<T>> for ArcSignal<T, SyncStorage> {\n        #[track_caller]\n        fn from(value: ArcReadSignal<T>) -> Self {\n            Self {\n                inner: SignalTypes::ReadSignal(value),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T, S> From<ReadSignal<T, S>> for ArcSignal<T, S>\n    where\n        S: Storage<ArcReadSignal<T>> + Storage<T>,\n    {\n        #[track_caller]\n        fn from(value: ReadSignal<T, S>) -> Self {\n            Self {\n                inner: SignalTypes::ReadSignal(value.into()),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T: Send + Sync> From<ArcRwSignal<T>> for ArcSignal<T, SyncStorage> {\n        #[track_caller]\n        fn from(value: ArcRwSignal<T>) -> Self {\n            Self {\n                inner: SignalTypes::ReadSignal(value.read_only()),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T, S> From<RwSignal<T, S>> for ArcSignal<T, S>\n    where\n        S: Storage<ArcRwSignal<T>> + Storage<ArcReadSignal<T>> + Storage<T>,\n    {\n        #[track_caller]\n        fn from(value: RwSignal<T, S>) -> Self {\n            Self {\n                inner: SignalTypes::ReadSignal(value.read_only().into()),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T, S> From<ArcMemo<T, S>> for ArcSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        #[track_caller]\n        fn from(value: ArcMemo<T, S>) -> Self {\n            Self {\n                inner: SignalTypes::Memo(value),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T, S> From<Memo<T, S>> for ArcSignal<T, S>\n    where\n        S: Storage<ArcMemo<T, S>> + Storage<T>,\n    {\n        #[track_caller]\n        fn from(value: Memo<T, S>) -> Self {\n            Self {\n                inner: SignalTypes::Memo(value.into()),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<S> From<&'static str> for ArcSignal<String, S>\n    where\n        S: Storage<&'static str> + Storage<String>,\n    {\n        #[track_caller]\n        fn from(value: &'static str) -> Self {\n            Self {\n                inner: SignalTypes::Stored(ArcStoredValue::new(\n                    value.to_string(),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T, S> DefinedAt for ArcSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn defined_at(&self) -> Option<&'static Location<'static>> {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            {\n                Some(self.defined_at)\n            }\n            #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n            {\n                None\n            }\n        }\n    }\n\n    impl<T, S> Track for ArcSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn track(&self) {\n            match &self.inner {\n                SignalTypes::ReadSignal(i) => {\n                    i.track();\n                }\n                SignalTypes::Memo(i) => {\n                    i.track();\n                }\n                SignalTypes::DerivedSignal(i) => {\n                    i();\n                }\n                // Doesn't change.\n                SignalTypes::Stored(_) => {}\n            }\n        }\n    }\n\n    impl<T, S> ReadUntracked for ArcSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        type Value = ReadGuard<T, SignalReadGuard<T, S>>;\n\n        fn try_read_untracked(&self) -> Option<Self::Value> {\n            match &self.inner {\n                SignalTypes::ReadSignal(i) => {\n                    i.try_read_untracked().map(SignalReadGuard::Read)\n                }\n                SignalTypes::Memo(i) => {\n                    i.try_read_untracked().map(SignalReadGuard::Memo)\n                }\n                SignalTypes::DerivedSignal(i) => {\n                    Some(SignalReadGuard::Owned(untrack(|| i())))\n                }\n                SignalTypes::Stored(i) => {\n                    i.try_read_value().map(SignalReadGuard::Read)\n                }\n            }\n            .map(ReadGuard::new)\n        }\n\n        /// Overriding the default auto implemented [`Read::try_read`] to combine read and track,\n        /// to avoid 2 clones and just have 1 in the [`SignalTypes::DerivedSignal`].\n        fn custom_try_read(&self) -> Option<Option<Self::Value>> {\n            Some(\n                match &self.inner {\n                    SignalTypes::ReadSignal(i) => {\n                        i.try_read().map(SignalReadGuard::Read)\n                    }\n                    SignalTypes::Memo(i) => {\n                        i.try_read().map(SignalReadGuard::Memo)\n                    }\n                    SignalTypes::DerivedSignal(i) => {\n                        Some(SignalReadGuard::Owned(i()))\n                    }\n                    SignalTypes::Stored(i) => {\n                        i.try_read_value().map(SignalReadGuard::Read)\n                    }\n                }\n                .map(ReadGuard::new),\n            )\n        }\n    }\n\n    /// A wrapper for any kind of arena-allocated reactive signal:\n    /// a [`ReadSignal`], [`Memo`], [`RwSignal`], or derived signal closure,\n    /// or a plain value of the same type\n    ///\n    /// This allows you to create APIs that take `T` or any reactive value that returns `T`\n    /// as an argument, rather than adding a generic `F: Fn() -> T`.\n    ///\n    /// Values can be accessed with the same function call, `read()`, `with()`, and `get()`\n    /// APIs as other signals.\n    ///\n    /// ## Important Notes about Derived Signals\n    ///\n    /// `Signal::derive()` is simply a way to box and type-erase a “derived signal,” which\n    /// is a plain closure that accesses one or more signals. It does *not* cache the value\n    /// of that computation. Accessing the value of a `Signal<_>` that is created using `Signal::derive()`\n    /// will run the closure again every time you call `.read()`, `.with()`, or `.get()`.\n    ///\n    /// If you want the closure to run the minimal number of times necessary to update its state,\n    /// and then to cache its value, you should use a [`Memo`] (and convert it into a `Signal<_>`)\n    /// rather than using `Signal::derive()`.\n    ///\n    /// Note that for many computations, it is nevertheless less expensive to use a derived signal\n    /// than to create a separate memo and to cache the value: creating a new reactive node and\n    /// taking the lock on that cached value whenever you access the signal is *more* expensive than\n    /// simply re-running the calculation in many cases.\n    pub struct Signal<T, S = SyncStorage>\n    where\n        S: Storage<T>,\n    {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        defined_at: &'static Location<'static>,\n        inner: ArenaItem<SignalTypes<T, S>, S>,\n    }\n\n    impl<T, S> Dispose for Signal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn dispose(self) {\n            self.inner.dispose()\n        }\n    }\n\n    impl<T, S> Clone for Signal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn clone(&self) -> Self {\n            *self\n        }\n    }\n\n    impl<T, S> Copy for Signal<T, S> where S: Storage<T> {}\n\n    impl<T, S> core::fmt::Debug for Signal<T, S>\n    where\n        S: std::fmt::Debug + Storage<T>,\n    {\n        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n            let mut s = f.debug_struct(\"Signal\");\n            s.field(\"inner\", &self.inner);\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            s.field(\"defined_at\", &self.defined_at);\n            s.finish()\n        }\n    }\n\n    impl<T, S> Eq for Signal<T, S> where S: Storage<T> {}\n\n    impl<T, S> PartialEq for Signal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn eq(&self, other: &Self) -> bool {\n            self.inner == other.inner\n        }\n    }\n\n    impl<T, S> DefinedAt for Signal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn defined_at(&self) -> Option<&'static Location<'static>> {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            {\n                Some(self.defined_at)\n            }\n            #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n            {\n                None\n            }\n        }\n    }\n\n    impl<T, S> Track for Signal<T, S>\n    where\n        T: 'static,\n        S: Storage<T> + Storage<SignalTypes<T, S>>,\n    {\n        fn track(&self) {\n            let inner = self\n                .inner\n                // clone the inner Arc type and release the lock\n                // prevents deadlocking if the derived value includes taking a lock on the arena\n                .try_with_value(Clone::clone)\n                .unwrap_or_else(unwrap_signal!(self));\n            match inner {\n                SignalTypes::ReadSignal(i) => {\n                    i.track();\n                }\n                SignalTypes::Memo(i) => {\n                    i.track();\n                }\n                SignalTypes::DerivedSignal(i) => {\n                    i();\n                }\n                // Doesn't change.\n                SignalTypes::Stored(_) => {}\n            }\n        }\n    }\n\n    impl<T, S> ReadUntracked for Signal<T, S>\n    where\n        T: 'static,\n        S: Storage<SignalTypes<T, S>> + Storage<T>,\n    {\n        type Value = ReadGuard<T, SignalReadGuard<T, S>>;\n\n        fn try_read_untracked(&self) -> Option<Self::Value> {\n            self.inner\n                // clone the inner Arc type and release the lock\n                // prevents deadlocking if the derived value includes taking a lock on the arena\n                .try_with_value(Clone::clone)\n                .and_then(|inner| {\n                    match &inner {\n                        SignalTypes::ReadSignal(i) => {\n                            i.try_read_untracked().map(SignalReadGuard::Read)\n                        }\n                        SignalTypes::Memo(i) => {\n                            i.try_read_untracked().map(SignalReadGuard::Memo)\n                        }\n                        SignalTypes::DerivedSignal(i) => {\n                            Some(SignalReadGuard::Owned(untrack(|| i())))\n                        }\n                        SignalTypes::Stored(i) => {\n                            i.try_read_value().map(SignalReadGuard::Read)\n                        }\n                    }\n                    .map(ReadGuard::new)\n                })\n        }\n\n        /// Overriding the default auto implemented [`Read::try_read`] to combine read and track,\n        /// to avoid 2 clones and just have 1 in the [`SignalTypes::DerivedSignal`].\n        fn custom_try_read(&self) -> Option<Option<Self::Value>> {\n            Some(\n                self.inner\n                    // clone the inner Arc type and release the lock\n                    // prevents deadlocking if the derived value includes taking a lock on the arena\n                    .try_with_value(Clone::clone)\n                    .and_then(|inner| {\n                        match &inner {\n                            SignalTypes::ReadSignal(i) => {\n                                i.try_read().map(SignalReadGuard::Read)\n                            }\n                            SignalTypes::Memo(i) => {\n                                i.try_read().map(SignalReadGuard::Memo)\n                            }\n                            SignalTypes::DerivedSignal(i) => {\n                                Some(SignalReadGuard::Owned(i()))\n                            }\n                            SignalTypes::Stored(i) => {\n                                i.try_read_value().map(SignalReadGuard::Read)\n                            }\n                        }\n                        .map(ReadGuard::new)\n                    }),\n            )\n        }\n    }\n\n    impl<T> Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        /// Wraps a derived signal, i.e., any computation that accesses one or more\n        /// reactive signals.\n        /// ```rust\n        /// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n        /// # use reactive_graph::wrappers::read::Signal;\n        /// # use reactive_graph::prelude::*;\n        /// let (count, set_count) = signal(2);\n        /// let double_count = Signal::derive(move || count.get() * 2);\n        ///\n        /// // this function takes any kind of wrapped signal\n        /// fn above_3(arg: &Signal<i32>) -> bool {\n        ///     arg.get() > 3\n        /// }\n        ///\n        /// assert_eq!(above_3(&count.into()), false);\n        /// assert_eq!(above_3(&double_count), true);\n        /// ```\n        #[track_caller]\n        pub fn derive(\n            derived_signal: impl Fn() -> T + Send + Sync + 'static,\n        ) -> Self {\n            #[cfg(feature = \"tracing\")]\n            let span = ::tracing::Span::current();\n\n            let derived_signal = move || {\n                #[cfg(feature = \"tracing\")]\n                let _guard = span.enter();\n                derived_signal()\n            };\n\n            Self {\n                inner: ArenaItem::new_with_storage(SignalTypes::DerivedSignal(\n                    Arc::new(derived_signal),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n\n        /// Moves a static, nonreactive value into a signal, backed by [`ArcStoredValue`].\n        #[track_caller]\n        pub fn stored(value: T) -> Self {\n            Self {\n                inner: ArenaItem::new_with_storage(SignalTypes::Stored(\n                    ArcStoredValue::new(value),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> Signal<T, LocalStorage>\n    where\n        T: 'static,\n    {\n        /// Wraps a derived signal. Works like [`Signal::derive`] but uses [`LocalStorage`].\n        #[track_caller]\n        pub fn derive_local(derived_signal: impl Fn() -> T + 'static) -> Self {\n            let derived_signal = SendWrapper::new(derived_signal);\n            #[cfg(feature = \"tracing\")]\n            let span = ::tracing::Span::current();\n\n            let derived_signal = move || {\n                #[cfg(feature = \"tracing\")]\n                let _guard = span.enter();\n                derived_signal()\n            };\n\n            Self {\n                inner: ArenaItem::new_local(SignalTypes::DerivedSignal(\n                    Arc::new(derived_signal),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n\n        /// Moves a static, nonreactive value into a signal, backed by [`ArcStoredValue`].\n        /// Works like [`Signal::stored`] but uses [`LocalStorage`].\n        #[track_caller]\n        pub fn stored_local(value: T) -> Self {\n            Self {\n                inner: ArenaItem::new_local(SignalTypes::Stored(\n                    ArcStoredValue::new(value),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> Default for Signal<T>\n    where\n        T: Send + Sync + Default + 'static,\n    {\n        fn default() -> Self {\n            Self::stored(Default::default())\n        }\n    }\n\n    impl<T> Default for Signal<T, LocalStorage>\n    where\n        T: Default + 'static,\n    {\n        fn default() -> Self {\n            Self::stored_local(Default::default())\n        }\n    }\n\n    impl<T: Send + Sync + 'static> From<T> for ArcSignal<T, SyncStorage> {\n        #[track_caller]\n        fn from(value: T) -> Self {\n            ArcSignal::stored(value)\n        }\n    }\n\n    impl<T> From<T> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: T) -> Self {\n            Self::stored(value)\n        }\n    }\n\n    impl<T> From<T> for Signal<T, LocalStorage>\n    where\n        T: 'static,\n    {\n        #[track_caller]\n        fn from(value: T) -> Self {\n            Self::stored_local(value)\n        }\n    }\n\n    impl<T> From<ArcSignal<T, SyncStorage>> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ArcSignal<T, SyncStorage>) -> Self {\n            Signal {\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: Location::caller(),\n                inner: ArenaItem::new(value.inner),\n            }\n        }\n    }\n\n    impl<T> FromLocal<ArcSignal<T, LocalStorage>> for Signal<T, LocalStorage>\n    where\n        T: 'static,\n    {\n        #[track_caller]\n        fn from_local(value: ArcSignal<T, LocalStorage>) -> Self {\n            Signal {\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: Location::caller(),\n                inner: ArenaItem::new_local(value.inner),\n            }\n        }\n    }\n\n    impl<T, S> From<Signal<T, S>> for ArcSignal<T, S>\n    where\n        S: Storage<SignalTypes<T, S>> + Storage<T>,\n    {\n        #[track_caller]\n        fn from(value: Signal<T, S>) -> Self {\n            ArcSignal {\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: Location::caller(),\n                inner: value\n                    .inner\n                    .try_get_value()\n                    .unwrap_or_else(unwrap_signal!(value)),\n            }\n        }\n    }\n\n    impl<T> From<ReadSignal<T>> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ReadSignal<T>) -> Self {\n            Self {\n                inner: ArenaItem::new(SignalTypes::ReadSignal(value.into())),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<ReadSignal<T, LocalStorage>> for Signal<T, LocalStorage>\n    where\n        T: 'static,\n    {\n        #[track_caller]\n        fn from(value: ReadSignal<T, LocalStorage>) -> Self {\n            Self {\n                inner: ArenaItem::new_local(SignalTypes::ReadSignal(\n                    value.into(),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<ArcReadSignal<T>> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ArcReadSignal<T>) -> Self {\n            Self {\n                inner: ArenaItem::new(SignalTypes::ReadSignal(value)),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<ArcReadSignal<T>> for Signal<T, LocalStorage>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ArcReadSignal<T>) -> Self {\n            Self {\n                inner: ArenaItem::new_local(SignalTypes::ReadSignal(value)),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<RwSignal<T>> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: RwSignal<T>) -> Self {\n            Self {\n                inner: ArenaItem::new(SignalTypes::ReadSignal(\n                    value.read_only().into(),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<MappedSignal<T>> for Signal<T>\n    where\n        T: Clone + Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: MappedSignal<T>) -> Self {\n            Self::derive(move || value.get())\n        }\n    }\n\n    impl<T> From<RwSignal<T, LocalStorage>> for Signal<T, LocalStorage>\n    where\n        T: 'static,\n    {\n        #[track_caller]\n        fn from(value: RwSignal<T, LocalStorage>) -> Self {\n            Self {\n                inner: ArenaItem::new_local(SignalTypes::ReadSignal(\n                    value.read_only().into(),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<ArcRwSignal<T>> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ArcRwSignal<T>) -> Self {\n            Self {\n                inner: ArenaItem::new(SignalTypes::ReadSignal(\n                    value.read_only(),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<ArcMappedSignal<T>> for Signal<T>\n    where\n        T: Clone + Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ArcMappedSignal<T>) -> Self {\n            MappedSignal::from(value).into()\n        }\n    }\n\n    impl<T> From<ArcRwSignal<T>> for Signal<T, LocalStorage>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ArcRwSignal<T>) -> Self {\n            Self {\n                inner: ArenaItem::new_local(SignalTypes::ReadSignal(\n                    value.read_only(),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<Memo<T>> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: Memo<T>) -> Self {\n            Self {\n                inner: ArenaItem::new(SignalTypes::Memo(value.into())),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<Memo<T, LocalStorage>> for Signal<T, LocalStorage>\n    where\n        T: 'static,\n    {\n        #[track_caller]\n        fn from(value: Memo<T, LocalStorage>) -> Self {\n            Self {\n                inner: ArenaItem::new_local(SignalTypes::Memo(value.into())),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<ArcMemo<T>> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ArcMemo<T>) -> Self {\n            Self {\n                inner: ArenaItem::new(SignalTypes::Memo(value)),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<ArcMemo<T, LocalStorage>> for Signal<T, LocalStorage>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: ArcMemo<T, LocalStorage>) -> Self {\n            Self {\n                inner: ArenaItem::new_local(SignalTypes::Memo(value)),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T> From<T> for Signal<Option<T>>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: T) -> Self {\n            Signal::stored(Some(value))\n        }\n    }\n\n    impl<T> From<T> for Signal<Option<T>, LocalStorage>\n    where\n        T: 'static,\n    {\n        #[track_caller]\n        fn from(value: T) -> Self {\n            Signal::stored_local(Some(value))\n        }\n    }\n\n    impl<T> From<Signal<T>> for Signal<Option<T>>\n    where\n        T: Clone + Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: Signal<T>) -> Self {\n            Signal::derive(move || Some(value.get()))\n        }\n    }\n\n    impl<T> From<Signal<T, LocalStorage>> for Signal<Option<T>, LocalStorage>\n    where\n        T: Clone + 'static,\n    {\n        #[track_caller]\n        fn from(value: Signal<T, LocalStorage>) -> Self {\n            Signal::derive_local(move || Some(value.get()))\n        }\n    }\n\n    impl From<&str> for Signal<String> {\n        #[track_caller]\n        fn from(value: &str) -> Self {\n            Signal::stored(value.to_string())\n        }\n    }\n\n    impl From<&str> for Signal<String, LocalStorage> {\n        #[track_caller]\n        fn from(value: &str) -> Self {\n            Signal::stored_local(value.to_string())\n        }\n    }\n\n    impl From<&str> for Signal<Option<String>> {\n        #[track_caller]\n        fn from(value: &str) -> Self {\n            Signal::stored(Some(value.to_string()))\n        }\n    }\n\n    impl From<&str> for Signal<Option<String>, LocalStorage> {\n        #[track_caller]\n        fn from(value: &str) -> Self {\n            Signal::stored_local(Some(value.to_string()))\n        }\n    }\n\n    impl From<Signal<&'static str>> for Signal<String> {\n        #[track_caller]\n        fn from(value: Signal<&'static str>) -> Self {\n            Signal::derive(move || value.read().to_string())\n        }\n    }\n\n    impl From<Signal<&'static str, LocalStorage>> for Signal<String, LocalStorage> {\n        #[track_caller]\n        fn from(value: Signal<&'static str, LocalStorage>) -> Self {\n            Signal::derive_local(move || value.read().to_string())\n        }\n    }\n\n    impl From<Signal<&'static str>> for Signal<String, LocalStorage> {\n        #[track_caller]\n        fn from(value: Signal<&'static str>) -> Self {\n            Signal::derive_local(move || value.read().to_string())\n        }\n    }\n\n    impl From<Signal<&'static str>> for Signal<Option<String>> {\n        #[track_caller]\n        fn from(value: Signal<&'static str>) -> Self {\n            Signal::derive(move || Some(value.read().to_string()))\n        }\n    }\n\n    impl From<Signal<&'static str>> for Signal<Option<String>, LocalStorage> {\n        #[track_caller]\n        fn from(value: Signal<&'static str>) -> Self {\n            Signal::derive_local(move || Some(value.read().to_string()))\n        }\n    }\n\n    impl From<Signal<Option<&'static str>>> for Signal<Option<String>> {\n        #[track_caller]\n        fn from(value: Signal<Option<&'static str>>) -> Self {\n            Signal::derive(move || value.read().map(str::to_string))\n        }\n    }\n\n    impl From<Signal<Option<&'static str>, LocalStorage>>\n        for Signal<Option<String>, LocalStorage>\n    {\n        #[track_caller]\n        fn from(value: Signal<Option<&'static str>, LocalStorage>) -> Self {\n            Signal::derive_local(move || value.read().map(str::to_string))\n        }\n    }\n\n    impl From<Signal<Option<&'static str>>>\n        for Signal<Option<String>, LocalStorage>\n    {\n        #[track_caller]\n        fn from(value: Signal<Option<&'static str>>) -> Self {\n            Signal::derive_local(move || value.read().map(str::to_string))\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    #[doc(hidden)]\n    pub struct __IntoReactiveValueMarkerSignalFromReactiveClosure;\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    #[doc(hidden)]\n    pub struct __IntoReactiveValueMarkerSignalStrOutputToString;\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    #[doc(hidden)]\n    pub struct __IntoReactiveValueMarkerOptionalSignalFromReactiveClosureAlways;\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<T, F>\n        crate::IntoReactiveValue<\n            Signal<T, SyncStorage>,\n            __IntoReactiveValueMarkerSignalFromReactiveClosure,\n        > for F\n    where\n        T: Send + Sync + 'static,\n        F: Fn() -> T + Send + Sync + 'static,\n    {\n        fn into_reactive_value(self) -> Signal<T, SyncStorage> {\n            Signal::derive(self)\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<T, F>\n        crate::IntoReactiveValue<\n            ArcSignal<T, SyncStorage>,\n            __IntoReactiveValueMarkerSignalFromReactiveClosure,\n        > for F\n    where\n        T: Send + Sync + 'static,\n        F: Fn() -> T + Send + Sync + 'static,\n    {\n        fn into_reactive_value(self) -> ArcSignal<T, SyncStorage> {\n            ArcSignal::derive(self)\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<T, F>\n        crate::IntoReactiveValue<\n            Signal<T, LocalStorage>,\n            __IntoReactiveValueMarkerSignalFromReactiveClosure,\n        > for F\n    where\n        T: 'static,\n        F: Fn() -> T + 'static,\n    {\n        fn into_reactive_value(self) -> Signal<T, LocalStorage> {\n            Signal::derive_local(self)\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<T, F>\n        crate::IntoReactiveValue<\n            ArcSignal<T, LocalStorage>,\n            __IntoReactiveValueMarkerSignalFromReactiveClosure,\n        > for F\n    where\n        T: 'static,\n        F: Fn() -> T + 'static,\n    {\n        fn into_reactive_value(self) -> ArcSignal<T, LocalStorage> {\n            ArcSignal::derive_local(self)\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<F>\n        crate::IntoReactiveValue<\n            Signal<String, SyncStorage>,\n            __IntoReactiveValueMarkerSignalStrOutputToString,\n        > for F\n    where\n        F: Fn() -> &'static str + Send + Sync + 'static,\n    {\n        fn into_reactive_value(self) -> Signal<String, SyncStorage> {\n            Signal::derive(move || self().to_string())\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<F>\n        crate::IntoReactiveValue<\n            ArcSignal<String, SyncStorage>,\n            __IntoReactiveValueMarkerSignalStrOutputToString,\n        > for F\n    where\n        F: Fn() -> &'static str + Send + Sync + 'static,\n    {\n        fn into_reactive_value(self) -> ArcSignal<String, SyncStorage> {\n            ArcSignal::derive(move || self().to_string())\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<F>\n        crate::IntoReactiveValue<\n            Signal<String, LocalStorage>,\n            __IntoReactiveValueMarkerSignalStrOutputToString,\n        > for F\n    where\n        F: Fn() -> &'static str + 'static,\n    {\n        fn into_reactive_value(self) -> Signal<String, LocalStorage> {\n            Signal::derive_local(move || self().to_string())\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<F>\n        crate::IntoReactiveValue<\n            ArcSignal<String, LocalStorage>,\n            __IntoReactiveValueMarkerSignalStrOutputToString,\n        > for F\n    where\n        F: Fn() -> &'static str + 'static,\n    {\n        fn into_reactive_value(self) -> ArcSignal<String, LocalStorage> {\n            ArcSignal::derive_local(move || self().to_string())\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<T, F>\n        crate::IntoReactiveValue<\n            Signal<Option<T>, SyncStorage>,\n            __IntoReactiveValueMarkerOptionalSignalFromReactiveClosureAlways,\n        > for F\n    where\n        T: Send + Sync + 'static,\n        F: Fn() -> T + Send + Sync + 'static,\n    {\n        fn into_reactive_value(self) -> Signal<Option<T>, SyncStorage> {\n            Signal::derive(move || Some(self()))\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<T, F>\n        crate::IntoReactiveValue<\n            ArcSignal<Option<T>, SyncStorage>,\n            __IntoReactiveValueMarkerOptionalSignalFromReactiveClosureAlways,\n        > for F\n    where\n        T: Send + Sync + 'static,\n        F: Fn() -> T + Send + Sync + 'static,\n    {\n        fn into_reactive_value(self) -> ArcSignal<Option<T>, SyncStorage> {\n            ArcSignal::derive(move || Some(self()))\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<T, F>\n        crate::IntoReactiveValue<\n            Signal<Option<T>, LocalStorage>,\n            __IntoReactiveValueMarkerOptionalSignalFromReactiveClosureAlways,\n        > for F\n    where\n        T: 'static,\n        F: Fn() -> T + 'static,\n    {\n        fn into_reactive_value(self) -> Signal<Option<T>, LocalStorage> {\n            Signal::derive_local(move || Some(self()))\n        }\n    }\n\n    #[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\n    impl<T, F>\n        crate::IntoReactiveValue<\n            ArcSignal<Option<T>, LocalStorage>,\n            __IntoReactiveValueMarkerOptionalSignalFromReactiveClosureAlways,\n        > for F\n    where\n        T: 'static,\n        F: Fn() -> T + 'static,\n    {\n        fn into_reactive_value(self) -> ArcSignal<Option<T>, LocalStorage> {\n            ArcSignal::derive_local(move || Some(self()))\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<MaybeSignal<T>> for Signal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: MaybeSignal<T>) -> Self {\n            match value {\n                MaybeSignal::Static(value) => Signal::stored(value),\n                MaybeSignal::Dynamic(signal) => signal,\n            }\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<MaybeSignal<T, LocalStorage>> for Signal<T, LocalStorage>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: MaybeSignal<T, LocalStorage>) -> Self {\n            match value {\n                MaybeSignal::Static(value) => Signal::stored_local(value),\n                MaybeSignal::Dynamic(signal) => signal,\n            }\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<MaybeSignal<T>> for Signal<Option<T>>\n    where\n        T: Clone + Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: MaybeSignal<T>) -> Self {\n            match value {\n                MaybeSignal::Static(value) => Signal::stored(Some(value)),\n                MaybeSignal::Dynamic(signal) => {\n                    Signal::derive(move || Some(signal.get()))\n                }\n            }\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<MaybeSignal<T, LocalStorage>> for Signal<Option<T>, LocalStorage>\n    where\n        T: Clone + Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: MaybeSignal<T, LocalStorage>) -> Self {\n            match value {\n                MaybeSignal::Static(value) => Signal::stored_local(Some(value)),\n                MaybeSignal::Dynamic(signal) => {\n                    Signal::derive_local(move || Some(signal.get()))\n                }\n            }\n        }\n    }\n\n    impl<T> From<MaybeProp<T>> for Option<Signal<Option<T>>>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: MaybeProp<T>) -> Self {\n            value.0\n        }\n    }\n\n    impl<T> From<MaybeProp<T, LocalStorage>>\n        for Option<Signal<Option<T>, LocalStorage>>\n    where\n        T: Send + Sync + 'static,\n    {\n        #[track_caller]\n        fn from(value: MaybeProp<T, LocalStorage>) -> Self {\n            value.0\n        }\n    }\n\n    /// A wrapper for a value that is *either* `T` or [`Signal<T>`].\n    ///\n    /// This allows you to create APIs that take either a reactive or a non-reactive value\n    /// of the same type. This is especially useful for component properties.\n    ///\n    /// ```\n    /// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # use reactive_graph::wrappers::read::MaybeSignal;\n    /// # use reactive_graph::computed::Memo;\n    /// # use reactive_graph::prelude::*;\n    /// let (count, set_count) = signal(2);\n    /// let double_count = MaybeSignal::derive(move || count.get() * 2);\n    /// let memoized_double_count = Memo::new(move |_| count.get() * 2);\n    /// let static_value = 5;\n    ///\n    /// // this function takes either a reactive or non-reactive value\n    /// fn above_3(arg: &MaybeSignal<i32>) -> bool {\n    ///     // ✅ calling the signal clones and returns the value\n    ///     //    it is a shorthand for arg.get()\n    ///     arg.get() > 3\n    /// }\n    ///\n    /// assert_eq!(above_3(&static_value.into()), true);\n    /// assert_eq!(above_3(&count.into()), false);\n    /// assert_eq!(above_3(&double_count), true);\n    /// assert_eq!(above_3(&memoized_double_count.into()), true);\n    /// ```\n    #[derive(Debug, PartialEq, Eq)]\n    #[deprecated(\n        since = \"0.7.0-rc3\",\n        note = \"`MaybeSignal<T>` is deprecated in favour of `Signal<T>` which \\\n                is `Copy`, now has a more efficient From<T> implementation \\\n                and other benefits in 0.7.\"\n    )]\n    pub enum MaybeSignal<T, S = SyncStorage>\n    where\n        T: 'static,\n        S: Storage<T>,\n    {\n        /// An unchanging value of type `T`.\n        Static(T),\n        /// A reactive signal that contains a value of type `T`.\n        Dynamic(Signal<T, S>),\n    }\n\n    #[allow(deprecated)]\n    impl<T: Clone, S> Clone for MaybeSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn clone(&self) -> Self {\n            match self {\n                Self::Static(item) => Self::Static(item.clone()),\n                Self::Dynamic(signal) => Self::Dynamic(*signal),\n            }\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T: Copy, S> Copy for MaybeSignal<T, S> where S: Storage<T> {}\n\n    #[allow(deprecated)]\n    impl<T: Default, S> Default for MaybeSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn default() -> Self {\n            Self::Static(Default::default())\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T, S> DefinedAt for MaybeSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn defined_at(&self) -> Option<&'static Location<'static>> {\n            // TODO this could be improved, but would require moving from an enum to a struct here.\n            // Probably not worth it for relatively small benefits.\n            None\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T, S> Track for MaybeSignal<T, S>\n    where\n        S: Storage<T> + Storage<SignalTypes<T, S>>,\n    {\n        fn track(&self) {\n            match self {\n                Self::Static(_) => {}\n                Self::Dynamic(signal) => signal.track(),\n            }\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T, S> ReadUntracked for MaybeSignal<T, S>\n    where\n        T: Clone,\n        S: Storage<SignalTypes<T, S>> + Storage<T>,\n    {\n        type Value = ReadGuard<T, SignalReadGuard<T, S>>;\n\n        fn try_read_untracked(&self) -> Option<Self::Value> {\n            match self {\n                Self::Static(t) => {\n                    Some(ReadGuard::new(SignalReadGuard::Owned(t.clone())))\n                }\n                Self::Dynamic(s) => s.try_read_untracked(),\n            }\n        }\n\n        fn custom_try_read(&self) -> Option<Option<Self::Value>> {\n            match self {\n                Self::Static(_) => None,\n                Self::Dynamic(s) => s.custom_try_read(),\n            }\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> MaybeSignal<T>\n    where\n        T: Send + Sync,\n    {\n        /// Wraps a derived signal, i.e., any computation that accesses one or more\n        /// reactive signals.\n        pub fn derive(\n            derived_signal: impl Fn() -> T + Send + Sync + 'static,\n        ) -> Self {\n            Self::Dynamic(Signal::derive(derived_signal))\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> MaybeSignal<T, LocalStorage> {\n        /// Wraps a derived signal, i.e., any computation that accesses one or more\n        /// reactive signals.\n        pub fn derive_local(derived_signal: impl Fn() -> T + 'static) -> Self {\n            Self::Dynamic(Signal::derive_local(derived_signal))\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<T> for MaybeSignal<T, SyncStorage>\n    where\n        SyncStorage: Storage<T>,\n    {\n        fn from(value: T) -> Self {\n            Self::Static(value)\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> FromLocal<T> for MaybeSignal<T, LocalStorage>\n    where\n        LocalStorage: Storage<T>,\n    {\n        fn from_local(value: T) -> Self {\n            Self::Static(value)\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<ReadSignal<T>> for MaybeSignal<T>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: ReadSignal<T>) -> Self {\n            Self::Dynamic(value.into())\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<ReadSignal<T, LocalStorage>> for MaybeSignal<T, LocalStorage> {\n        fn from(value: ReadSignal<T, LocalStorage>) -> Self {\n            Self::Dynamic(value.into())\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<RwSignal<T>> for MaybeSignal<T>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: RwSignal<T>) -> Self {\n            Self::Dynamic(value.into())\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<RwSignal<T, LocalStorage>> for MaybeSignal<T, LocalStorage> {\n        fn from(value: RwSignal<T, LocalStorage>) -> Self {\n            Self::Dynamic(value.into())\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<Memo<T>> for MaybeSignal<T>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: Memo<T>) -> Self {\n            Self::Dynamic(value.into())\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<Memo<T, LocalStorage>> for MaybeSignal<T, LocalStorage> {\n        fn from(value: Memo<T, LocalStorage>) -> Self {\n            Self::Dynamic(value.into())\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<ArcReadSignal<T>> for MaybeSignal<T>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: ArcReadSignal<T>) -> Self {\n            ReadSignal::from(value).into()\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> FromLocal<ArcReadSignal<T>> for MaybeSignal<T, LocalStorage> {\n        fn from_local(value: ArcReadSignal<T>) -> Self {\n            ReadSignal::from_local(value).into()\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<ArcRwSignal<T>> for MaybeSignal<T>\n    where\n        T: Send + Sync + 'static,\n    {\n        fn from(value: ArcRwSignal<T>) -> Self {\n            RwSignal::from(value).into()\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> FromLocal<ArcRwSignal<T>> for MaybeSignal<T, LocalStorage>\n    where\n        T: 'static,\n    {\n        fn from_local(value: ArcRwSignal<T>) -> Self {\n            RwSignal::from_local(value).into()\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<ArcMemo<T>> for MaybeSignal<T>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: ArcMemo<T>) -> Self {\n            Memo::from(value).into()\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> FromLocal<ArcMemo<T, LocalStorage>> for MaybeSignal<T, LocalStorage> {\n        fn from_local(value: ArcMemo<T, LocalStorage>) -> Self {\n            Memo::from_local(value).into()\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T, S> From<Signal<T, S>> for MaybeSignal<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn from(value: Signal<T, S>) -> Self {\n            Self::Dynamic(value)\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<S> From<&str> for MaybeSignal<String, S>\n    where\n        S: Storage<String> + Storage<Arc<RwLock<String>>>,\n    {\n        fn from(value: &str) -> Self {\n            Self::Static(value.to_string())\n        }\n    }\n\n    /// A wrapping type for an optional component prop.\n    ///\n    /// This can either be a signal or a non-reactive value, and may or may not have a value.\n    /// In other words, this is an `Option<Signal<Option<T>>>`, but automatically flattens its getters.\n    ///\n    /// This creates an extremely flexible type for component libraries, etc.\n    ///\n    /// ## Examples\n    /// ```rust\n    /// # use reactive_graph::signal::*; let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # use reactive_graph::wrappers::read::MaybeProp;\n    /// # use reactive_graph::computed::Memo;\n    /// # use reactive_graph::prelude::*;\n    /// let (count, set_count) = signal(Some(2));\n    /// let double = |n| n * 2;\n    /// let double_count = MaybeProp::derive(move || count.get().map(double));\n    /// let memoized_double_count = Memo::new(move |_| count.get().map(double));\n    /// let static_value = 5;\n    ///\n    /// // this function takes either a reactive or non-reactive value\n    /// fn above_3(arg: &MaybeProp<i32>) -> bool {\n    ///     // ✅ calling the signal clones and returns the value\n    ///     //    it is a shorthand for arg.get()q\n    ///     arg.get().map(|arg| arg > 3).unwrap_or(false)\n    /// }\n    ///\n    /// assert_eq!(above_3(&None::<i32>.into()), false);\n    /// assert_eq!(above_3(&static_value.into()), true);\n    /// assert_eq!(above_3(&count.into()), false);\n    /// assert_eq!(above_3(&double_count), true);\n    /// assert_eq!(above_3(&memoized_double_count.into()), true);\n    /// ```\n    #[derive(Debug, PartialEq, Eq)]\n    pub struct MaybeProp<T: 'static, S = SyncStorage>(\n        pub(crate) Option<Signal<Option<T>, S>>,\n    )\n    where\n        S: Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>>;\n\n    impl<T, S> Clone for MaybeProp<T, S>\n    where\n        S: Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>>,\n    {\n        fn clone(&self) -> Self {\n            *self\n        }\n    }\n\n    impl<T, S> Copy for MaybeProp<T, S> where\n        S: Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>>\n    {\n    }\n\n    impl<T, S> Default for MaybeProp<T, S>\n    where\n        S: Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>>,\n    {\n        fn default() -> Self {\n            Self(None)\n        }\n    }\n\n    impl<T, S> DefinedAt for MaybeProp<T, S>\n    where\n        S: Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>>,\n    {\n        fn defined_at(&self) -> Option<&'static Location<'static>> {\n            // TODO this can be improved by adding a defined_at field\n            None\n        }\n    }\n\n    impl<T, S> Track for MaybeProp<T, S>\n    where\n        S: Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>>,\n    {\n        fn track(&self) {\n            match &self.0 {\n                None => {}\n                Some(signal) => signal.track(),\n            }\n        }\n    }\n\n    impl<T, S> ReadUntracked for MaybeProp<T, S>\n    where\n        S: Storage<Option<T>> + Storage<SignalTypes<Option<T>, S>>,\n    {\n        type Value = ReadGuard<Option<T>, SignalReadGuard<Option<T>, S>>;\n\n        fn try_read_untracked(&self) -> Option<Self::Value> {\n            match &self.0 {\n                None => Some(ReadGuard::new(SignalReadGuard::Owned(None))),\n                Some(inner) => inner.try_read_untracked(),\n            }\n        }\n\n        fn custom_try_read(&self) -> Option<Option<Self::Value>> {\n            match &self.0 {\n                None => None,\n                Some(inner) => inner.custom_try_read(),\n            }\n        }\n    }\n\n    impl<T> MaybeProp<T>\n    where\n        T: Send + Sync,\n    {\n        /// Wraps a derived signal, i.e., any computation that accesses one or more\n        /// reactive signals.\n        pub fn derive(\n            derived_signal: impl Fn() -> Option<T> + Send + Sync + 'static,\n        ) -> Self {\n            Self(Some(Signal::derive(derived_signal)))\n        }\n    }\n\n    impl<T> From<T> for MaybeProp<T>\n    where\n        T: Send + Sync,\n        SyncStorage: Storage<Option<T>>,\n    {\n        fn from(value: T) -> Self {\n            Self(Some(Signal::stored(Some(value))))\n        }\n    }\n\n    impl<T> From<Option<T>> for MaybeProp<T>\n    where\n        T: Send + Sync,\n        SyncStorage: Storage<Option<T>>,\n    {\n        fn from(value: Option<T>) -> Self {\n            Self(Some(Signal::stored(value)))\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<MaybeSignal<Option<T>>> for MaybeProp<T>\n    where\n        T: Send + Sync,\n        SyncStorage: Storage<Option<T>>,\n    {\n        fn from(value: MaybeSignal<Option<T>>) -> Self {\n            Self(Some(value.into()))\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<Option<MaybeSignal<Option<T>>>> for MaybeProp<T>\n    where\n        T: Send + Sync,\n        SyncStorage: Storage<Option<T>>,\n    {\n        fn from(value: Option<MaybeSignal<Option<T>>>) -> Self {\n            Self(value.map(Into::into))\n        }\n    }\n\n    impl<T> From<ReadSignal<Option<T>>> for MaybeProp<T>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: ReadSignal<Option<T>>) -> Self {\n            Self(Some(value.into()))\n        }\n    }\n\n    impl<T> From<RwSignal<Option<T>>> for MaybeProp<T>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: RwSignal<Option<T>>) -> Self {\n            Self(Some(value.into()))\n        }\n    }\n\n    impl<T> From<Memo<Option<T>>> for MaybeProp<T>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: Memo<Option<T>>) -> Self {\n            Self(Some(value.into()))\n        }\n    }\n\n    impl<T> From<Signal<Option<T>>> for MaybeProp<T>\n    where\n        T: Send + Sync,\n        SyncStorage: Storage<Option<T>>,\n    {\n        fn from(value: Signal<Option<T>>) -> Self {\n            Self(Some(value))\n        }\n    }\n\n    impl<T> From<ReadSignal<T>> for MaybeProp<T>\n    where\n        T: Send + Sync + Clone,\n    {\n        fn from(value: ReadSignal<T>) -> Self {\n            Self(Some(Signal::derive(move || Some(value.get()))))\n        }\n    }\n\n    impl<T> From<RwSignal<T>> for MaybeProp<T>\n    where\n        T: Send + Sync + Clone,\n    {\n        fn from(value: RwSignal<T>) -> Self {\n            Self(Some(Signal::derive(move || Some(value.get()))))\n        }\n    }\n\n    impl<T> From<Memo<T>> for MaybeProp<T>\n    where\n        T: Send + Sync + Clone,\n    {\n        fn from(value: Memo<T>) -> Self {\n            Self(Some(Signal::derive(move || Some(value.get()))))\n        }\n    }\n\n    impl<T> From<Signal<T>> for MaybeProp<T>\n    where\n        T: Send + Sync + Clone,\n    {\n        fn from(value: Signal<T>) -> Self {\n            Self(Some(Signal::derive(move || Some(value.get()))))\n        }\n    }\n\n    impl From<&str> for MaybeProp<String> {\n        fn from(value: &str) -> Self {\n            Self(Some(Signal::from(Some(value.to_string()))))\n        }\n    }\n\n    impl<T> MaybeProp<T, LocalStorage> {\n        /// Wraps a derived signal, i.e., any computation that accesses one or more\n        /// reactive signals.\n        pub fn derive_local(\n            derived_signal: impl Fn() -> Option<T> + 'static,\n        ) -> Self {\n            Self(Some(Signal::derive_local(derived_signal)))\n        }\n    }\n\n    impl<T> FromLocal<T> for MaybeProp<T, LocalStorage> {\n        fn from_local(value: T) -> Self {\n            Self(Some(Signal::stored_local(Some(value))))\n        }\n    }\n\n    impl<T> FromLocal<Option<T>> for MaybeProp<T, LocalStorage> {\n        fn from_local(value: Option<T>) -> Self {\n            Self(Some(Signal::stored_local(value)))\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<MaybeSignal<Option<T>, LocalStorage>>\n        for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: MaybeSignal<Option<T>, LocalStorage>) -> Self {\n            Self(Some(value.into()))\n        }\n    }\n\n    #[allow(deprecated)]\n    impl<T> From<Option<MaybeSignal<Option<T>, LocalStorage>>>\n        for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: Option<MaybeSignal<Option<T>, LocalStorage>>) -> Self {\n            Self(value.map(Into::into))\n        }\n    }\n\n    impl<T> From<ReadSignal<Option<T>, LocalStorage>> for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: ReadSignal<Option<T>, LocalStorage>) -> Self {\n            Self(Some(value.into()))\n        }\n    }\n\n    impl<T> From<RwSignal<Option<T>, LocalStorage>> for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: RwSignal<Option<T>, LocalStorage>) -> Self {\n            Self(Some(value.into()))\n        }\n    }\n\n    impl<T> From<Memo<Option<T>, LocalStorage>> for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync,\n    {\n        fn from(value: Memo<Option<T>, LocalStorage>) -> Self {\n            Self(Some(value.into()))\n        }\n    }\n\n    impl<T> From<Signal<Option<T>, LocalStorage>> for MaybeProp<T, LocalStorage> {\n        fn from(value: Signal<Option<T>, LocalStorage>) -> Self {\n            Self(Some(value))\n        }\n    }\n\n    impl<T> From<ReadSignal<T, LocalStorage>> for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync + Clone,\n    {\n        fn from(value: ReadSignal<T, LocalStorage>) -> Self {\n            Self(Some(Signal::derive_local(move || Some(value.get()))))\n        }\n    }\n\n    impl<T> From<RwSignal<T, LocalStorage>> for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync + Clone,\n    {\n        fn from(value: RwSignal<T, LocalStorage>) -> Self {\n            Self(Some(Signal::derive_local(move || Some(value.get()))))\n        }\n    }\n\n    impl<T> From<Memo<T, LocalStorage>> for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync + Clone,\n    {\n        fn from(value: Memo<T, LocalStorage>) -> Self {\n            Self(Some(Signal::derive_local(move || Some(value.get()))))\n        }\n    }\n\n    impl<T> From<Signal<T, LocalStorage>> for MaybeProp<T, LocalStorage>\n    where\n        T: Send + Sync + Clone,\n    {\n        fn from(value: Signal<T, LocalStorage>) -> Self {\n            Self(Some(Signal::derive_local(move || Some(value.get()))))\n        }\n    }\n\n    impl From<&str> for MaybeProp<String, LocalStorage> {\n        fn from(value: &str) -> Self {\n            Self(Some(Signal::stored_local(Some(value.to_string()))))\n        }\n    }\n\n    /// The content of a [`Signal`] wrapper read guard, variable depending on the signal type.\n    pub enum SignalReadGuard<T: 'static, S: Storage<T>> {\n        /// A read signal guard.\n        Read(ReadGuard<T, Plain<T>>),\n        #[allow(clippy::type_complexity)]\n        /// A memo guard.\n        Memo(\n            ReadGuard<T, Mapped<Plain<Option<<S as Storage<T>>::Wrapped>>, T>>,\n        ),\n        /// A fake guard for derived signals, the content had to actually be cloned, so it's not a guard but we pretend it is.\n        Owned(T),\n    }\n\n    impl<T: 'static + std::fmt::Debug, S: Storage<T> + std::fmt::Debug>\n        std::fmt::Debug for SignalReadGuard<T, S>\n    where\n        <S as Storage<T>>::Wrapped: std::fmt::Debug,\n    {\n        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n            match self {\n                Self::Read(arg0) => f.debug_tuple(\"Read\").field(arg0).finish(),\n                Self::Memo(arg0) => f.debug_tuple(\"Memo\").field(arg0).finish(),\n                Self::Owned(arg0) => {\n                    f.debug_tuple(\"Owned\").field(arg0).finish()\n                }\n            }\n        }\n    }\n\n    impl<T, S> Clone for SignalReadGuard<T, S>\n    where\n        S: Storage<T>,\n        T: Clone,\n        Plain<T>: Clone,\n        Mapped<Plain<Option<<S as Storage<T>>::Wrapped>>, T>: Clone,\n    {\n        fn clone(&self) -> Self {\n            match self {\n                SignalReadGuard::Read(i) => SignalReadGuard::Read(i.clone()),\n                SignalReadGuard::Memo(i) => SignalReadGuard::Memo(i.clone()),\n                SignalReadGuard::Owned(i) => SignalReadGuard::Owned(i.clone()),\n            }\n        }\n    }\n\n    impl<T, S> Deref for SignalReadGuard<T, S>\n    where\n        S: Storage<T>,\n    {\n        type Target = T;\n        fn deref(&self) -> &Self::Target {\n            match self {\n                SignalReadGuard::Read(i) => i,\n                SignalReadGuard::Memo(i) => i,\n                SignalReadGuard::Owned(i) => i,\n            }\n        }\n    }\n\n    impl<T, S> Borrow<T> for SignalReadGuard<T, S>\n    where\n        S: Storage<T>,\n    {\n        fn borrow(&self) -> &T {\n            self.deref()\n        }\n    }\n\n    impl<T, S> PartialEq<T> for SignalReadGuard<T, S>\n    where\n        S: Storage<T>,\n        T: PartialEq,\n    {\n        fn eq(&self, other: &T) -> bool {\n            self.deref() == other\n        }\n    }\n\n    impl<T, S> Display for SignalReadGuard<T, S>\n    where\n        S: Storage<T>,\n        T: Display,\n    {\n        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n            Display::fmt(&**self, f)\n        }\n    }\n}\n\n/// Types that abstract over the ability to update a signal.\npub mod write {\n    use crate::{\n        owner::{ArenaItem, Storage, SyncStorage},\n        signal::{ArcRwSignal, ArcWriteSignal, RwSignal, WriteSignal},\n        traits::Set,\n    };\n\n    /// Helper trait for converting `Fn(T)` into [`SignalSetter<T>`].\n    pub trait IntoSignalSetter<T, S>: Sized {\n        /// Consumes `self`, returning [`SignalSetter<T>`].\n        fn into_signal_setter(self) -> SignalSetter<T, S>;\n    }\n\n    impl<F, T, S> IntoSignalSetter<T, S> for F\n    where\n        F: Fn(T) + 'static + Send + Sync,\n        S: Storage<Box<dyn Fn(T) + Send + Sync>>,\n    {\n        fn into_signal_setter(self) -> SignalSetter<T, S> {\n            SignalSetter::map(self)\n        }\n    }\n\n    /// A wrapper for any kind of settable reactive signal: a [`WriteSignal`],\n    /// [`RwSignal`], or closure that receives a value and sets a signal depending\n    /// on it.\n    ///\n    /// This allows you to create APIs that take any kind of `SignalSetter<T>` as an argument,\n    /// rather than adding a generic `F: Fn(T)`. Values can be set with the same\n    /// function call or `set()`, API as other signals.\n    ///\n    /// ## Core Trait Implementations\n    /// - [`.set()`](#impl-SignalSet<T>-for-SignalSetter<T>) (or calling the setter as a function)\n    ///   sets the signal’s value, and notifies all subscribers that the signal’s value has changed.\n    ///   to subscribe to the signal, and to re-run whenever the value of the signal changes.\n    ///\n    /// ## Examples\n    /// ```rust\n    /// # use reactive_graph::prelude::*;  let owner = reactive_graph::owner::Owner::new(); owner.set();\n    /// # use reactive_graph::wrappers::write::SignalSetter;\n    /// # use reactive_graph::signal::signal;\n    /// let (count, set_count) = signal(2);\n    /// let set_double_input = SignalSetter::map(move |n| set_count.set(n * 2));\n    ///\n    /// // this function takes any kind of signal setter\n    /// fn set_to_4(setter: &SignalSetter<i32>) {\n    ///     // ✅ calling the signal sets the value\n    ///     //    can be `setter(4)` on nightly\n    ///     setter.set(4);\n    /// }\n    ///\n    /// set_to_4(&set_count.into());\n    /// assert_eq!(count.get(), 4);\n    /// set_to_4(&set_double_input);\n    /// assert_eq!(count.get(), 8);\n    /// ```\n    #[derive(Debug, PartialEq, Eq)]\n    pub struct SignalSetter<T, S = SyncStorage>\n    where\n        T: 'static,\n    {\n        inner: SignalSetterTypes<T, S>,\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        defined_at: &'static std::panic::Location<'static>,\n    }\n\n    impl<T, S> Clone for SignalSetter<T, S> {\n        fn clone(&self) -> Self {\n            *self\n        }\n    }\n\n    impl<T: Default + 'static, S> Default for SignalSetter<T, S> {\n        #[track_caller]\n        fn default() -> Self {\n            Self {\n                inner: SignalSetterTypes::Default,\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T, S> Copy for SignalSetter<T, S> {}\n\n    impl<T, S> Set for SignalSetter<T, S>\n    where\n        T: 'static,\n        S: Storage<ArcWriteSignal<T>> + Storage<Box<dyn Fn(T) + Send + Sync>>,\n    {\n        type Value = T;\n\n        fn set(&self, new_value: Self::Value) {\n            match self.inner {\n                SignalSetterTypes::Default => {}\n                SignalSetterTypes::Write(w) => w.set(new_value),\n                SignalSetterTypes::Mapped(s) => {\n                    s.try_with_value(|setter| setter(new_value));\n                }\n            }\n        }\n\n        fn try_set(&self, new_value: Self::Value) -> Option<Self::Value> {\n            match self.inner {\n                SignalSetterTypes::Default => Some(new_value),\n                SignalSetterTypes::Write(w) => w.try_set(new_value),\n                SignalSetterTypes::Mapped(s) => {\n                    let mut new_value = Some(new_value);\n\n                    let _ = s.try_with_value(|setter| {\n                        setter(new_value.take().unwrap())\n                    });\n\n                    new_value\n                }\n            }\n        }\n    }\n\n    impl<T, S> SignalSetter<T, S>\n    where\n        S: Storage<Box<dyn Fn(T) + Send + Sync>>,\n    {\n        /// Wraps a signal-setting closure, i.e., any computation that sets one or more reactive signals.\n        #[track_caller]\n        pub fn map(mapped_setter: impl Fn(T) + Send + Sync + 'static) -> Self {\n            Self {\n                inner: SignalSetterTypes::Mapped(ArenaItem::new_with_storage(\n                    Box::new(mapped_setter),\n                )),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T, S> From<WriteSignal<T, S>> for SignalSetter<T, S> {\n        #[track_caller]\n        fn from(value: WriteSignal<T, S>) -> Self {\n            Self {\n                inner: SignalSetterTypes::Write(value),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    impl<T, S> From<RwSignal<T, S>> for SignalSetter<T, S>\n    where\n        T: Send + Sync + 'static,\n        S: Storage<ArcRwSignal<T>> + Storage<ArcWriteSignal<T>>,\n    {\n        #[track_caller]\n        fn from(value: RwSignal<T, S>) -> Self {\n            Self {\n                inner: SignalSetterTypes::Write(value.write_only()),\n                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                defined_at: std::panic::Location::caller(),\n            }\n        }\n    }\n\n    enum SignalSetterTypes<T, S = SyncStorage>\n    where\n        T: 'static,\n    {\n        Write(WriteSignal<T, S>),\n        Mapped(ArenaItem<Box<dyn Fn(T) + Send + Sync>, S>),\n        Default,\n    }\n\n    impl<T, S> Clone for SignalSetterTypes<T, S> {\n        fn clone(&self) -> Self {\n            *self\n        }\n    }\n\n    impl<T, S> Copy for SignalSetterTypes<T, S> {}\n\n    impl<T, S> core::fmt::Debug for SignalSetterTypes<T, S>\n    where\n        T: core::fmt::Debug,\n        S: core::fmt::Debug,\n    {\n        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n            match self {\n                Self::Write(arg0) => {\n                    f.debug_tuple(\"WriteSignal\").field(arg0).finish()\n                }\n                Self::Mapped(_) => f.debug_tuple(\"Mapped\").finish(),\n                Self::Default => {\n                    f.debug_tuple(\"SignalSetter<Default>\").finish()\n                }\n            }\n        }\n    }\n\n    impl<T, S> PartialEq for SignalSetterTypes<T, S>\n    where\n        T: PartialEq,\n    {\n        fn eq(&self, other: &Self) -> bool {\n            match (self, other) {\n                (Self::Write(l0), Self::Write(r0)) => l0 == r0,\n                (Self::Mapped(l0), Self::Mapped(r0)) => std::ptr::eq(l0, r0),\n                _ => false,\n            }\n        }\n    }\n\n    impl<T, S> Eq for SignalSetterTypes<T, S> where T: PartialEq {}\n}\n"
  },
  {
    "path": "reactive_graph/tests/async_derived.rs",
    "content": "use any_spawner::Executor;\nuse reactive_graph::{\n    computed::{ArcAsyncDerived, AsyncDerived},\n    owner::Owner,\n    signal::RwSignal,\n    traits::{Get, Read, Set, With, WithUntracked},\n};\nuse std::future::pending;\n\n#[tokio::test]\nasync fn arc_async_derived_calculates_eagerly() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let value = ArcAsyncDerived::new(|| async {\n        Executor::tick().await;\n        42\n    });\n\n    assert_eq!(value.clone().await, 42);\n}\n\n#[tokio::test]\nasync fn arc_async_derived_tracks_signal_change() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let signal = RwSignal::new(10);\n    let value = ArcAsyncDerived::new(move || async move {\n        Executor::tick().await;\n        signal.get()\n    });\n\n    assert_eq!(value.clone().await, 10);\n    signal.set(30);\n    Executor::tick().await;\n    assert_eq!(value.clone().await, 30);\n    signal.set(50);\n    Executor::tick().await;\n    assert_eq!(value.clone().await, 50);\n}\n\n#[tokio::test]\nasync fn async_derived_calculates_eagerly() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let value = AsyncDerived::new(|| async {\n        Executor::tick().await;\n        42\n    });\n\n    assert_eq!(value.await, 42);\n}\n\n#[tokio::test]\nasync fn async_derived_tracks_signal_change() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let signal = RwSignal::new(10);\n    let value = AsyncDerived::new(move || async move {\n        Executor::tick().await;\n        signal.get()\n    });\n\n    assert_eq!(value.await, 10);\n    signal.set(30);\n    Executor::tick().await;\n    assert_eq!(value.await, 30);\n    signal.set(50);\n    Executor::tick().await;\n    assert_eq!(value.await, 50);\n}\n\n#[tokio::test]\nasync fn read_signal_traits_on_arc() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let value = ArcAsyncDerived::new(pending::<()>);\n    assert_eq!(value.read(), None);\n    assert_eq!(value.with_untracked(|n| *n), None);\n    assert_eq!(value.with(|n| *n), None);\n    assert_eq!(value.get(), None);\n}\n\n#[tokio::test]\nasync fn read_signal_traits_on_arena() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let value = AsyncDerived::new(pending::<()>);\n    println!(\"{:?}\", value.read());\n    assert_eq!(value.read(), None);\n    assert_eq!(value.with_untracked(|n| *n), None);\n    assert_eq!(value.with(|n| *n), None);\n    assert_eq!(value.get(), None);\n}\n\n#[tokio::test]\nasync fn async_derived_with_initial() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let signal1 = RwSignal::new(0);\n    let signal2 = RwSignal::new(0);\n    let derived =\n        ArcAsyncDerived::new_with_initial(Some(5), move || async move {\n            // reactive values can be tracked anywhere in the `async` block\n            let value1 = signal1.get();\n            tokio::time::sleep(std::time::Duration::from_millis(25)).await;\n            let value2 = signal2.get();\n\n            value1 + value2\n        });\n\n    // the value can be accessed synchronously as `Option<T>`\n    assert_eq!(derived.get(), Some(5));\n    // we can also .await the value, i.e., convert it into a Future\n    assert_eq!(derived.clone().await, 0);\n    assert_eq!(derived.get(), Some(0));\n\n    signal1.set(1);\n    // while the new value is still pending, the signal holds the old value\n    tokio::time::sleep(std::time::Duration::from_millis(5)).await;\n    assert_eq!(derived.get(), Some(0));\n\n    // setting multiple dependencies will hold until the latest change is ready\n    signal2.set(1);\n    assert_eq!(derived.await, 2);\n}\n"
  },
  {
    "path": "reactive_graph/tests/cleanup.rs",
    "content": "use reactive_graph::{\n    computed::Memo,\n    owner::{on_cleanup, Owner},\n    signal::{RwSignal, Trigger},\n    traits::{Dispose, GetUntracked, Track},\n};\nuse std::sync::Arc;\n\n#[test]\nfn cleanup_on_dispose() {\n    let owner = Owner::new();\n    owner.set();\n\n    struct ExecuteOnDrop(Option<Box<dyn FnOnce() + Send + Sync>>);\n\n    impl ExecuteOnDrop {\n        fn new(f: impl FnOnce() + Send + Sync + 'static) -> Self {\n            Self(Some(Box::new(f)))\n        }\n    }\n    impl Drop for ExecuteOnDrop {\n        fn drop(&mut self) {\n            self.0.take().unwrap()();\n        }\n    }\n\n    let trigger = Trigger::new();\n\n    println!(\"STARTING\");\n\n    let memo = Memo::new(move |_| {\n        trigger.track();\n\n        // An example of why you might want to do this is that\n        // when something goes out of reactive scope you want it to be cleaned up.\n        // The cleaning up might have side effects, and those side effects might cause\n        // re-renders where new `on_cleanup` are registered.\n        let on_drop = ExecuteOnDrop::new(|| {\n            on_cleanup(|| println!(\"Nested cleanup in progress.\"))\n        });\n\n        on_cleanup(move || {\n            println!(\"Cleanup in progress.\");\n            drop(on_drop)\n        });\n    });\n    println!(\"Memo 1: {memo:?}\");\n    memo.get_untracked(); // First cleanup registered.\n\n    memo.dispose(); // Cleanup not run here.\n\n    println!(\"Cleanup should have been executed.\");\n\n    let memo = Memo::new(move |_| {\n        // New cleanup registered. It'll panic here.\n        on_cleanup(move || println!(\"Test passed.\"));\n    });\n    println!(\"Memo 2: {memo:?}\");\n    println!(\"^ Note how the memos have the same key (different versions).\");\n    memo.get_untracked(); // First cleanup registered.\n\n    println!(\"Test passed.\");\n\n    memo.dispose();\n}\n\n#[test]\nfn leak_on_dispose() {\n    let owner = Owner::new();\n    owner.set();\n\n    let trigger = Trigger::new();\n\n    let value = Arc::new(());\n    let weak = Arc::downgrade(&value);\n\n    let memo = Memo::new(move |_| {\n        trigger.track();\n\n        RwSignal::new(value.clone());\n    });\n\n    memo.get_untracked();\n\n    memo.dispose();\n\n    assert!(weak.upgrade().is_none()); // Should have been dropped.\n}\n"
  },
  {
    "path": "reactive_graph/tests/effect.rs",
    "content": "#[cfg(feature = \"effects\")]\npub mod imports {\n    pub use any_spawner::Executor;\n    pub use reactive_graph::{\n        effect::{Effect, RenderEffect},\n        owner::Owner,\n        prelude::*,\n        signal::RwSignal,\n    };\n    pub use std::{\n        mem,\n        sync::{Arc, RwLock},\n    };\n    pub use tokio::task;\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn render_effect_runs() {\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n    task::LocalSet::new()\n        .run_until(async {\n            let a = RwSignal::new(-1);\n\n            // simulate an arbitrary side effect\n            let b = Arc::new(RwLock::new(String::new()));\n\n            // we forget it so it continues running\n            // if it's dropped, it will stop listening\n            mem::forget(RenderEffect::new({\n                let b = b.clone();\n                move |_| {\n                    let formatted = format!(\"Value is {}\", a.get());\n                    *b.write().unwrap() = formatted;\n                }\n            }));\n\n            Executor::tick().await;\n            assert_eq!(b.read().unwrap().as_str(), \"Value is -1\");\n\n            println!(\"setting to 1\");\n            a.set(1);\n\n            Executor::tick().await;\n            assert_eq!(b.read().unwrap().as_str(), \"Value is 1\");\n        })\n        .await;\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn effect_runs() {\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let a = RwSignal::new(-1);\n\n            // simulate an arbitrary side effect\n            let b = Arc::new(RwLock::new(String::new()));\n\n            Effect::new({\n                let b = b.clone();\n                move || {\n                    let formatted = format!(\"Value is {}\", a.get());\n                    *b.write().unwrap() = formatted;\n                }\n            });\n\n            Executor::tick().await;\n            assert_eq!(b.read().unwrap().as_str(), \"Value is -1\");\n\n            println!(\"setting to 1\");\n            a.set(1);\n\n            Executor::tick().await;\n            assert_eq!(b.read().unwrap().as_str(), \"Value is 1\");\n        })\n        .await\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn dynamic_dependencies() {\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let first = RwSignal::new(\"Greg\");\n            let last = RwSignal::new(\"Johnston\");\n            let use_last = RwSignal::new(true);\n\n            let combined_count = Arc::new(RwLock::new(0));\n\n            mem::forget(RenderEffect::new({\n                let combined_count = Arc::clone(&combined_count);\n                move |_| {\n                    *combined_count.write().unwrap() += 1;\n                    if use_last.get() {\n                        println!(\"{} {}\", first.get(), last.get());\n                    } else {\n                        println!(\"{}\", first.get());\n                    }\n                }\n            }));\n\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 1);\n\n            println!(\"\\nsetting `first` to Bob\");\n            first.set(\"Bob\");\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 2);\n\n            println!(\"\\nsetting `last` to Bob\");\n            last.set(\"Thompson\");\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 3);\n\n            println!(\"\\nsetting `use_last` to false\");\n            use_last.set(false);\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 4);\n\n            println!(\"\\nsetting `last` to Jones\");\n            last.set(\"Jones\");\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 4);\n\n            println!(\"\\nsetting `last` to Jones\");\n            last.set(\"Smith\");\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 4);\n\n            println!(\"\\nsetting `last` to Stevens\");\n            last.set(\"Stevens\");\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 4);\n\n            println!(\"\\nsetting `use_last` to true\");\n            use_last.set(true);\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 5);\n        })\n        .await\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn recursive_effect_runs_recursively() {\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n    task::LocalSet::new()\n        .run_until(async {\n            let s = RwSignal::new(0);\n\n            let logged_values = Arc::new(RwLock::new(Vec::new()));\n\n            mem::forget(RenderEffect::new({\n                let logged_values = Arc::clone(&logged_values);\n                move |_| {\n                    let a = s.get();\n                    println!(\"a = {a}\");\n                    logged_values.write().unwrap().push(a);\n                    if a == 0 {\n                        return;\n                    }\n                    s.set(0);\n                }\n            }));\n\n            s.set(1);\n            Executor::tick().await;\n            s.set(2);\n            Executor::tick().await;\n            s.set(3);\n            Executor::tick().await;\n\n            assert_eq!(0, s.get_untracked());\n            assert_eq!(&*logged_values.read().unwrap(), &[0, 1, 0, 2, 0, 3, 0]);\n        })\n        .await;\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn paused_effect_pauses() {\n    use imports::*;\n    use reactive_graph::owner::StoredValue;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let a = RwSignal::new(-1);\n\n            // simulate an arbitrary side effect\n            let runs = StoredValue::new(0);\n\n            let owner = StoredValue::new(None);\n\n            Effect::new({\n                move || {\n                    *owner.write_value() = Owner::current();\n\n                    let _ = a.get();\n                    *runs.write_value() += 1;\n                }\n            });\n\n            Executor::tick().await;\n            assert_eq!(runs.get_value(), 1);\n\n            println!(\"setting to 1\");\n            a.set(1);\n\n            Executor::tick().await;\n            assert_eq!(runs.get_value(), 2);\n\n            println!(\"pausing\");\n            owner.get_value().unwrap().pause();\n\n            println!(\"setting to 2\");\n            a.set(2);\n\n            Executor::tick().await;\n            assert_eq!(runs.get_value(), 2);\n\n            println!(\"resuming\");\n            owner.get_value().unwrap().resume();\n\n            println!(\"setting to 3\");\n            a.set(3);\n\n            Executor::tick().await;\n            println!(\"checking value\");\n            assert_eq!(runs.get_value(), 3);\n        })\n        .await\n}\n"
  },
  {
    "path": "reactive_graph/tests/effect_immediate.rs",
    "content": "#[cfg(feature = \"effects\")]\npub mod imports {\n    pub use any_spawner::Executor;\n    pub use reactive_graph::{\n        effect::ImmediateEffect, owner::Owner, prelude::*, signal::RwSignal,\n    };\n    pub use std::sync::{Arc, RwLock};\n    pub use tokio::task;\n}\n\n#[cfg(feature = \"effects\")]\n#[test]\nfn effect_runs() {\n    use imports::*;\n\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new(-1);\n\n    // simulate an arbitrary side effect\n    let b = Arc::new(RwLock::new(String::new()));\n\n    let _guard = ImmediateEffect::new({\n        let b = b.clone();\n        move || {\n            let formatted = format!(\"Value is {}\", a.get());\n            *b.write().unwrap() = formatted;\n        }\n    });\n    assert_eq!(b.read().unwrap().as_str(), \"Value is -1\");\n\n    println!(\"setting to 1\");\n    a.set(1);\n    assert_eq!(b.read().unwrap().as_str(), \"Value is 1\");\n}\n\n#[cfg(feature = \"effects\")]\n#[test]\nfn dynamic_dependencies() {\n    use imports::*;\n\n    let owner = Owner::new();\n    owner.set();\n\n    let first = RwSignal::new(\"Greg\");\n    let last = RwSignal::new(\"Johnston\");\n    let use_last = RwSignal::new(true);\n\n    let combined_count = Arc::new(RwLock::new(0));\n\n    let _guard = ImmediateEffect::new({\n        let combined_count = Arc::clone(&combined_count);\n        move || {\n            *combined_count.write().unwrap() += 1;\n            if use_last.get() {\n                println!(\"{} {}\", first.get(), last.get());\n            } else {\n                println!(\"{}\", first.get());\n            }\n        }\n    });\n\n    assert_eq!(*combined_count.read().unwrap(), 1);\n\n    println!(\"\\nsetting `first` to Bob\");\n    first.set(\"Bob\");\n    assert_eq!(*combined_count.read().unwrap(), 2);\n\n    println!(\"\\nsetting `last` to Bob\");\n    last.set(\"Thompson\");\n    assert_eq!(*combined_count.read().unwrap(), 3);\n\n    println!(\"\\nsetting `use_last` to false\");\n    use_last.set(false);\n    assert_eq!(*combined_count.read().unwrap(), 4);\n\n    println!(\"\\nsetting `last` to Jones\");\n    last.set(\"Jones\");\n    assert_eq!(*combined_count.read().unwrap(), 4);\n\n    println!(\"\\nsetting `last` to Jones\");\n    last.set(\"Smith\");\n    assert_eq!(*combined_count.read().unwrap(), 4);\n\n    println!(\"\\nsetting `last` to Stevens\");\n    last.set(\"Stevens\");\n    assert_eq!(*combined_count.read().unwrap(), 4);\n\n    println!(\"\\nsetting `use_last` to true\");\n    use_last.set(true);\n    assert_eq!(*combined_count.read().unwrap(), 5);\n}\n\n#[cfg(feature = \"effects\")]\n#[test]\nfn recursive_effect_runs_recursively() {\n    use imports::*;\n\n    let owner = Owner::new();\n    owner.set();\n\n    let s = RwSignal::new(0);\n\n    let logged_values = Arc::new(RwLock::new(Vec::new()));\n\n    let _guard = ImmediateEffect::new({\n        let logged_values = Arc::clone(&logged_values);\n        move || {\n            let a = s.get();\n            println!(\"a = {a}\");\n            logged_values.write().unwrap().push(a);\n            if a == 0 {\n                return;\n            }\n            s.set(0);\n        }\n    });\n\n    s.set(1);\n    s.set(2);\n    s.set(3);\n\n    assert_eq!(0, s.get_untracked());\n    assert_eq!(&*logged_values.read().unwrap(), &[0, 1, 0, 2, 0, 3, 0]);\n}\n\n#[cfg(feature = \"effects\")]\n#[test]\nfn paused_effect_pauses() {\n    use imports::*;\n    use reactive_graph::owner::StoredValue;\n\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new(-1);\n\n    // simulate an arbitrary side effect\n    let runs = StoredValue::new(0);\n\n    let owner = StoredValue::new(None);\n\n    let _guard = ImmediateEffect::new({\n        move || {\n            *owner.write_value() = Owner::current();\n\n            let _ = a.get();\n            *runs.write_value() += 1;\n        }\n    });\n\n    assert_eq!(runs.get_value(), 1);\n\n    println!(\"setting to 1\");\n    a.set(1);\n\n    assert_eq!(runs.get_value(), 2);\n\n    println!(\"pausing\");\n    owner.get_value().unwrap().pause();\n\n    println!(\"setting to 2\");\n    a.set(2);\n\n    assert_eq!(runs.get_value(), 2);\n\n    println!(\"resuming\");\n    owner.get_value().unwrap().resume();\n\n    println!(\"setting to 3\");\n    a.set(3);\n\n    println!(\"checking value\");\n    assert_eq!(runs.get_value(), 3);\n}\n\n#[cfg(feature = \"effects\")]\n#[test]\n#[ignore = \"Parallel signal access can panic.\"]\nfn threaded_chaos_effect() {\n    use imports::*;\n    use reactive_graph::owner::StoredValue;\n\n    const SIGNAL_COUNT: usize = 5;\n    const THREAD_COUNT: usize = 10;\n\n    let owner = Owner::new();\n    owner.set();\n\n    let signals = vec![RwSignal::new(0); SIGNAL_COUNT];\n\n    let runs = StoredValue::new(0);\n\n    let _guard = ImmediateEffect::new({\n        let signals = signals.clone();\n        move || {\n            *runs.write_value() += 1;\n\n            let mut values = vec![];\n            for s in &signals {\n                let v = s.get();\n                values.push(v);\n                if v != 0 {\n                    s.set(v - 1);\n                }\n            }\n            println!(\"{values:?}\");\n        }\n    });\n\n    std::thread::scope(|s| {\n        for _ in 0..THREAD_COUNT {\n            let signals = signals.clone();\n            s.spawn(move || {\n                for s in &signals {\n                    s.set(1);\n                }\n            });\n        }\n    });\n\n    assert_eq!(runs.get_value(), 1 + THREAD_COUNT * SIGNAL_COUNT);\n\n    let values: Vec<_> = signals.iter().map(|s| s.get_untracked()).collect();\n    println!(\"FINAL: {values:?}\");\n}\n\n#[cfg(feature = \"effects\")]\n#[test]\nfn test_batch() {\n    use imports::*;\n    use reactive_graph::{effect::batch, owner::StoredValue};\n\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new(0);\n    let b = RwSignal::new(0);\n\n    let values = StoredValue::new(Vec::new());\n\n    ImmediateEffect::new_scoped(move || {\n        println!(\"{} = {}\", a.get(), b.get());\n        values.write_value().push((a.get(), b.get()));\n    });\n\n    a.set(1);\n    b.set(1);\n\n    batch(move || {\n        a.set(2);\n        b.set(2);\n\n        batch(move || {\n            a.set(3);\n            b.set(3);\n        });\n    });\n\n    assert_eq!(values.get_value(), vec![(0, 0), (1, 0), (1, 1), (3, 3)]);\n}\n"
  },
  {
    "path": "reactive_graph/tests/memo.rs",
    "content": "use reactive_graph::{\n    computed::{ArcMemo, Memo},\n    owner::Owner,\n    prelude::*,\n    signal::RwSignal,\n    wrappers::read::Signal,\n};\nuse std::{\n    rc::Rc,\n    sync::{Arc, RwLock},\n};\n\n#[cfg(feature = \"effects\")]\npub mod imports {\n    pub use any_spawner::Executor;\n    pub use reactive_graph::{\n        computed::{ArcMemo, Memo},\n        effect::{Effect, RenderEffect},\n        prelude::*,\n        signal::RwSignal,\n        wrappers::read::Signal,\n    };\n    pub use std::{\n        mem,\n        rc::Rc,\n        sync::{Arc, RwLock},\n    };\n    pub use tokio::task;\n}\n\n#[test]\nfn memo_calculates_value() {\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new(1);\n    let b = RwSignal::new(2);\n    let c = RwSignal::new(3);\n\n    let d = Memo::new(move |_| a.get() + b.get() + c.get());\n    assert_eq!(d.read(), 6);\n    assert_eq!(d.with_untracked(|n| *n), 6);\n    assert_eq!(d.with(|n| *n), 6);\n    assert_eq!(d.get_untracked(), 6);\n}\n\n#[test]\nfn arc_memo_readable() {\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new(1);\n    let b = RwSignal::new(2);\n    let c = RwSignal::new(3);\n\n    let d = ArcMemo::new(move |_| a.get() + b.get() + c.get());\n    assert_eq!(d.read(), 6);\n}\n\n#[test]\nfn memo_doesnt_repeat_calculation_per_get() {\n    let owner = Owner::new();\n    owner.set();\n\n    let calculations = Arc::new(RwLock::new(0));\n\n    let a = RwSignal::new(1);\n    let b = RwSignal::new(2);\n    let c = RwSignal::new(3);\n\n    let d = Memo::new({\n        let calculations = Arc::clone(&calculations);\n        move |_| {\n            *calculations.write().unwrap() += 1;\n            a.get() + b.get() + c.get()\n        }\n    });\n    assert_eq!(d.get_untracked(), 6);\n    assert_eq!(d.get_untracked(), 6);\n    assert_eq!(d.get_untracked(), 6);\n    assert_eq!(*calculations.read().unwrap(), 1);\n\n    println!(\"\\n\\n**setting to 0**\");\n    a.set(0);\n    assert_eq!(d.get_untracked(), 5);\n    assert_eq!(*calculations.read().unwrap(), 2);\n}\n\n#[test]\nfn nested_memos() {\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new(0); // 1\n    let b = RwSignal::new(0); // 2\n    let c = Memo::new(move |_| {\n        println!(\"calculating C\");\n        a.get() + b.get()\n    }); // 3\n    let d = Memo::new(move |_| {\n        println!(\"calculating D\");\n        c.get() * 2\n    }); // 4\n    let e = Memo::new(move |_| {\n        println!(\"calculating E\");\n        d.get() + 1\n    }); // 5\n    assert_eq!(e.get_untracked(), 1);\n    assert_eq!(d.get_untracked(), 0);\n    assert_eq!(c.get_untracked(), 0);\n\n    println!(\"\\n\\nFirst Set\\n\\n\");\n    a.set(5);\n    assert_eq!(c.get_untracked(), 5);\n    assert_eq!(d.get_untracked(), 10);\n    assert_eq!(e.get_untracked(), 11);\n\n    println!(\"\\n\\nSecond Set\\n\\n\");\n    b.set(1);\n    assert_eq!(e.get_untracked(), 13);\n    assert_eq!(d.get_untracked(), 12);\n    assert_eq!(c.get_untracked(), 6);\n}\n\n#[test]\nfn memo_runs_only_when_inputs_change() {\n    let owner = Owner::new();\n    owner.set();\n\n    let call_count = Arc::new(RwLock::new(0));\n    let a = RwSignal::new(0);\n    let b = RwSignal::new(0);\n    let c = RwSignal::new(0);\n\n    // pretend that this is some kind of expensive computation and we need to access its its value often\n    // we could do this with a derived signal, but that would re-run the computation\n    // memos should only run when their inputs actually change: this is the only point\n    let c = Memo::new({\n        let call_count = call_count.clone();\n        move |_| {\n            let mut call_count = call_count.write().unwrap();\n            *call_count += 1;\n\n            a.get() + b.get() + c.get()\n        }\n    });\n\n    // initially the memo has not been called at all, because it's lazy\n    assert_eq!(*call_count.read().unwrap(), 0);\n\n    // here we access the value a bunch of times\n    assert_eq!(c.get_untracked(), 0);\n    assert_eq!(c.get_untracked(), 0);\n    assert_eq!(c.get_untracked(), 0);\n    assert_eq!(c.get_untracked(), 0);\n    assert_eq!(c.get_untracked(), 0);\n\n    // we've still only called the memo calculation once\n    assert_eq!(*call_count.read().unwrap(), 1);\n\n    // and we only call it again when an input changes\n    a.set(1);\n    assert_eq!(c.get_untracked(), 1);\n    assert_eq!(*call_count.read().unwrap(), 2);\n}\n\n#[test]\nfn diamond_problem() {\n    let owner = Owner::new();\n    owner.set();\n\n    let name = RwSignal::new(\"Greg Johnston\".to_string());\n    let first = Memo::new(move |_| {\n        println!(\"calculating first\");\n        name.get().split_whitespace().next().unwrap().to_string()\n    });\n    let last = Memo::new(move |_| {\n        println!(\"calculating last\");\n        name.get().split_whitespace().nth(1).unwrap().to_string()\n    });\n\n    let combined_count = Arc::new(RwLock::new(0));\n    let combined = Memo::new({\n        let combined_count = Arc::clone(&combined_count);\n        move |_| {\n            println!(\"calculating combined\");\n            let mut combined_count = combined_count.write().unwrap();\n            *combined_count += 1;\n\n            format!(\"{} {}\", first.get(), last.get())\n        }\n    });\n\n    assert_eq!(first.get_untracked(), \"Greg\");\n    assert_eq!(last.get_untracked(), \"Johnston\");\n\n    name.set(\"Will Smith\".to_string());\n    assert_eq!(first.get_untracked(), \"Will\");\n    assert_eq!(last.get_untracked(), \"Smith\");\n    assert_eq!(combined.get_untracked(), \"Will Smith\");\n    // should not have run the memo logic twice, even\n    // though both paths have been updated\n    assert_eq!(*combined_count.read().unwrap(), 1);\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn dynamic_dependencies() {\n    let owner = Owner::new();\n    owner.set();\n\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    let first = RwSignal::new(\"Greg\");\n    let last = RwSignal::new(\"Johnston\");\n    let use_last = RwSignal::new(true);\n    let name = Memo::new(move |_| {\n        if use_last.get() {\n            format!(\"{} {}\", first.get(), last.get())\n        } else {\n            first.get().to_string()\n        }\n    });\n\n    let combined_count = Arc::new(RwLock::new(0));\n\n    // we forget it so it continues running\n    // if it's dropped, it will stop listening\n    println!(\"[Initial]\");\n    Effect::new_sync({\n        let combined_count = Arc::clone(&combined_count);\n        move |_| {\n            println!(\"Effect running.\");\n            _ = name.get();\n            *combined_count.write().unwrap() += 1;\n        }\n    });\n    Executor::tick().await;\n    println!(\"[After 1 tick]\");\n\n    assert_eq!(*combined_count.read().unwrap(), 1);\n\n    println!(\"[Set 'Bob']\");\n    first.set(\"Bob\");\n    Executor::tick().await;\n\n    assert_eq!(name.get_untracked(), \"Bob Johnston\");\n\n    assert_eq!(*combined_count.read().unwrap(), 2);\n\n    println!(\"[Set 'Thompson']\");\n    last.set(\"Thompson\");\n    Executor::tick().await;\n\n    assert_eq!(*combined_count.read().unwrap(), 3);\n\n    use_last.set(false);\n    Executor::tick().await;\n\n    assert_eq!(name.get_untracked(), \"Bob\");\n    assert_eq!(*combined_count.read().unwrap(), 4);\n\n    assert_eq!(*combined_count.read().unwrap(), 4);\n    last.set(\"Jones\");\n    Executor::tick().await;\n\n    assert_eq!(*combined_count.read().unwrap(), 4);\n    last.set(\"Smith\");\n    Executor::tick().await;\n\n    assert_eq!(*combined_count.read().unwrap(), 4);\n    last.set(\"Stevens\");\n    Executor::tick().await;\n\n    assert_eq!(*combined_count.read().unwrap(), 4);\n\n    use_last.set(true);\n    Executor::tick().await;\n    assert_eq!(name.get_untracked(), \"Bob Stevens\");\n\n    assert_eq!(*combined_count.read().unwrap(), 5);\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn render_effect_doesnt_rerun_if_memo_didnt_change() {\n    let owner = Owner::new();\n    owner.set();\n\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let count = RwSignal::new(1);\n            let even = Memo::new(move |_| *count.read() % 2 == 0);\n\n            let combined_count = Arc::new(RwLock::new(0));\n\n            println!(\"[Initial]\");\n            mem::forget(RenderEffect::new({\n                let combined_count = Arc::clone(&combined_count);\n                move |_| {\n                    println!(\"INSIDE RENDEREFFECT\");\n                    *combined_count.write().unwrap() += 1;\n                    println!(\"even = {}\", even.get());\n                }\n            }));\n\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 1);\n            println!(\"[done]\\n\");\n\n            println!(\"\\n[Set Signal to 2]\");\n            count.set(2);\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 2);\n            println!(\"[done]\\n\");\n\n            println!(\"\\n[Set Signal to 4]\");\n            count.set(4);\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 2);\n            println!(\"[done]\\n\");\n        })\n        .await\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn effect_doesnt_rerun_if_memo_didnt_change() {\n    let owner = Owner::new();\n    owner.set();\n\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let count = RwSignal::new(1);\n            let even = Memo::new(move |_| *count.read() % 2 == 0);\n\n            let combined_count = Arc::new(RwLock::new(0));\n\n            Effect::new({\n                let combined_count = Arc::clone(&combined_count);\n                move |_| {\n                    *combined_count.write().unwrap() += 1;\n                    println!(\"even = {}\", even.get());\n                }\n            });\n\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 1);\n\n            count.set(2);\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 2);\n\n            count.set(4);\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 2);\n        })\n        .await\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn effect_depending_on_signal_and_memo_doesnt_rerun_unnecessarily() {\n    let owner = Owner::new();\n    owner.set();\n\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let other_signal = RwSignal::new(false);\n            let count = RwSignal::new(1);\n            let even = Memo::new(move |_| *count.read() % 2 == 0);\n\n            let combined_count = Arc::new(RwLock::new(0));\n\n            Effect::new({\n                let combined_count = Arc::clone(&combined_count);\n                move |_| {\n                    *combined_count.write().unwrap() += 1;\n                    println!(\n                        \"even = {}\\nother_signal = {}\",\n                        even.get(),\n                        other_signal.get()\n                    );\n                }\n            });\n\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 1);\n\n            count.set(2);\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 2);\n\n            count.set(4);\n            Executor::tick().await;\n            assert_eq!(*combined_count.read().unwrap(), 2);\n        })\n        .await\n}\n\n#[test]\nfn unsync_derived_signal_and_memo() {\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new_local(Rc::new(1));\n    let b = RwSignal::new(2);\n    let c = RwSignal::new(3);\n    let d = Memo::new(move |_| *a.get() + b.get() + c.get());\n\n    let e = Rc::new(0);\n    let f = Signal::derive_local(move || d.get() + *e);\n\n    assert_eq!(d.read(), 6);\n    assert_eq!(d.with_untracked(|n| *n), 6);\n    assert_eq!(d.with(|n| *n), 6);\n    assert_eq!(d.get_untracked(), 6);\n\n    // derived signal also works\n    assert_eq!(f.with_untracked(|n| *n), 6);\n    assert_eq!(f.with(|n| *n), 6);\n    assert_eq!(f.get_untracked(), 6);\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn test_memo_multiple_read_guards() {\n    // regression test for https://github.com/leptos-rs/leptos/issues/3158\n    let owner = Owner::new();\n    owner.set();\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n    task::LocalSet::new()\n        .run_until(async {\n            let memo = Memo::<i32>::new_with_compare(|_| 42, |_, _| true);\n\n            Effect::new(move |_| {\n                let guard_a = memo.read();\n                let guard_b = memo.read();\n                assert_eq!(guard_a, 42);\n                assert_eq!(guard_b, 42);\n            });\n            Executor::tick().await;\n        })\n        .await\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn test_memo_read_guard_held() {\n    // regression test for https://github.com/leptos-rs/leptos/issues/3252\n    let owner = Owner::new();\n    owner.set();\n    use imports::*;\n\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n    task::LocalSet::new()\n        .run_until(async {\n            let source = RwSignal::new(0);\n\n            let directly_derived =\n                Memo::new_with_compare(move |_| source.get(), |_, _| true);\n            let indirect = Memo::new_with_compare(\n                move |_| directly_derived.get(),\n                |_, _| true,\n            );\n\n            Effect::new(move |_| {\n                let direct_value = directly_derived.read();\n                let indirect_value = indirect.get();\n                assert_eq!(direct_value, indirect_value);\n            });\n\n            Executor::tick().await;\n            source.set(1);\n            Executor::tick().await;\n            source.set(2);\n            Executor::tick().await;\n        })\n        .await\n}\n\n#[test]\nfn memo_updates_even_if_not_read_until_later() {\n    #![allow(clippy::bool_assert_comparison)]\n\n    let owner = Owner::new();\n    owner.set();\n\n    // regression test for https://github.com/leptos-rs/leptos/issues/3339\n\n    let input = RwSignal::new(0);\n    let first_memo = Memo::new(move |_| input.get() == 1);\n    let second_memo = Memo::new(move |_| first_memo.get());\n\n    assert_eq!(input.get(), 0);\n    assert_eq!(first_memo.get(), false);\n\n    println!(\"update to 1\");\n    input.set(1);\n    assert_eq!(input.get(), 1);\n    println!(\"read memo 1\");\n    assert_eq!(first_memo.get(), true);\n    println!(\"read memo 2\");\n    assert_eq!(second_memo.get(), true);\n\n    // this time, we don't read the memo\n    println!(\"\\nupdate to 2\");\n    input.set(2);\n    assert_eq!(input.get(), 2);\n    println!(\"read memo 1\");\n    assert_eq!(first_memo.get(), false);\n\n    println!(\"\\nupdate to 3\");\n    input.set(3);\n    assert_eq!(input.get(), 3);\n    println!(\"read memo 1\");\n    assert_eq!(first_memo.get(), false);\n    println!(\"read memo 2\");\n    assert_eq!(second_memo.get(), false);\n}\n"
  },
  {
    "path": "reactive_graph/tests/signal.rs",
    "content": "use reactive_graph::{\n    owner::Owner,\n    signal::{arc_signal, signal, ArcRwSignal, RwSignal},\n    traits::{\n        Dispose, Get, GetUntracked, IntoInner, Read, Set, Update,\n        UpdateUntracked, With, WithUntracked, Write,\n    },\n};\n\n#[test]\nfn create_arc_rw_signal() {\n    let a = ArcRwSignal::new(0);\n    assert_eq!(a.read(), 0);\n    assert_eq!(a.get(), 0);\n    assert_eq!(a.get_untracked(), 0);\n    assert_eq!(a.with_untracked(|n| n + 1), 1);\n    assert_eq!(a.with(|n| n + 1), 1);\n    assert_eq!(format!(\"{}\", a.read()), \"0\");\n}\n\n#[test]\nfn update_arc_rw_signal() {\n    let a = ArcRwSignal::new(0);\n    *a.write() += 1;\n    assert_eq!(a.get(), 1);\n    a.update(|n| *n += 1);\n    assert_eq!(a.get(), 2);\n    a.update_untracked(|n| *n += 1);\n    assert_eq!(a.get(), 3);\n    a.set(4);\n    assert_eq!(a.get(), 4);\n}\n\n#[test]\nfn create_arc_signal() {\n    let (a, _) = arc_signal(0);\n    assert_eq!(a.read(), 0);\n    assert_eq!(a.get(), 0);\n    assert_eq!(a.with_untracked(|n| n + 1), 1);\n    assert_eq!(a.with(|n| n + 1), 1);\n}\n\n#[test]\nfn update_arc_signal() {\n    let (a, set_a) = arc_signal(0);\n    *set_a.write() += 1;\n    assert_eq!(a.get(), 1);\n    set_a.update(|n| *n += 1);\n    assert_eq!(a.get(), 2);\n    set_a.update_untracked(|n| *n += 1);\n    assert_eq!(a.get(), 3);\n    set_a.set(4);\n    assert_eq!(a.get(), 4);\n}\n\n#[test]\nfn create_rw_signal() {\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new(0);\n    assert_eq!(a.read(), 0);\n    assert_eq!(a.get(), 0);\n    assert_eq!(a.with_untracked(|n| n + 1), 1);\n    assert_eq!(a.with(|n| n + 1), 1);\n}\n\n#[test]\nfn update_rw_signal() {\n    let owner = Owner::new();\n    owner.set();\n\n    let a = RwSignal::new(1);\n    assert_eq!(a.read(), 1);\n    assert_eq!(a.get(), 1);\n    a.update(|n| *n += 1);\n    assert_eq!(a.get(), 2);\n    a.update_untracked(|n| *n += 1);\n    assert_eq!(a.get(), 3);\n    a.set(4);\n    assert_eq!(a.get(), 4);\n}\n\n#[test]\nfn create_signal() {\n    let owner = Owner::new();\n    owner.set();\n\n    let (a, _) = signal(0);\n    assert_eq!(a.read(), 0);\n    assert_eq!(a.get(), 0);\n    assert_eq!(a.get_untracked(), 0);\n    assert_eq!(a.with_untracked(|n| n + 1), 1);\n    assert_eq!(a.with(|n| n + 1), 1);\n}\n\n#[test]\nfn update_signal() {\n    let owner = Owner::new();\n    owner.set();\n\n    let (a, set_a) = signal(1);\n    assert_eq!(a.get(), 1);\n    set_a.update(|n| *n += 1);\n    assert_eq!(a.get(), 2);\n    set_a.update_untracked(|n| *n += 1);\n    assert_eq!(a.get(), 3);\n    set_a.set(4);\n    assert_eq!(a.get(), 4);\n}\n\n#[test]\nfn into_inner_signal() {\n    let owner = Owner::new();\n    owner.set();\n\n    let rw_signal = RwSignal::new(1);\n    assert_eq!(rw_signal.get(), 1);\n    assert_eq!(rw_signal.into_inner(), Some(1));\n}\n\n#[test]\nfn into_inner_arc_signal() {\n    let owner = Owner::new();\n    owner.set();\n\n    let (a, b) = arc_signal(2);\n    assert_eq!(a.get(), 2);\n    std::mem::drop(b);\n    assert_eq!(a.into_inner(), Some(2));\n}\n\n#[test]\nfn into_inner_non_arc_signal() {\n    let owner = Owner::new();\n    owner.set();\n\n    let (a, b) = signal(2);\n    assert_eq!(a.get(), 2);\n    b.dispose();\n    assert_eq!(a.into_inner(), Some(2));\n}\n"
  },
  {
    "path": "reactive_graph/tests/watch.rs",
    "content": "#[cfg(feature = \"effects\")]\nuse any_spawner::Executor;\n#[cfg(feature = \"effects\")]\nuse reactive_graph::owner::Owner;\n#[cfg(feature = \"effects\")]\nuse reactive_graph::{effect::Effect, prelude::*, signal::RwSignal};\n#[cfg(feature = \"effects\")]\nuse std::sync::{Arc, RwLock};\n#[cfg(feature = \"effects\")]\nuse tokio::task;\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn watch_runs() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let a = RwSignal::new(-1);\n\n            // simulate an arbitrary side effect\n            let b = Arc::new(RwLock::new(String::new()));\n\n            let effect = Effect::watch(\n                move || a.get(),\n                {\n                    let b = b.clone();\n\n                    move |a, prev_a, prev_ret| {\n                        let formatted = format!(\n                            \"Value is {a}; Prev is {prev_a:?}; Prev return is \\\n                             {prev_ret:?}\"\n                        );\n                        *b.write().unwrap() = formatted;\n\n                        a + 10\n                    }\n                },\n                false,\n            );\n\n            Executor::tick().await;\n            assert_eq!(b.read().unwrap().as_str(), \"\");\n\n            a.set(1);\n\n            Executor::tick().await;\n            assert_eq!(\n                b.read().unwrap().as_str(),\n                \"Value is 1; Prev is Some(-1); Prev return is None\"\n            );\n\n            a.set(2);\n\n            Executor::tick().await;\n            assert_eq!(\n                b.read().unwrap().as_str(),\n                \"Value is 2; Prev is Some(1); Prev return is Some(11)\"\n            );\n\n            effect.stop();\n\n            *b.write().unwrap() = \"nothing happened\".to_string();\n            a.set(3);\n\n            Executor::tick().await;\n            assert_eq!(b.read().unwrap().as_str(), \"nothing happened\");\n        })\n        .await\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn watch_runs_immediately() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let a = RwSignal::new(-1);\n\n            // simulate an arbitrary side effect\n            let b = Arc::new(RwLock::new(String::new()));\n\n            Effect::watch(\n                move || a.get(),\n                {\n                    let b = b.clone();\n\n                    move |a, prev_a, prev_ret| {\n                        let formatted = format!(\n                            \"Value is {a}; Prev is {prev_a:?}; Prev return is \\\n                             {prev_ret:?}\"\n                        );\n                        *b.write().unwrap() = formatted;\n\n                        a + 10\n                    }\n                },\n                true,\n            );\n\n            Executor::tick().await;\n            assert_eq!(\n                b.read().unwrap().as_str(),\n                \"Value is -1; Prev is None; Prev return is None\"\n            );\n\n            a.set(1);\n\n            Executor::tick().await;\n            assert_eq!(\n                b.read().unwrap().as_str(),\n                \"Value is 1; Prev is Some(-1); Prev return is Some(9)\"\n            );\n        })\n        .await\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn watch_ignores_callback() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let a = RwSignal::new(-1);\n            let b = RwSignal::new(0);\n\n            // simulate an arbitrary side effect\n            let s = Arc::new(RwLock::new(String::new()));\n\n            Effect::watch(\n                move || a.get(),\n                {\n                    let s = s.clone();\n\n                    move |a, _, _| {\n                        let formatted =\n                            format!(\"Value a is {a}; Value b is {}\", b.get());\n                        *s.write().unwrap() = formatted;\n\n                        a + 10\n                    }\n                },\n                false,\n            );\n\n            Executor::tick().await;\n\n            a.set(1);\n\n            Executor::tick().await;\n            assert_eq!(\n                s.read().unwrap().as_str(),\n                \"Value a is 1; Value b is 0\"\n            );\n\n            *s.write().unwrap() = \"nothing happened\".to_string();\n            b.set(10);\n\n            Executor::tick().await;\n            assert_eq!(s.read().unwrap().as_str(), \"nothing happened\");\n\n            a.set(2);\n\n            Executor::tick().await;\n            assert_eq!(\n                s.read().unwrap().as_str(),\n                \"Value a is 2; Value b is 10\"\n            );\n        })\n        .await\n}\n\n#[cfg(feature = \"effects\")]\n#[tokio::test]\nasync fn deprecated_watch_runs() {\n    _ = Executor::init_tokio();\n    let owner = Owner::new();\n    owner.set();\n\n    task::LocalSet::new()\n        .run_until(async {\n            let a = RwSignal::new(-1);\n\n            // simulate an arbitrary side effect\n            let b = Arc::new(RwLock::new(String::new()));\n\n            #[allow(deprecated)]\n            let effect = reactive_graph::effect::watch(\n                move || a.get(),\n                {\n                    let b = b.clone();\n\n                    move |a, prev_a, prev_ret| {\n                        let formatted = format!(\n                            \"Value is {a}; Prev is {prev_a:?}; Prev return is \\\n                             {prev_ret:?}\"\n                        );\n                        *b.write().unwrap() = formatted;\n\n                        a + 10\n                    }\n                },\n                false,\n            );\n\n            Executor::tick().await;\n            assert_eq!(b.read().unwrap().as_str(), \"\");\n\n            a.set(1);\n\n            Executor::tick().await;\n            assert_eq!(\n                b.read().unwrap().as_str(),\n                \"Value is 1; Prev is Some(-1); Prev return is None\"\n            );\n\n            a.set(2);\n\n            Executor::tick().await;\n            assert_eq!(\n                b.read().unwrap().as_str(),\n                \"Value is 2; Prev is Some(1); Prev return is Some(11)\"\n            );\n\n            effect();\n\n            *b.write().unwrap() = \"nothing happened\".to_string();\n            a.set(3);\n\n            Executor::tick().await;\n            assert_eq!(b.read().unwrap().as_str(), \"nothing happened\");\n        })\n        .await\n}\n"
  },
  {
    "path": "reactive_stores/Cargo.toml",
    "content": "[package]\nname = \"reactive_stores\"\nversion = \"0.4.2\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Stores for holding deeply-nested reactive state while maintaining fine-grained reactive tracking.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[features]\ndefault = []\nserde = [\"dep:serde\"]\nslotmap = [\"dep:slotmap\"]\n\n[dependencies]\nguardian = { workspace = true, default-features = true }\nitertools = { workspace = true, default-features = true }\nor_poisoned = { workspace = true }\npaste = { workspace = true, default-features = true }\nreactive_graph = { workspace = true }\nrustc-hash = { workspace = true, default-features = true }\nreactive_stores_macro = { workspace = true }\nsend_wrapper = { workspace = true, default-features = true }\nserde = { workspace = true, optional = true }\nslotmap = { workspace = true, optional = true }\nindexmap = { workspace = true, default-features = true }\n\n[dev-dependencies]\ntokio = { features = [\n\t\"rt-multi-thread\",\n\t\"macros\",\n], workspace = true, default-features = true }\ntokio-test = { workspace = true, default-features = true }\nany_spawner = { workspace = true, features = [\"futures-executor\", \"tokio\"] }\nreactive_graph = { workspace = true, features = [\"effects\"] }\nleptos = { path = \"../leptos\", features = [\"csr\"] }\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(leptos_debuginfo)'] }\n"
  },
  {
    "path": "reactive_stores/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "reactive_stores/README.md",
    "content": "# Stores\n\nStores are a data structure for nested reactivity.\n\nThe [`reactive_graph`](https://crates.io/crates/reactive_graph) crate provides primitives for fine-grained reactivity\nvia signals, memos, and effects.\n\nThis crate extends that reactivity to support reactive access to nested structs, without the need to create nested signals.\n\nUsing the `#[derive(Store)]` macro on a struct creates a series of getters that allow accessing each field. Individual fields \ncan then be read as if they were signals. Changes to parents will notify their children, but changing one sibling field will  \nnot notify any of the others, nor will it require diffing those sibling fields (unlike earlier solutions using memoized “slices”).\n\nThis is published for use with the Leptos framework but can be used in any scenario where `reactive_graph` is being used \nfor reactivity.\n"
  },
  {
    "path": "reactive_stores/src/arc_field.rs",
    "content": "use crate::{\n    path::{StorePath, StorePathSegment},\n    ArcStore, AtIndex, AtKeyed, DerefedField, KeyMap, KeyedAccess,\n    KeyedSubfield, Store, StoreField, StoreFieldTrigger, Subfield,\n};\nuse reactive_graph::{\n    owner::Storage,\n    traits::{\n        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,\n        Write,\n    },\n};\nuse std::{\n    fmt::Debug,\n    hash::Hash,\n    ops::{Deref, DerefMut, IndexMut},\n    panic::Location,\n    sync::Arc,\n};\n\n/// Reference-counted access to a single field of type `T`.\n///\n/// This can be used to erase the chain of field-accessors, to make it easier to pass this into\n/// another component or function without needing to specify the full type signature.\npub struct ArcField<T>\nwhere\n    T: 'static,\n{\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    path: Arc<dyn Fn() -> StorePath + Send + Sync>,\n    path_unkeyed: Arc<dyn Fn() -> StorePath + Send + Sync>,\n    get_trigger: Arc<dyn Fn(StorePath) -> StoreFieldTrigger + Send + Sync>,\n    get_trigger_unkeyed:\n        Arc<dyn Fn(StorePath) -> StoreFieldTrigger + Send + Sync>,\n    read: Arc<dyn Fn() -> Option<StoreFieldReader<T>> + Send + Sync>,\n    pub(crate) write:\n        Arc<dyn Fn() -> Option<StoreFieldWriter<T>> + Send + Sync>,\n    keys: Arc<dyn Fn() -> Option<KeyMap> + Send + Sync>,\n    track_field: Arc<dyn Fn() + Send + Sync>,\n    notify: Arc<dyn Fn() + Send + Sync>,\n}\n\nimpl<T> Debug for ArcField<T>\nwhere\n    T: 'static,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut f = f.debug_struct(\"ArcField\");\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let f = f.field(\"defined_at\", &self.defined_at);\n        f.finish_non_exhaustive()\n    }\n}\n\npub struct StoreFieldReader<T>(Box<dyn Deref<Target = T>>);\n\nimpl<T> StoreFieldReader<T> {\n    pub fn new(inner: impl Deref<Target = T> + 'static) -> Self {\n        Self(Box::new(inner))\n    }\n}\n\nimpl<T> Deref for StoreFieldReader<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.0.deref()\n    }\n}\n\npub struct StoreFieldWriter<T>(Box<dyn UntrackableGuard<Target = T>>);\n\nimpl<T> StoreFieldWriter<T> {\n    pub fn new(inner: impl UntrackableGuard<Target = T> + 'static) -> Self {\n        Self(Box::new(inner))\n    }\n}\n\nimpl<T> Deref for StoreFieldWriter<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.0.deref()\n    }\n}\n\nimpl<T> DerefMut for StoreFieldWriter<T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.0.deref_mut()\n    }\n}\n\nimpl<T> UntrackableGuard for StoreFieldWriter<T> {\n    fn untrack(&mut self) {\n        self.0.untrack();\n    }\n}\n\nimpl<T> StoreField for ArcField<T> {\n    type Value = T;\n    type Reader = StoreFieldReader<T>;\n    type Writer = StoreFieldWriter<T>;\n\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        (self.get_trigger)(path)\n    }\n\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        (self.get_trigger_unkeyed)(path)\n    }\n\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        (self.path)()\n    }\n\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        (self.path_unkeyed)()\n    }\n\n    fn reader(&self) -> Option<Self::Reader> {\n        (self.read)().map(StoreFieldReader::new)\n    }\n\n    fn writer(&self) -> Option<Self::Writer> {\n        (self.write)().map(StoreFieldWriter::new)\n    }\n\n    fn keys(&self) -> Option<KeyMap> {\n        (self.keys)()\n    }\n}\n\nimpl<T, S> From<Store<T, S>> for ArcField<T>\nwhere\n    T: 'static,\n    S: Storage<ArcStore<T>>,\n{\n    #[track_caller]\n    fn from(value: Store<T, S>) -> Self {\n        ArcField {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            path: Arc::new(move || value.path().into_iter().collect()),\n            path_unkeyed: Arc::new(move || {\n                value.path_unkeyed().into_iter().collect()\n            }),\n            get_trigger: Arc::new(move |path| value.get_trigger(path)),\n            get_trigger_unkeyed: Arc::new(move |path| {\n                value.get_trigger_unkeyed(path)\n            }),\n            read: Arc::new(move || value.reader().map(StoreFieldReader::new)),\n            write: Arc::new(move || value.writer().map(StoreFieldWriter::new)),\n            keys: Arc::new(move || value.keys()),\n            track_field: Arc::new(move || value.track_field()),\n            notify: Arc::new(move || value.notify()),\n        }\n    }\n}\n\nimpl<T> From<ArcStore<T>> for ArcField<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    #[track_caller]\n    fn from(value: ArcStore<T>) -> Self {\n        ArcField {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            path: Arc::new({\n                let value = value.clone();\n                move || value.path().into_iter().collect()\n            }),\n            path_unkeyed: Arc::new({\n                let value = value.clone();\n                move || value.path_unkeyed().into_iter().collect()\n            }),\n            get_trigger: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger(path)\n            }),\n            get_trigger_unkeyed: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger_unkeyed(path)\n            }),\n            read: Arc::new({\n                let value = value.clone();\n                move || value.reader().map(StoreFieldReader::new)\n            }),\n            write: Arc::new({\n                let value = value.clone();\n                move || value.writer().map(StoreFieldWriter::new)\n            }),\n            keys: Arc::new({\n                let value = value.clone();\n                move || value.keys()\n            }),\n            track_field: Arc::new({\n                let value = value.clone();\n                move || value.track_field()\n            }),\n            notify: Arc::new({\n                let value = value.clone();\n                move || value.notify()\n            }),\n        }\n    }\n}\n\nimpl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for ArcField<T>\nwhere\n    T: Send + Sync,\n    Subfield<Inner, Prev, T>: Clone,\n    Inner: StoreField<Value = Prev> + Send + Sync + 'static,\n    Prev: 'static,\n{\n    #[track_caller]\n    fn from(value: Subfield<Inner, Prev, T>) -> Self {\n        ArcField {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            path: Arc::new({\n                let value = value.clone();\n                move || value.path().into_iter().collect()\n            }),\n            path_unkeyed: Arc::new({\n                let value = value.clone();\n                move || value.path_unkeyed().into_iter().collect()\n            }),\n            get_trigger: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger(path)\n            }),\n            get_trigger_unkeyed: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger_unkeyed(path)\n            }),\n            read: Arc::new({\n                let value = value.clone();\n                move || value.reader().map(StoreFieldReader::new)\n            }),\n            write: Arc::new({\n                let value = value.clone();\n                move || value.writer().map(StoreFieldWriter::new)\n            }),\n            keys: Arc::new({\n                let value = value.clone();\n                move || value.keys()\n            }),\n            track_field: Arc::new({\n                let value = value.clone();\n                move || value.track_field()\n            }),\n            notify: Arc::new({\n                let value = value.clone();\n                move || value.notify()\n            }),\n        }\n    }\n}\n\nimpl<Inner, T> From<DerefedField<Inner>> for ArcField<T>\nwhere\n    Inner: Clone + StoreField + Send + Sync + 'static,\n    Inner::Value: Deref<Target = T> + DerefMut,\n    T: Sized + 'static,\n{\n    #[track_caller]\n    fn from(value: DerefedField<Inner>) -> Self {\n        ArcField {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            path: Arc::new({\n                let value = value.clone();\n                move || value.path().into_iter().collect()\n            }),\n            path_unkeyed: Arc::new({\n                let value = value.clone();\n                move || value.path_unkeyed().into_iter().collect()\n            }),\n            get_trigger: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger(path)\n            }),\n            get_trigger_unkeyed: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger_unkeyed(path)\n            }),\n            read: Arc::new({\n                let value = value.clone();\n                move || value.reader().map(StoreFieldReader::new)\n            }),\n            write: Arc::new({\n                let value = value.clone();\n                move || value.writer().map(StoreFieldWriter::new)\n            }),\n            keys: Arc::new({\n                let value = value.clone();\n                move || value.keys()\n            }),\n            track_field: Arc::new({\n                let value = value.clone();\n                move || value.track_field()\n            }),\n            notify: Arc::new({\n                let value = value.clone();\n                move || value.notify()\n            }),\n        }\n    }\n}\n\nimpl<Inner, Prev> From<AtIndex<Inner, Prev>> for ArcField<Prev::Output>\nwhere\n    AtIndex<Inner, Prev>: Clone,\n    Inner: StoreField<Value = Prev> + Send + Sync + 'static,\n    Prev: IndexMut<usize> + Send + Sync + 'static,\n    Prev::Output: Sized + Send + Sync,\n{\n    #[track_caller]\n    fn from(value: AtIndex<Inner, Prev>) -> Self {\n        ArcField {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            path: Arc::new({\n                let value = value.clone();\n                move || value.path().into_iter().collect()\n            }),\n            path_unkeyed: Arc::new({\n                let value = value.clone();\n                move || value.path_unkeyed().into_iter().collect()\n            }),\n            get_trigger: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger(path)\n            }),\n            get_trigger_unkeyed: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger_unkeyed(path)\n            }),\n            read: Arc::new({\n                let value = value.clone();\n                move || value.reader().map(StoreFieldReader::new)\n            }),\n            write: Arc::new({\n                let value = value.clone();\n                move || value.writer().map(StoreFieldWriter::new)\n            }),\n            keys: Arc::new({\n                let value = value.clone();\n                move || value.keys()\n            }),\n            track_field: Arc::new({\n                let value = value.clone();\n                move || value.track_field()\n            }),\n            notify: Arc::new({\n                let value = value.clone();\n                move || value.notify()\n            }),\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> From<AtKeyed<Inner, Prev, K, T>> for ArcField<T::Value>\nwhere\n    AtKeyed<Inner, Prev, K, T>: Clone,\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev> + Send + Sync + 'static,\n    Prev: 'static,\n    T: KeyedAccess<K> + 'static,\n    T::Value: Sized,\n{\n    #[track_caller]\n    fn from(value: AtKeyed<Inner, Prev, K, T>) -> Self {\n        ArcField {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            path: Arc::new({\n                let value = value.clone();\n                move || value.path().into_iter().collect()\n            }),\n            path_unkeyed: Arc::new({\n                let value = value.clone();\n                move || value.path_unkeyed().into_iter().collect()\n            }),\n            get_trigger: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger(path)\n            }),\n            get_trigger_unkeyed: Arc::new({\n                let value = value.clone();\n                move |path| value.get_trigger_unkeyed(path)\n            }),\n            read: Arc::new({\n                let value = value.clone();\n                move || value.reader().map(StoreFieldReader::new)\n            }),\n            write: Arc::new({\n                let value = value.clone();\n                move || value.writer().map(StoreFieldWriter::new)\n            }),\n            keys: Arc::new({\n                let value = value.clone();\n                move || value.keys()\n            }),\n            track_field: Arc::new({\n                let value = value.clone();\n                move || value.track_field()\n            }),\n            notify: Arc::new({\n                let value = value.clone();\n                move || value.notify()\n            }),\n        }\n    }\n}\n\nimpl<T> Clone for ArcField<T> {\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            path: self.path.clone(),\n            path_unkeyed: self.path_unkeyed.clone(),\n            get_trigger: Arc::clone(&self.get_trigger),\n            get_trigger_unkeyed: Arc::clone(&self.get_trigger_unkeyed),\n            read: Arc::clone(&self.read),\n            write: Arc::clone(&self.write),\n            keys: Arc::clone(&self.keys),\n            track_field: Arc::clone(&self.track_field),\n            notify: Arc::clone(&self.notify),\n        }\n    }\n}\n\nimpl<T> DefinedAt for ArcField<T> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> Notify for ArcField<T> {\n    fn notify(&self) {\n        (self.notify)()\n    }\n}\n\nimpl<T> Track for ArcField<T> {\n    fn track(&self) {\n        (self.track_field)();\n    }\n}\n\nimpl<T> ReadUntracked for ArcField<T> {\n    type Value = StoreFieldReader<T>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        (self.read)()\n    }\n}\n\nimpl<T> Write for ArcField<T> {\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        (self.write)()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        let mut guard = (self.write)()?;\n        guard.untrack();\n        Some(guard)\n    }\n}\n\nimpl<T> IsDisposed for ArcField<T> {\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/deref.rs",
    "content": "use crate::{\n    path::{StorePath, StorePathSegment},\n    store_field::StoreField,\n    KeyMap, StoreFieldTrigger,\n};\nuse reactive_graph::{\n    signal::guards::{Mapped, MappedMut},\n    traits::{\n        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,\n        Write,\n    },\n};\nuse std::{\n    ops::{Deref, DerefMut},\n    panic::Location,\n};\n\n/// Maps a store field that is a smart pointer to a subfield of the dereferenced inner type.\npub trait DerefField\nwhere\n    Self: StoreField,\n    Self::Value: Deref + DerefMut,\n    <Self::Value as Deref>::Target: Sized + 'static,\n{\n    /// Returns a new store field with the value mapped to the target type of dereferencing this\n    /// field\n    ///\n    /// For example, if you have a store field with a `Box<T>`, `.deref_field()` will return a\n    /// new store field containing a `T`.\n    fn deref_field(self) -> DerefedField<Self>;\n}\n\nimpl<S> DerefField for S\nwhere\n    S: StoreField,\n    S::Value: Deref + DerefMut,\n    <S::Value as Deref>::Target: Sized + 'static,\n{\n    #[track_caller]\n    fn deref_field(self) -> DerefedField<Self> {\n        DerefedField {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: self,\n        }\n    }\n}\n\n/// A wrapper from a store field containing a smart pointer to a store field containing the\n/// dereferenced target type of that smart pointer.\n#[derive(Debug, Copy, Clone)]\npub struct DerefedField<S> {\n    inner: S,\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n}\n\nimpl<S> StoreField for DerefedField<S>\nwhere\n    S: StoreField,\n    S::Value: Deref + DerefMut,\n    <S::Value as Deref>::Target: Sized + 'static,\n{\n    type Value = <S::Value as Deref>::Target;\n    type Reader = Mapped<S::Reader, Self::Value>;\n    type Writer = MappedMut<S::Writer, Self::Value>;\n\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger(path)\n    }\n\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger_unkeyed(path)\n    }\n\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner.path()\n    }\n\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner.path_unkeyed()\n    }\n\n    fn reader(&self) -> Option<Self::Reader> {\n        let inner = self.inner.reader()?;\n        Some(Mapped::new_with_guard(inner, |n| n.deref()))\n    }\n    fn writer(&self) -> Option<Self::Writer> {\n        let inner = self.inner.writer()?;\n        Some(MappedMut::new(inner, |n| n.deref(), |n| n.deref_mut()))\n    }\n    #[inline(always)]\n    fn keys(&self) -> Option<KeyMap> {\n        self.inner.keys()\n    }\n}\n\nimpl<S> DefinedAt for DerefedField<S>\nwhere\n    S: StoreField,\n    S::Value: Deref + DerefMut,\n    <S::Value as Deref>::Target: Sized + 'static,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\nimpl<S> IsDisposed for DerefedField<S>\nwhere\n    S: IsDisposed,\n{\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\nimpl<S> Notify for DerefedField<S>\nwhere\n    S: StoreField,\n    S::Value: Deref + DerefMut,\n    <S::Value as Deref>::Target: Sized + 'static,\n{\n    fn notify(&self) {\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.notify();\n        trigger.children.notify();\n    }\n}\nimpl<S> Track for DerefedField<S>\nwhere\n    S: StoreField,\n    S::Value: Deref + DerefMut,\n    <S::Value as Deref>::Target: Sized + 'static,\n{\n    fn track(&self) {\n        self.track_field();\n    }\n}\nimpl<S> ReadUntracked for DerefedField<S>\nwhere\n    S: StoreField,\n    S::Value: Deref + DerefMut,\n    <S::Value as Deref>::Target: Sized + 'static,\n{\n    type Value = <Self as StoreField>::Reader;\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.reader()\n    }\n}\nimpl<S> Write for DerefedField<S>\nwhere\n    S: StoreField,\n    S::Value: Deref + DerefMut,\n    <S::Value as Deref>::Target: Sized + 'static,\n{\n    type Value = <S::Value as Deref>::Target;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.writer()\n    }\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.writer().map(|mut writer| {\n            writer.untrack();\n            writer\n        })\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/field.rs",
    "content": "use crate::{\n    arc_field::{StoreFieldReader, StoreFieldWriter},\n    path::{StorePath, StorePathSegment},\n    ArcField, ArcStore, AtIndex, AtKeyed, DerefedField, KeyMap, KeyedAccess,\n    KeyedSubfield, Store, StoreField, StoreFieldTrigger, Subfield,\n};\nuse reactive_graph::{\n    owner::{ArenaItem, Storage, SyncStorage},\n    traits::{\n        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,\n        Write,\n    },\n};\nuse std::{\n    fmt::Debug,\n    hash::Hash,\n    ops::{Deref, DerefMut, IndexMut},\n    panic::Location,\n};\n\n/// Wraps access to a single field of type `T`.\n///\n/// This can be used to erase the chain of field-accessors, to make it easier to pass this into\n/// another component or function without needing to specify the full type signature.\npub struct Field<T, S = SyncStorage>\nwhere\n    T: 'static,\n{\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    inner: ArenaItem<ArcField<T>, S>,\n}\n\nimpl<T, S> Debug for Field<T, S>\nwhere\n    T: 'static,\n    S: Debug,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut f = f.debug_struct(\"Field\");\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let f = f.field(\"defined_at\", &self.defined_at);\n        f.field(\"inner\", &self.inner).finish()\n    }\n}\n\nimpl<T, S> StoreField for Field<T, S>\nwhere\n    S: Storage<ArcField<T>>,\n{\n    type Value = T;\n    type Reader = StoreFieldReader<T>;\n    type Writer = StoreFieldWriter<T>;\n\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.get_trigger(path))\n            .unwrap_or_default()\n    }\n\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.get_trigger_unkeyed(path))\n            .unwrap_or_default()\n    }\n\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.path().into_iter().collect::<Vec<_>>())\n            .unwrap_or_default()\n    }\n\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .try_get_value()\n            .map(|inner| inner.path_unkeyed().into_iter().collect::<Vec<_>>())\n            .unwrap_or_default()\n    }\n\n    fn reader(&self) -> Option<Self::Reader> {\n        self.inner.try_get_value().and_then(|inner| inner.reader())\n    }\n\n    fn writer(&self) -> Option<Self::Writer> {\n        self.inner.try_get_value().and_then(|inner| inner.writer())\n    }\n\n    fn keys(&self) -> Option<KeyMap> {\n        self.inner.try_get_value().and_then(|n| n.keys())\n    }\n}\n\nimpl<T, S> From<Store<T, S>> for Field<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStore<T>> + Storage<ArcField<T>>,\n{\n    #[track_caller]\n    fn from(value: Store<T, S>) -> Self {\n        Field {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value.into()),\n        }\n    }\n}\n\nimpl<T, S> From<ArcField<T>> for Field<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcField<T>>,\n{\n    #[track_caller]\n    fn from(value: ArcField<T>) -> Self {\n        Field {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\nimpl<T, S> From<ArcStore<T>> for Field<T, S>\nwhere\n    T: Send + Sync + 'static,\n    S: Storage<ArcStore<T>> + Storage<ArcField<T>>,\n{\n    #[track_caller]\n    fn from(value: ArcStore<T>) -> Self {\n        Field {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value.into()),\n        }\n    }\n}\n\nimpl<Inner, Prev, T, S> From<Subfield<Inner, Prev, T>> for Field<T, S>\nwhere\n    T: Send + Sync,\n    S: Storage<ArcField<T>>,\n    Subfield<Inner, Prev, T>: Clone,\n    Inner: StoreField<Value = Prev> + Send + Sync + 'static,\n    Prev: 'static,\n{\n    #[track_caller]\n    fn from(value: Subfield<Inner, Prev, T>) -> Self {\n        Field {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value.into()),\n        }\n    }\n}\n\nimpl<Inner, T> From<DerefedField<Inner>> for Field<T>\nwhere\n    Inner: Clone + StoreField + Send + Sync + 'static,\n    Inner::Value: Deref<Target = T> + DerefMut,\n    T: Sized + 'static,\n{\n    #[track_caller]\n    fn from(value: DerefedField<Inner>) -> Self {\n        Field {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value.into()),\n        }\n    }\n}\n\nimpl<Inner, Prev, S> From<AtIndex<Inner, Prev>> for Field<Prev::Output, S>\nwhere\n    AtIndex<Inner, Prev>: Clone,\n    S: Storage<ArcField<Prev::Output>>,\n    Inner: StoreField<Value = Prev> + Send + Sync + 'static,\n    Prev: IndexMut<usize> + Send + Sync + 'static,\n    Prev::Output: Sized + Send + Sync,\n{\n    #[track_caller]\n    fn from(value: AtIndex<Inner, Prev>) -> Self {\n        Field {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value.into()),\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T, S> From<AtKeyed<Inner, Prev, K, T>>\n    for Field<T::Value, S>\nwhere\n    S: Storage<ArcField<T::Value>>,\n    AtKeyed<Inner, Prev, K, T>: Clone,\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev> + Send + Sync + 'static,\n    Prev: 'static,\n    T: KeyedAccess<K> + 'static,\n    T::Value: Sized,\n{\n    #[track_caller]\n    fn from(value: AtKeyed<Inner, Prev, K, T>) -> Self {\n        Field {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(value.into()),\n        }\n    }\n}\n\nimpl<T, S> Clone for Field<T, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> Copy for Field<T, S> {}\n\nimpl<T, S> DefinedAt for Field<T, S> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T, S> Notify for Field<T, S>\nwhere\n    S: Storage<ArcField<T>>,\n{\n    fn notify(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.notify();\n        }\n    }\n}\n\nimpl<T, S> Track for Field<T, S>\nwhere\n    S: Storage<ArcField<T>>,\n{\n    fn track(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.track();\n        }\n    }\n}\n\nimpl<T, S> ReadUntracked for Field<T, S>\nwhere\n    S: Storage<ArcField<T>>,\n{\n    type Value = StoreFieldReader<T>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.inner\n            .try_get_value()\n            .and_then(|inner| inner.try_read_untracked())\n    }\n}\n\nimpl<T> Write for Field<T> {\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.inner.try_get_value().and_then(|inner| (inner.write)())\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.inner.try_get_value().and_then(|inner| {\n            let mut guard = (inner.write)()?;\n            guard.untrack();\n            Some(guard)\n        })\n    }\n}\n\nimpl<T, S> IsDisposed for Field<T, S> {\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/iter.rs",
    "content": "use crate::{\n    len::Len,\n    path::{StorePath, StorePathSegment},\n    store_field::StoreField,\n    KeyMap, StoreFieldTrigger,\n};\nuse reactive_graph::{\n    signal::{\n        guards::{MappedMutArc, WriteGuard},\n        ArcTrigger,\n    },\n    traits::{\n        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,\n        Write,\n    },\n};\nuse std::{\n    iter,\n    marker::PhantomData,\n    ops::{DerefMut, IndexMut},\n    panic::Location,\n};\n\n/// Provides access to the data at some index in another collection.\n#[derive(Debug)]\npub struct AtIndex<Inner, Prev> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    inner: Inner,\n    index: usize,\n    ty: PhantomData<Prev>,\n}\n\nimpl<Inner, Prev> Clone for AtIndex<Inner, Prev>\nwhere\n    Inner: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            inner: self.inner.clone(),\n            index: self.index,\n            ty: self.ty,\n        }\n    }\n}\n\nimpl<Inner, Prev> Copy for AtIndex<Inner, Prev> where Inner: Copy {}\n\nimpl<Inner, Prev> AtIndex<Inner, Prev> {\n    /// Creates a new accessor for the inner collection at the given index.\n    #[track_caller]\n    pub fn new(inner: Inner, index: usize) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner,\n            index,\n            ty: PhantomData,\n        }\n    }\n}\n\nimpl<Inner, Prev> StoreField for AtIndex<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev>,\n    Prev: IndexMut<usize> + 'static,\n    Prev::Output: Sized,\n{\n    type Value = Prev::Output;\n    type Reader = MappedMutArc<Inner::Reader, Prev::Output>;\n    type Writer =\n        MappedMutArc<WriteGuard<ArcTrigger, Inner::Writer>, Prev::Output>;\n\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .path()\n            .into_iter()\n            .chain(iter::once(self.index.into()))\n    }\n\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .path_unkeyed()\n            .into_iter()\n            .chain(iter::once(self.index.into()))\n    }\n\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger(path)\n    }\n\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger_unkeyed(path)\n    }\n\n    fn reader(&self) -> Option<Self::Reader> {\n        let inner = self.inner.reader()?;\n        let index = self.index;\n        Some(MappedMutArc::new(\n            inner,\n            move |n| &n[index],\n            move |n| &mut n[index],\n        ))\n    }\n\n    fn writer(&self) -> Option<Self::Writer> {\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        let inner = WriteGuard::new(trigger.children, self.inner.writer()?);\n        let index = self.index;\n        Some(MappedMutArc::new(\n            inner,\n            move |n| &n[index],\n            move |n| &mut n[index],\n        ))\n    }\n\n    #[inline(always)]\n    fn keys(&self) -> Option<KeyMap> {\n        self.inner.keys()\n    }\n\n    fn track_field(&self) {\n        let mut full_path = self.path().into_iter().collect::<StorePath>();\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.track();\n        trigger.children.track();\n\n        // tracks `this` for all ancestors: i.e., it will track any change that is made\n        // directly to one of its ancestors, but not a change made to a *child* of an ancestor\n        // (which would end up with every subfield tracking its own siblings, because they are\n        // children of its parent)\n        while !full_path.is_empty() {\n            full_path.pop();\n            let inner = self.get_trigger(full_path.clone());\n            inner.this.track();\n        }\n    }\n}\n\nimpl<Inner, Prev> DefinedAt for AtIndex<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev>,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<Inner, Prev> IsDisposed for AtIndex<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev> + IsDisposed,\n{\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<Inner, Prev> Notify for AtIndex<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev>,\n    Prev: IndexMut<usize> + 'static,\n    Prev::Output: Sized,\n{\n    fn notify(&self) {\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.notify();\n    }\n}\n\nimpl<Inner, Prev> Track for AtIndex<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev> + Send + Sync + Clone + 'static,\n    Prev: IndexMut<usize> + 'static,\n    Prev::Output: Sized + 'static,\n{\n    fn track(&self) {\n        self.track_field();\n    }\n}\n\nimpl<Inner, Prev> ReadUntracked for AtIndex<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev>,\n    Prev: IndexMut<usize> + 'static,\n    Prev::Output: Sized,\n{\n    type Value = <Self as StoreField>::Reader;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.reader()\n    }\n}\n\nimpl<Inner, Prev> Write for AtIndex<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev>,\n    Prev: IndexMut<usize> + 'static,\n    Prev::Output: Sized + 'static,\n{\n    type Value = Prev::Output;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.writer()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.writer().map(|mut writer| {\n            writer.untrack();\n            writer\n        })\n    }\n}\n\n/// Provides unkeyed reactive access to the fields of some collection.\npub trait StoreFieldIterator<Prev>\nwhere\n    Self: StoreField<Value = Prev>,\n{\n    /// Reactive access to the value at some index.\n    fn at_unkeyed(self, index: usize) -> AtIndex<Self, Prev>;\n\n    /// An iterator over the values in the collection.\n    fn iter_unkeyed(self) -> StoreFieldIter<Self, Prev>;\n}\n\nimpl<Inner, Prev> StoreFieldIterator<Prev> for Inner\nwhere\n    Inner: StoreField<Value = Prev> + Clone,\n    Prev::Output: Sized,\n    Prev: IndexMut<usize> + Len,\n{\n    #[track_caller]\n    fn at_unkeyed(self, index: usize) -> AtIndex<Inner, Prev> {\n        AtIndex::new(self.clone(), index)\n    }\n\n    #[track_caller]\n    fn iter_unkeyed(self) -> StoreFieldIter<Inner, Prev> {\n        // reactively track changes to this field\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.track();\n        trigger.children.track();\n\n        // get the current length of the field by accessing slice\n        let len = self.reader().map(|n| n.len()).unwrap_or(0);\n\n        // return the iterator\n        StoreFieldIter {\n            inner: self,\n            idx: 0,\n            len,\n            prev: PhantomData,\n        }\n    }\n}\n\n/// An iterator over the values in a collection, as reactive fields.\npub struct StoreFieldIter<Inner, Prev> {\n    inner: Inner,\n    idx: usize,\n    len: usize,\n    prev: PhantomData<Prev>,\n}\n\nimpl<Inner, Prev> Iterator for StoreFieldIter<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev> + Clone + 'static,\n    Prev: IndexMut<usize> + 'static,\n    Prev::Output: Sized + 'static,\n{\n    type Item = AtIndex<Inner, Prev>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.idx < self.len {\n            let field = AtIndex::new(self.inner.clone(), self.idx);\n            self.idx += 1;\n            Some(field)\n        } else {\n            None\n        }\n    }\n}\n\nimpl<Inner, Prev> DoubleEndedIterator for StoreFieldIter<Inner, Prev>\nwhere\n    Inner: StoreField<Value = Prev> + Clone + 'static,\n    Prev: IndexMut<usize> + 'static,\n    Prev::Output: Sized + 'static,\n{\n    fn next_back(&mut self) -> Option<Self::Item> {\n        if self.len > self.idx {\n            self.len -= 1;\n            let field = AtIndex::new(self.inner.clone(), self.len);\n            Some(field)\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/keyed.rs",
    "content": "use crate::{\n    path::{StorePath, StorePathSegment},\n    store_field::StoreField,\n    KeyMap, StoreFieldTrigger,\n};\nuse reactive_graph::{\n    signal::{\n        guards::{Mapped, MappedMut, MappedMutArc, WriteGuard},\n        ArcTrigger,\n    },\n    traits::{\n        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,\n        Write,\n    },\n};\nuse std::{\n    collections::VecDeque,\n    fmt::Debug,\n    hash::Hash,\n    iter,\n    ops::{Deref, DerefMut, Index, IndexMut},\n    panic::Location,\n};\n\n/// Accesses an item from a keyed collection.\n///\n/// `K` is the identity key type used to uniquely identify entries. Collections\n/// that are indexed by position (like `Vec`) can implement this for any `K`,\n/// ignoring the key and using the `index` parameter instead. Collections that\n/// are indexed by key (like `HashMap`) use the `key` parameter.\npub trait KeyedAccess<K> {\n    /// Collection values.\n    type Value;\n    /// Acquire read-only access to a value.\n    fn keyed(&self, index: usize, key: &K) -> &Self::Value;\n    /// Acquire mutable access to a value.\n    fn keyed_mut(&mut self, index: usize, key: &K) -> &mut Self::Value;\n}\nimpl<K, T> KeyedAccess<K> for VecDeque<T> {\n    type Value = T;\n    fn keyed(&self, index: usize, _key: &K) -> &Self::Value {\n        self.index(index)\n    }\n    fn keyed_mut(&mut self, index: usize, _key: &K) -> &mut Self::Value {\n        self.index_mut(index)\n    }\n}\nimpl<K, T> KeyedAccess<K> for Vec<T> {\n    type Value = T;\n    fn keyed(&self, index: usize, _key: &K) -> &Self::Value {\n        self.index(index)\n    }\n    fn keyed_mut(&mut self, index: usize, _key: &K) -> &mut Self::Value {\n        self.index_mut(index)\n    }\n}\nimpl<K, T> KeyedAccess<K> for [T] {\n    type Value = T;\n    fn keyed(&self, index: usize, _key: &K) -> &Self::Value {\n        self.index(index)\n    }\n    fn keyed_mut(&mut self, index: usize, _key: &K) -> &mut Self::Value {\n        self.index_mut(index)\n    }\n}\nimpl<K: Ord, V> KeyedAccess<K> for std::collections::BTreeMap<K, V> {\n    type Value = V;\n    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {\n        self.get(key).expect(\"key does not exist\")\n    }\n    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {\n        self.get_mut(key).expect(\"key does not exist\")\n    }\n}\nimpl<K: Hash + Eq, V> KeyedAccess<K> for std::collections::HashMap<K, V> {\n    type Value = V;\n    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {\n        self.get(key).expect(\"key does not exist\")\n    }\n    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {\n        self.get_mut(key).expect(\"key does not exist\")\n    }\n}\n\n/// Provides access to a subfield that contains some kind of keyed collection.\n#[derive(Debug)]\npub struct KeyedSubfield<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n{\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    path_segment: StorePathSegment,\n    inner: Inner,\n    read: fn(&Prev) -> &T,\n    write: fn(&mut Prev) -> &mut T,\n    pub(crate) key_fn: fn(<&T as IntoIterator>::Item) -> K,\n}\n\nimpl<Inner, Prev, K, T> Clone for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    Inner: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            path_segment: self.path_segment,\n            inner: self.inner.clone(),\n            read: self.read,\n            write: self.write,\n            key_fn: self.key_fn,\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> Copy for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    Inner: Copy,\n{\n}\n\nimpl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n{\n    /// Creates a keyed subfield of the inner data type with the given key function.\n    #[track_caller]\n    pub fn new(\n        inner: Inner,\n        path_segment: StorePathSegment,\n        key_fn: fn(<&T as IntoIterator>::Item) -> K,\n        read: fn(&Prev) -> &T,\n        write: fn(&mut Prev) -> &mut T,\n    ) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner,\n            path_segment,\n            read,\n            write,\n            key_fn,\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> StoreField for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    type Value = T;\n    type Reader = Mapped<Inner::Reader, T>;\n    type Writer = MappedMut<WriteGuard<Vec<ArcTrigger>, Inner::Writer>, T>;\n\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .path()\n            .into_iter()\n            .chain(iter::once(self.path_segment))\n    }\n\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .path_unkeyed()\n            .into_iter()\n            .chain(iter::once(self.path_segment))\n    }\n\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger(path)\n    }\n\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger_unkeyed(path)\n    }\n\n    fn reader(&self) -> Option<Self::Reader> {\n        let inner = self.inner.reader()?;\n        Some(Mapped::new_with_guard(inner, self.read))\n    }\n\n    fn writer(&self) -> Option<Self::Writer> {\n        let mut parent = self.inner.writer()?;\n        parent.untrack();\n        let triggers = self.triggers_for_current_path();\n        let guard = WriteGuard::new(triggers, parent);\n        Some(MappedMut::new(guard, self.read, self.write))\n    }\n\n    #[inline(always)]\n    fn keys(&self) -> Option<KeyMap> {\n        self.inner.keys()\n    }\n\n    fn track_field(&self) {\n        let mut full_path = self.path().into_iter().collect::<StorePath>();\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.track();\n        trigger.children.track();\n\n        // tracks `this` for all ancestors: i.e., it will track any change that is made\n        // directly to one of its ancestors, but not a change made to a *child* of an ancestor\n        // (which would end up with every subfield tracking its own siblings, because they are\n        // children of its parent)\n        while !full_path.is_empty() {\n            full_path.pop();\n            let inner = self.get_trigger(full_path.clone());\n            inner.this.track();\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    fn latest_keys(&self) -> Vec<K> {\n        self.reader()\n            .map(|r| r.deref().into_iter().map(|n| (self.key_fn)(n)).collect())\n            .unwrap_or_default()\n    }\n\n    pub(crate) fn path_at_key(\n        &self,\n        base_path: &StorePath,\n        key: &K,\n    ) -> Option<StorePath> {\n        let keys = self.keys();\n        let keys = keys.as_ref()?;\n        let segment = keys\n            .with_field_keys(\n                base_path.clone(),\n                |keys| (keys.get(key), vec![]),\n                || self.latest_keys(),\n            )\n            .flatten()\n            .map(|(_, idx)| idx)?;\n        let mut path = base_path.clone();\n        path.push(segment);\n        Some(path)\n    }\n}\n\nimpl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n{\n    /// Keyed access to a keyed subfield of a store.\n    pub fn at_key(&self, key: K) -> AtKeyed<Inner, Prev, K, T> {\n        AtKeyed::new(self.clone(), key)\n    }\n}\n\n/// Gives keyed write access to a value in some collection.\npub struct KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>\nwhere\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    inner: KeyedSubfield<Inner, Prev, K, T>,\n    guard: Option<Guard>,\n    untracked: bool,\n}\n\nimpl<Inner, Prev, K, T, Guard> Deref\n    for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>\nwhere\n    Guard: Deref,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    type Target = Guard::Target;\n\n    fn deref(&self) -> &Self::Target {\n        self.guard\n            .as_ref()\n            .expect(\"should be Some(_) until dropped\")\n            .deref()\n    }\n}\n\nimpl<Inner, Prev, K, T, Guard> DerefMut\n    for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>\nwhere\n    Guard: DerefMut,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.guard\n            .as_mut()\n            .expect(\"should be Some(_) until dropped\")\n            .deref_mut()\n    }\n}\n\nimpl<Inner, Prev, K, T, Guard> UntrackableGuard\n    for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>\nwhere\n    Guard: UntrackableGuard,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    fn untrack(&mut self) {\n        self.untracked = true;\n        if let Some(inner) = self.guard.as_mut() {\n            inner.untrack();\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T, Guard> Drop\n    for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>\nwhere\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    fn drop(&mut self) {\n        // dropping the inner guard will\n        // 1) synchronously release its write lock on the store's value\n        // 2) trigger an (asynchronous) reactive update\n        drop(self.guard.take());\n\n        // now that the write lock is release, we can get a read lock to refresh this keyed field\n        // based on the new value\n        self.inner.update_keys();\n\n        if !self.untracked {\n            self.inner.notify();\n        }\n\n        // reactive updates happen on the next tick\n    }\n}\n\nimpl<Inner, Prev, K, T> DefinedAt for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> IsDisposed for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    Inner: IsDisposed,\n{\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<Inner, Prev, K, T> Notify for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    fn notify(&self) {\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.notify();\n        trigger.children.notify();\n    }\n}\n\nimpl<Inner, Prev, K, T> Track for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev> + Track + 'static,\n    Prev: 'static,\n    T: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    fn track(&self) {\n        self.track_field();\n    }\n}\n\nimpl<Inner, Prev, K, T> ReadUntracked for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    type Value = <Self as StoreField>::Reader;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.reader()\n    }\n}\n\nimpl<Inner, Prev, K, T> Write for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    T: 'static,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        let guard = self.writer()?;\n        Some(KeyedSubfieldWriteGuard {\n            inner: self.clone(),\n            guard: Some(guard),\n            untracked: false,\n        })\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        let mut guard = self.writer()?;\n        guard.untrack();\n        Some(KeyedSubfieldWriteGuard {\n            inner: self.clone(),\n            guard: Some(guard),\n            untracked: true,\n        })\n    }\n}\n\n/// Gives access to the value in a collection based on some key.\n#[derive(Debug)]\npub struct AtKeyed<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n{\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    inner: KeyedSubfield<Inner, Prev, K, T>,\n    key: K,\n}\n\nimpl<Inner, Prev, K, T> AtKeyed<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    K: Clone,\n{\n    /// Key used for keyed collection access.\n    pub fn key(&self) -> K {\n        self.key.clone()\n    }\n}\n\nimpl<Inner, Prev, K, T> Clone for AtKeyed<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    K: Debug + Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            inner: self.inner.clone(),\n            key: self.key.clone(),\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> Copy for AtKeyed<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    KeyedSubfield<Inner, Prev, K, T>: Copy,\n    K: Debug + Copy,\n{\n}\n\nimpl<Inner, Prev, K, T> AtKeyed<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n{\n    /// Provides access to the item in the inner collection at this key.\n    #[track_caller]\n    pub fn new(inner: KeyedSubfield<Inner, Prev, K, T>, key: K) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner,\n            key,\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> AtKeyed<Inner, Prev, K, T>\nwhere\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    T: KeyedAccess<K>,\n    T::Value: Sized,\n{\n    /// Attempt to resolve the inner index if is still exists.\n    fn resolve_index(&self) -> Option<usize> {\n        let inner_path = self.inner.path().into_iter().collect();\n        let keys = self.inner.keys()?;\n        keys.with_field_keys(\n            inner_path,\n            |keys| (keys.get(&self.key), vec![]),\n            || self.inner.latest_keys(),\n        )\n        .flatten()\n        .map(|(_, idx)| idx)\n    }\n}\n\nimpl<Inner, Prev, K, T> StoreField for AtKeyed<Inner, Prev, K, T>\nwhere\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    T: KeyedAccess<K>,\n    T::Value: Sized,\n{\n    type Value = T::Value;\n    type Reader = MappedMutArc<\n        <KeyedSubfield<Inner, Prev, K, T> as StoreField>::Reader,\n        T::Value,\n    >;\n    type Writer = WriteGuard<\n        Vec<ArcTrigger>,\n        MappedMutArc<\n            <KeyedSubfield<Inner, Prev, K, T> as StoreField>::Writer,\n            T::Value,\n        >,\n    >;\n\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        let inner = self.inner.path().into_iter().collect::<StorePath>();\n        let keys = self\n            .inner\n            .keys()\n            .expect(\"using keys on a store with no keys\");\n        let this = keys\n            .with_field_keys(\n                inner.clone(),\n                |keys| (keys.get(&self.key), vec![]),\n                || self.inner.latest_keys(),\n            )\n            .flatten()\n            .map(|(path, _)| path);\n        inner.into_iter().chain(this)\n    }\n\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        let inner =\n            self.inner.path_unkeyed().into_iter().collect::<StorePath>();\n        let keys = self\n            .inner\n            .keys()\n            .expect(\"using keys on a store with no keys\");\n        let this = keys\n            .with_field_keys(\n                inner.clone(),\n                |keys| (keys.get(&self.key), vec![]),\n                || self.inner.latest_keys(),\n            )\n            .flatten()\n            .map(|(_, idx)| StorePathSegment(idx));\n        inner.into_iter().chain(this)\n    }\n\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger(path)\n    }\n\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger_unkeyed(path)\n    }\n\n    fn reader(&self) -> Option<Self::Reader> {\n        let inner = self.inner.reader()?;\n        let index = self.resolve_index()?;\n        Some(MappedMutArc::new(\n            inner,\n            {\n                let key = self.key.clone();\n                move |n| n.keyed(index, &key)\n            },\n            {\n                let key = self.key.clone();\n                move |n| n.keyed_mut(index, &key)\n            },\n        ))\n    }\n\n    fn writer(&self) -> Option<Self::Writer> {\n        let mut inner = self.inner.writer()?;\n        inner.untrack();\n        let index = self.resolve_index()?;\n        let triggers = self.triggers_for_current_path();\n        Some(WriteGuard::new(\n            triggers,\n            MappedMutArc::new(\n                inner,\n                {\n                    let key = self.key.clone();\n                    move |n| n.keyed(index, &key)\n                },\n                {\n                    let key = self.key.clone();\n                    move |n| n.keyed_mut(index, &key)\n                },\n            ),\n        ))\n    }\n\n    #[inline(always)]\n    fn keys(&self) -> Option<KeyMap> {\n        self.inner.keys()\n    }\n\n    fn track_field(&self) {\n        let mut full_path = self.path().into_iter().collect::<StorePath>();\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.track();\n        trigger.children.track();\n\n        // tracks `this` for all ancestors: i.e., it will track any change that is made\n        // directly to one of its ancestors, but not a change made to a *child* of an ancestor\n        // (which would end up with every subfield tracking its own siblings, because they are\n        // children of its parent)\n        while !full_path.is_empty() {\n            full_path.pop();\n            let inner = self.get_trigger(full_path.clone());\n            inner.this.track();\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> DefinedAt for AtKeyed<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> IsDisposed for AtKeyed<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    Inner: IsDisposed,\n{\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<Inner, Prev, K, T> Notify for AtKeyed<Inner, Prev, K, T>\nwhere\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    T: KeyedAccess<K>,\n    T::Value: Sized,\n{\n    fn notify(&self) {\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.notify();\n        trigger.children.notify();\n    }\n}\n\nimpl<Inner, Prev, K, T> Track for AtKeyed<Inner, Prev, K, T>\nwhere\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    T: KeyedAccess<K>,\n    T::Value: Sized,\n{\n    fn track(&self) {\n        self.track_field();\n    }\n}\n\nimpl<Inner, Prev, K, T> ReadUntracked for AtKeyed<Inner, Prev, K, T>\nwhere\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    T: KeyedAccess<K>,\n    T::Value: Sized,\n{\n    type Value = <Self as StoreField>::Reader;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.reader()\n    }\n}\n\nimpl<Inner, Prev, K, T> Write for AtKeyed<Inner, Prev, K, T>\nwhere\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    KeyedSubfield<Inner, Prev, K, T>: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    T: KeyedAccess<K>,\n    T::Value: Sized + 'static,\n{\n    type Value = T::Value;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.writer()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.writer().map(|mut writer| {\n            writer.untrack();\n            writer\n        })\n    }\n}\n\nimpl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n{\n    /// Generates a new set of keys and registers those keys with the parent store.\n    pub fn update_keys(&self) {\n        let inner_path = self.path().into_iter().collect();\n        let keys = self\n            .inner\n            .keys()\n            .expect(\"updating keys on a store with no keys\");\n\n        // generating the latest keys out here means that if we have\n        // nested keyed fields, the second field will not try to take a\n        // read-lock on the key map to get the field while the first field\n        // is still holding the write-lock in the closure below\n        let latest = self.latest_keys();\n        keys.with_field_keys(\n            inner_path,\n            |keys| ((), keys.update(latest)),\n            || self.latest_keys(),\n        );\n    }\n}\n\nimpl<Inner, Prev, K, T> IntoIterator for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Inner: Clone + StoreField<Value = Prev> + 'static,\n    Prev: 'static,\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    T: KeyedAccess<K> + 'static,\n    T::Value: Sized,\n{\n    type Item = AtKeyed<Inner, Prev, K, T>;\n    type IntoIter = StoreFieldKeyedIter<Inner, Prev, K, T>;\n\n    #[track_caller]\n    fn into_iter(self) -> StoreFieldKeyedIter<Inner, Prev, K, T> {\n        // reactively track changes to this field\n        self.update_keys();\n        self.track_field();\n\n        // get the current length of the field by accessing slice\n        let reader = self.reader();\n\n        let keys = reader\n            .map(|r| {\n                r.into_iter()\n                    .map(|item| (self.key_fn)(item))\n                    .collect::<VecDeque<_>>()\n            })\n            .unwrap_or_default();\n\n        // return the iterator\n        StoreFieldKeyedIter { inner: self, keys }\n    }\n}\n\n/// An iterator over a [`KeyedSubfield`].\npub struct StoreFieldKeyedIter<Inner, Prev, K, T>\nwhere\n    for<'a> &'a T: IntoIterator,\n    T: KeyedAccess<K>,\n{\n    inner: KeyedSubfield<Inner, Prev, K, T>,\n    keys: VecDeque<K>,\n}\n\nimpl<Inner, Prev, K, T> Iterator for StoreFieldKeyedIter<Inner, Prev, K, T>\nwhere\n    Inner: StoreField<Value = Prev> + Clone + 'static,\n    T: KeyedAccess<K> + 'static,\n    for<'a> &'a T: IntoIterator,\n{\n    type Item = AtKeyed<Inner, Prev, K, T>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.keys\n            .pop_front()\n            .map(|key| AtKeyed::new(self.inner.clone(), key))\n    }\n}\n\nimpl<Inner, Prev, K, T> DoubleEndedIterator\n    for StoreFieldKeyedIter<Inner, Prev, K, T>\nwhere\n    Inner: StoreField<Value = Prev> + Clone + 'static,\n    T: KeyedAccess<K> + 'static,\n    T::Value: Sized + 'static,\n    for<'a> &'a T: IntoIterator,\n{\n    fn next_back(&mut self) -> Option<Self::Item> {\n        self.keys\n            .pop_back()\n            .map(|key| AtKeyed::new(self.inner.clone(), key))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{self as reactive_stores, tests::tick, AtKeyed, Store};\n    use reactive_graph::{\n        effect::Effect,\n        traits::{Get, GetUntracked, ReadUntracked, Set, Track, Write},\n    };\n    use reactive_stores::Patch;\n    use std::{\n        collections::{BTreeMap, BTreeSet, HashMap},\n        sync::{\n            atomic::{AtomicUsize, Ordering},\n            Arc,\n        },\n    };\n\n    #[derive(Debug, Store, Default, Patch)]\n    struct TodoVec {\n        #[store(key: usize = |todo| todo.id)]\n        todos: Vec<Todo>,\n    }\n    impl TodoVec {\n        fn test_data() -> Self {\n            Self {\n                todos: vec![\n                    Todo {\n                        id: 10,\n                        label: \"A\".to_string(),\n                    },\n                    Todo {\n                        id: 11,\n                        label: \"B\".to_string(),\n                    },\n                    Todo {\n                        id: 12,\n                        label: \"C\".to_string(),\n                    },\n                ],\n            }\n        }\n    }\n\n    #[derive(Debug, Store, Default)]\n    struct TodoBTreeMap {\n        #[store(key: usize = |(key, _)| *key)]\n        todos: BTreeMap<usize, Todo>,\n    }\n    impl TodoBTreeMap {\n        fn test_data() -> Self {\n            Self {\n                todos: [\n                    Todo {\n                        id: 10,\n                        label: \"A\".to_string(),\n                    },\n                    Todo {\n                        id: 11,\n                        label: \"B\".to_string(),\n                    },\n                    Todo {\n                        id: 12,\n                        label: \"C\".to_string(),\n                    },\n                ]\n                .into_iter()\n                .map(|todo| (todo.id, todo))\n                .collect(),\n            }\n        }\n    }\n\n    #[derive(Debug, Store, Default)]\n    struct TodoHashMap {\n        #[store(key: String = |(key, _)| key.clone())]\n        todos: HashMap<String, Todo>,\n    }\n    impl TodoHashMap {\n        fn test_data() -> Self {\n            Self {\n                todos: [\n                    Todo {\n                        id: 10,\n                        label: \"A\".to_string(),\n                    },\n                    Todo {\n                        id: 11,\n                        label: \"B\".to_string(),\n                    },\n                    Todo {\n                        id: 12,\n                        label: \"C\".to_string(),\n                    },\n                ]\n                .into_iter()\n                .map(|todo| (todo.label.clone(), todo))\n                .collect(),\n            }\n        }\n    }\n\n    #[derive(\n        Debug, Store, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Patch,\n    )]\n    struct Todo {\n        id: usize,\n        label: String,\n    }\n\n    impl Todo {\n        pub fn new(id: usize, label: impl ToString) -> Self {\n            Self {\n                id,\n                label: label.to_string(),\n            }\n        }\n    }\n\n    #[tokio::test]\n    async fn keyed_fields_can_be_moved() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let store = Store::new(TodoVec::test_data());\n        assert_eq!(store.read_untracked().todos.len(), 3);\n\n        // create an effect to read from each keyed field\n        let a_count = Arc::new(AtomicUsize::new(0));\n        let b_count = Arc::new(AtomicUsize::new(0));\n        let c_count = Arc::new(AtomicUsize::new(0));\n\n        let a = AtKeyed::new(store.todos(), 10);\n        let b = AtKeyed::new(store.todos(), 11);\n        let c = AtKeyed::new(store.todos(), 12);\n\n        Effect::new_sync({\n            let a_count = Arc::clone(&a_count);\n            move || {\n                a.track();\n                a_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let b_count = Arc::clone(&b_count);\n            move || {\n                b.track();\n                b_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let c_count = Arc::clone(&c_count);\n            move || {\n                c.track();\n                c_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 1);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // writing at a key doesn't notify siblings\n        *a.label().write() = \"Foo\".into();\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 2);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // the keys can be reorganized\n        store.todos().write().swap(0, 2);\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after,\n            vec![Todo::new(12, \"C\"), Todo::new(11, \"B\"), Todo::new(10, \"Foo\")]\n        );\n\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 3);\n        assert_eq!(b_count.load(Ordering::Relaxed), 2);\n        assert_eq!(c_count.load(Ordering::Relaxed), 2);\n\n        // and after we move the keys around, they still update the moved items\n        a.label().set(\"Bar\".into());\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after,\n            vec![Todo::new(12, \"C\"), Todo::new(11, \"B\"), Todo::new(10, \"Bar\")]\n        );\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 4);\n        assert_eq!(b_count.load(Ordering::Relaxed), 2);\n        assert_eq!(c_count.load(Ordering::Relaxed), 2);\n\n        // we can remove a key and add a new one\n        store.todos().write().pop();\n        store.todos().write().push(Todo::new(13, \"New\"));\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after,\n            vec![Todo::new(12, \"C\"), Todo::new(11, \"B\"), Todo::new(13, \"New\")]\n        );\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 5);\n        assert_eq!(b_count.load(Ordering::Relaxed), 3);\n        assert_eq!(c_count.load(Ordering::Relaxed), 3);\n    }\n\n    #[tokio::test]\n    async fn untracked_write_on_keyed_subfield_shouldnt_notify() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let store = Store::new(TodoVec::test_data());\n        assert_eq!(store.read_untracked().todos.len(), 3);\n\n        // create an effect to read from the keyed subfield\n        let todos_count = Arc::new(AtomicUsize::new(0));\n        Effect::new_sync({\n            let todos_count = Arc::clone(&todos_count);\n            move || {\n                store.todos().track();\n                todos_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(todos_count.load(Ordering::Relaxed), 1);\n\n        // writing to keyed subfield notifies the iterator\n        store.todos().write().push(Todo {\n            id: 13,\n            label: \"D\".into(),\n        });\n        tick().await;\n        assert_eq!(todos_count.load(Ordering::Relaxed), 2);\n\n        // but an untracked write doesn't\n        store.todos().write_untracked().push(Todo {\n            id: 14,\n            label: \"E\".into(),\n        });\n        tick().await;\n        assert_eq!(todos_count.load(Ordering::Relaxed), 2);\n    }\n\n    #[tokio::test]\n    async fn btree_keyed_fields_can_be_moved() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let store = Store::new(TodoBTreeMap::test_data());\n        assert_eq!(store.read_untracked().todos.len(), 3);\n\n        // create an effect to read from each keyed field\n        let a_count = Arc::new(AtomicUsize::new(0));\n        let b_count = Arc::new(AtomicUsize::new(0));\n        let c_count = Arc::new(AtomicUsize::new(0));\n\n        let a = AtKeyed::new(store.todos(), 10);\n        let b = AtKeyed::new(store.todos(), 11);\n        let c = AtKeyed::new(store.todos(), 12);\n\n        Effect::new_sync({\n            let a_count = Arc::clone(&a_count);\n            move || {\n                a.track();\n                a_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let b_count = Arc::clone(&b_count);\n            move || {\n                b.track();\n                b_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let c_count = Arc::clone(&c_count);\n            move || {\n                c.track();\n                c_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 1);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // writing at a key doesn't notify siblings\n        *a.label().write() = \"Foo\".into();\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 2);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<Vec<_>>(),\n            vec![Todo::new(10, \"Foo\"), Todo::new(11, \"B\"), Todo::new(12, \"C\"),]\n        );\n\n        a.label().set(\"Bar\".into());\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<Vec<_>>(),\n            vec![Todo::new(10, \"Bar\"), Todo::new(11, \"B\"), Todo::new(12, \"C\"),]\n        );\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 3);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // we can remove a key and add a new one\n        store.todos().write().remove(&12);\n        store.todos().write().insert(13, Todo::new(13, \"New\"));\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<Vec<_>>(),\n            vec![\n                Todo::new(10, \"Bar\"),\n                Todo::new(11, \"B\"),\n                Todo::new(13, \"New\")\n            ]\n        );\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 4);\n        assert_eq!(b_count.load(Ordering::Relaxed), 2);\n        assert_eq!(c_count.load(Ordering::Relaxed), 2);\n\n        assert_eq!(\n            after.keys().copied().collect::<BTreeSet<usize>>(),\n            BTreeSet::from([10, 11, 13])\n        );\n\n        let at_existing_key = AtKeyed::new(store.todos(), 13);\n        let existing = at_existing_key.try_get();\n        assert!(existing.is_some());\n        assert_eq!(existing, Some(Todo::new(13, \"New\")));\n\n        let at_faulty_key = AtKeyed::new(store.todos(), 999);\n        let missing = at_faulty_key.try_get();\n        assert!(missing.is_none(), \"faulty key should return none.\")\n    }\n\n    #[tokio::test]\n    async fn hashmap_keyed_fields_can_be_moved() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let store = Store::new(TodoHashMap::test_data());\n        assert_eq!(store.read_untracked().todos.len(), 3);\n\n        // create an effect to read from each keyed field\n        let a_count = Arc::new(AtomicUsize::new(0));\n        let b_count = Arc::new(AtomicUsize::new(0));\n        let c_count = Arc::new(AtomicUsize::new(0));\n\n        let a = AtKeyed::new(store.todos(), \"A\".to_string());\n        let b = AtKeyed::new(store.todos(), \"B\".to_string());\n        let c = AtKeyed::new(store.todos(), \"C\".to_string());\n\n        Effect::new_sync({\n            let a_count = Arc::clone(&a_count);\n            let a = a.clone();\n            move || {\n                a.track();\n                a_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let b_count = Arc::clone(&b_count);\n            move || {\n                b.track();\n                b_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let c_count = Arc::clone(&c_count);\n            move || {\n                c.track();\n                c_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 1);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // writing at a key doesn't notify siblings\n        *a.clone().label().write() = \"Foo\".to_string();\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 2);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<BTreeSet<_>>(),\n            BTreeSet::from([\n                Todo::new(10, \"Foo\"),\n                Todo::new(11, \"B\"),\n                Todo::new(12, \"C\"),\n            ])\n        );\n\n        a.clone().label().set(\"Bar\".into());\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<BTreeSet<_>>(),\n            BTreeSet::from([\n                Todo::new(10, \"Bar\"),\n                Todo::new(11, \"B\"),\n                Todo::new(12, \"C\")\n            ]),\n        );\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 3);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // we can remove a key and add a new one\n        store.todos().write().remove(&\"C\".to_string());\n        store\n            .todos()\n            .write()\n            .insert(\"New\".to_string(), Todo::new(13, \"New\"));\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<BTreeSet<_>>(),\n            BTreeSet::from([\n                Todo::new(10, \"Bar\"),\n                Todo::new(11, \"B\"),\n                Todo::new(13, \"New\"),\n            ])\n        );\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 4);\n        assert_eq!(b_count.load(Ordering::Relaxed), 2);\n        assert_eq!(c_count.load(Ordering::Relaxed), 2);\n\n        assert_eq!(\n            after.keys().cloned().collect::<BTreeSet<String>>(),\n            BTreeSet::from([\n                \"A\".to_string(),\n                \"B\".to_string(),\n                \"New\".to_string()\n            ])\n        );\n\n        let at_existing_key = AtKeyed::new(store.todos(), \"New\".to_string());\n        let existing = at_existing_key.try_get();\n        assert!(existing.is_some());\n        assert_eq!(existing, Some(Todo::new(13, \"New\")));\n\n        let at_faulty_key = AtKeyed::new(store.todos(), \"faulty\".to_string());\n        let missing = at_faulty_key.try_get();\n        assert!(missing.is_none(), \"faulty key should return none.\")\n    }\n\n    #[test]\n    fn non_usize_keys_work_for_vec() {\n        #[derive(Clone, PartialEq, Eq, Hash, Debug)]\n        struct MyIdType(u32);\n\n        #[derive(Debug, Store)]\n        struct Item {\n            id: MyIdType,\n            _value: String,\n        }\n\n        #[derive(Debug, Store)]\n        struct MyStore {\n            #[store(key: MyIdType = |item| item.id.clone())]\n            items: Vec<Item>,\n        }\n\n        let store = Store::new(MyStore { items: Vec::new() });\n\n        let _fields = store.items().into_iter();\n    }\n\n    #[tokio::test]\n    async fn patching_keyed_field_only_notifies_changed_keys() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let store = Store::new(TodoVec::test_data());\n        assert_eq!(store.read_untracked().todos.len(), 3);\n\n        // create an effect to read from each keyed field\n        let whole_count = Arc::new(AtomicUsize::new(0));\n        let a_count = Arc::new(AtomicUsize::new(0));\n        let b_count = Arc::new(AtomicUsize::new(0));\n        let c_count = Arc::new(AtomicUsize::new(0));\n\n        let whole = store.todos();\n        let a = AtKeyed::new(store.todos(), 10);\n        let b = AtKeyed::new(store.todos(), 11);\n        let c = AtKeyed::new(store.todos(), 12);\n\n        Effect::new_sync({\n            let whole_count = Arc::clone(&whole_count);\n            move || {\n                whole.track();\n                whole_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let a_count = Arc::clone(&a_count);\n            move || {\n                a.track();\n                a_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let b_count = Arc::clone(&b_count);\n            move || {\n                b.track();\n                b_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let c_count = Arc::clone(&c_count);\n            move || {\n                c.track();\n                c_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(whole_count.load(Ordering::Relaxed), 1);\n        assert_eq!(a_count.load(Ordering::Relaxed), 1);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // patching only notifies changed keys\n        let mut new_data = store.todos().get_untracked();\n        new_data.swap(0, 2);\n        store.todos().patch(new_data.clone());\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after,\n            vec![Todo::new(12, \"C\"), Todo::new(11, \"B\"), Todo::new(10, \"A\")]\n        );\n\n        tick().await;\n        assert_eq!(whole_count.load(Ordering::Relaxed), 2);\n        assert_eq!(a_count.load(Ordering::Relaxed), 1);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // and after we move the keys around, they still update the moved items\n        a.label().set(\"Bar\".into());\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after,\n            vec![Todo::new(12, \"C\"), Todo::new(11, \"B\"), Todo::new(10, \"Bar\")]\n        );\n        tick().await;\n        assert_eq!(whole_count.load(Ordering::Relaxed), 3);\n        assert_eq!(a_count.load(Ordering::Relaxed), 2);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // regular writes to the collection notify all keyed children\n        store.todos().write().pop();\n        store.todos().write().push(Todo::new(13, \"New\"));\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after,\n            vec![Todo::new(12, \"C\"), Todo::new(11, \"B\"), Todo::new(13, \"New\")]\n        );\n        tick().await;\n        assert_eq!(whole_count.load(Ordering::Relaxed), 4);\n        assert_eq!(a_count.load(Ordering::Relaxed), 3);\n        assert_eq!(b_count.load(Ordering::Relaxed), 2);\n        assert_eq!(c_count.load(Ordering::Relaxed), 2);\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/len.rs",
    "content": "use std::{\n    borrow::Cow,\n    collections::{LinkedList, VecDeque},\n};\n\n/// A trait for getting the length of a collection.\npub trait Len {\n    /// Returns the length of the collection.\n    fn len(&self) -> usize;\n\n    /// Returns true if the collection is empty\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n}\n\nmacro_rules! delegate_impl_len {\n    (<$($lt: lifetime,)*$($generics: ident,)*> $ty:ty) => {\n        impl<$($lt,)*$($generics,)*> Len for $ty {\n            #[inline(always)]\n            fn len(&self) -> usize {\n                <$ty>::len(self)\n            }\n\n            #[inline(always)]\n            fn is_empty(&self) -> bool {\n                <$ty>::is_empty(self)\n            }\n        }\n\n        impl<$($lt,)*$($generics,)*> Len for &$ty {\n            #[inline(always)]\n            fn len(&self) -> usize {\n                Len::len(*self)\n            }\n\n            #[inline(always)]\n            fn is_empty(&self) -> bool {\n                Len::is_empty(*self)\n            }\n        }\n\n        impl<$($lt,)*$($generics,)*> Len for &mut $ty {\n            #[inline(always)]\n            fn len(&self) -> usize {\n                Len::len(*self)\n            }\n\n            #[inline(always)]\n            fn is_empty(&self) -> bool {\n                Len::is_empty(*self)\n            }\n        }\n    };\n    ($ty:ty) => {\n        delegate_impl_len!(<> $ty);\n    };\n}\n\ndelegate_impl_len!(<T,> [T]);\ndelegate_impl_len!(<T,> Vec<T>);\ndelegate_impl_len!(str);\ndelegate_impl_len!(String);\n\nimpl Len for Cow<'_, str> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        <str>::len(self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        <str>::is_empty(self)\n    }\n}\n\nimpl Len for &Cow<'_, str> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        Len::len(*self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        Len::is_empty(*self)\n    }\n}\n\nimpl Len for &mut Cow<'_, str> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        Len::len(*self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        Len::is_empty(*self)\n    }\n}\n\nimpl<T> Len for Cow<'_, [T]>\nwhere\n    [T]: ToOwned,\n{\n    #[inline(always)]\n    fn len(&self) -> usize {\n        <[T]>::len(self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        <[T]>::is_empty(self)\n    }\n}\n\nimpl<T> Len for &Cow<'_, [T]>\nwhere\n    [T]: ToOwned,\n{\n    #[inline(always)]\n    fn len(&self) -> usize {\n        Len::len(*self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        Len::is_empty(*self)\n    }\n}\n\nimpl<T> Len for &mut Cow<'_, [T]>\nwhere\n    [T]: ToOwned,\n{\n    #[inline(always)]\n    fn len(&self) -> usize {\n        Len::len(*self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        Len::is_empty(*self)\n    }\n}\n\nimpl<T> Len for VecDeque<T> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        <VecDeque<T>>::len(self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        <VecDeque<T>>::is_empty(self)\n    }\n}\n\nimpl<T> Len for &VecDeque<T> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        Len::len(*self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        Len::is_empty(*self)\n    }\n}\n\nimpl<T> Len for &mut VecDeque<T> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        Len::len(&**self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        Len::is_empty(*self)\n    }\n}\n\nimpl<T> Len for LinkedList<T> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        <LinkedList<T>>::len(self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        <LinkedList<T>>::is_empty(self)\n    }\n}\n\nimpl<T> Len for &LinkedList<T> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        Len::len(*self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        Len::is_empty(*self)\n    }\n}\n\nimpl<T> Len for &mut LinkedList<T> {\n    #[inline(always)]\n    fn len(&self) -> usize {\n        Len::len(&**self)\n    }\n\n    #[inline(always)]\n    fn is_empty(&self) -> bool {\n        Len::is_empty(*self)\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/lib.rs",
    "content": "#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\n//! Stores are a primitive for creating deeply-nested reactive state, based on [`reactive_graph`].\n//!\n//! Reactive signals allow you to define atomic units of reactive state. However, signals are\n//! imperfect as a mechanism for tracking reactive change in structs or collections, because\n//! they do not allow you to track access to individual struct fields or individual items in a\n//! collection, rather than the struct as a whole or the collection as a whole. Reactivity for\n//! individual fields can be achieved by creating a struct of signals, but this has issues; it\n//! means that a struct is no longer a plain data structure, but requires wrappers on each field.\n//!\n//! Stores attempt to solve this problem by allowing arbitrarily-deep access to the fields of some\n//! data structure, while still maintaining fine-grained reactivity.\n//!\n//! The [`Store`](macro@Store) macro adds getters and setters for the fields of a struct. Call those getters or\n//! setters on a reactive [`Store`](struct@Store) or [`ArcStore`], or to a subfield, gives you\n//! access to a reactive subfield. This value of this field can be accessed via the ordinary signal\n//! traits (`Get`, `Set`, and so on).\n//!\n//! The [`Patch`](macro@Patch) macro allows you to annotate a struct such that stores and fields have a\n//! [`.patch()`](Patch::patch) method, which allows you to provide an entirely new value, but only\n//! notify fields that have changed.\n//!\n//! Updating a field will notify its parents and children, but not its siblings.\n//!\n//! Stores can therefore\n//! 1) work with plain Rust data types, and\n//! 2) provide reactive access to individual fields\n//!\n//! ### Example\n//!\n//! ```rust\n//! use reactive_graph::{\n//!     effect::Effect,\n//!     traits::{Read, Write},\n//! };\n//! use reactive_stores::{Patch, Store};\n//!\n//! #[derive(Debug, Store, Patch, Default)]\n//! struct Todos {\n//!     user: String,\n//!     todos: Vec<Todo>,\n//! }\n//!\n//! #[derive(Debug, Store, Patch, Default)]\n//! struct Todo {\n//!     label: String,\n//!     completed: bool,\n//! }\n//!\n//! let store = Store::new(Todos {\n//!     user: \"Alice\".to_string(),\n//!     todos: Vec::new(),\n//! });\n//!\n//! # if false { // don't run effect in doctests\n//! Effect::new(move |_| {\n//!     // you can access individual store fields with a getter\n//!     println!(\"user: {:?}\", &*store.user().read());\n//! });\n//! # }\n//!\n//! // won't notify the effect that listens to `user`\n//! store.todos().write().push(Todo {\n//!     label: \"Test\".to_string(),\n//!     completed: false,\n//! });\n//! ```\n//! ### Generated traits\n//! The [`Store`](macro@Store) macro generates traits for each `struct` to which it is applied.  When working\n//! within a single file or module, this is not an issue.  However, when working with multiple modules\n//! or files, one needs to `use` the generated traits.  The general pattern is that for each `struct`\n//! named `Foo`, the macro generates a trait named `FooStoreFields`.  For example:\n//! ```rust\n//! pub mod foo {\n//!   use reactive_stores::Store;\n\n//!   #[derive(Store)]\n//!   pub struct Foo {\n//!     field: i32,\n//!   }\n//! }\n//!\n//! pub mod user {\n//!   use leptos::prelude::*;\n//!   use reactive_stores::Field;\n//!   // Using FooStore fields here.\n//!   use crate::foo::{ Foo, FooStoreFields };\n//!\n//!   #[component]\n//!   pub fn UseFoo(foo: Field<Foo>) {\n//!     // Without FooStoreFields, foo.field() would fail to compile.\n//!     println!(\"field: {}\", foo.field().read());\n//!   }\n//! }\n//!\n//! # fn main() {\n//! # }\n//! ```\n//! ### Additional field types\n//!\n//! Most of the time, your structs will have fields as in the example above: the struct is comprised\n//! of primitive types, builtin types like [String], or other structs that implement [Store](struct@Store) or [Field].\n//! However, there are some special cases that require some additional understanding.\n//!\n//! #### Option\n//! [`Option<T>`](std::option::Option) behaves pretty much as you would expect, utilizing [.is_some()](std::option::Option::is_some)\n//! and [.is_none()](std::option::Option::is_none) to check the value and  [.unwrap()](OptionStoreExt::unwrap) method to access the inner value.  The [OptionStoreExt]\n//! trait is required to use the [.unwrap()](OptionStoreExt::unwrap) method.  Here is a quick example:\n//! ```rust\n//! // Including the trait OptionStoreExt here is required to use unwrap()\n//! use reactive_stores::{OptionStoreExt, Store};\n//! use reactive_graph::traits::{Get, Read};\n//!\n//! #[derive(Store)]\n//! struct StructWithOption {\n//!     opt_field: Option<i32>,\n//! }\n//!\n//! fn describe(store: &Store<StructWithOption>) -> String {\n//!     if store.opt_field().read().is_some() {\n//!         // Note here we need to use OptionStoreExt or unwrap() would not compile\n//!         format!(\"store has a value {}\", store.opt_field().unwrap().get())\n//!     } else {\n//!         format!(\"store has no value\")\n//!     }\n//! }\n//! let none_store = Store::new(StructWithOption { opt_field: None });\n//! let some_store = Store::new(StructWithOption { opt_field: Some(42)});\n//!\n//! assert_eq!(describe(&none_store), \"store has no value\");\n//! assert_eq!(describe(&some_store), \"store has a value 42\");\n//! ```\n//! #### Vec\n//! [`Vec<T>`](std::vec::Vec) requires some special treatment when trying to access\n//! elements of the vector directly.  Use the [StoreFieldIterator::at_unkeyed()] method to\n//! access a particular value in a [struct@Store] or [Field] for a [std::vec::Vec].  For example:\n//! ```rust\n//! # use reactive_stores::Store;\n//! // Needed to use at_unkeyed() on Vec\n//! use reactive_stores::StoreFieldIter;\n//! use reactive_stores::StoreFieldIterator;\n//! use reactive_graph::traits::Read;\n//! use reactive_graph::traits::Get;\n//!\n//! #[derive(Store)]\n//! struct StructWithVec {\n//!     vec_field: Vec<i32>,\n//! }\n//!\n//! let store = Store::new(StructWithVec { vec_field: vec![1, 2, 3] });\n//!\n//! assert_eq!(store.vec_field().at_unkeyed(0).get(), 1);\n//! assert_eq!(store.vec_field().at_unkeyed(1).get(), 2);\n//! assert_eq!(store.vec_field().at_unkeyed(2).get(), 3);\n//! ```\n//! #### Enum\n//! Enumerated types behave a bit differently as the [`Store`](macro@Store) macro builds underlying traits instead of alternate\n//! enumerated structures.  Each element in an `Enum` generates methods to access it in the store: a\n//! method with the name of the field gives a boolean if the `Enum` is that variant, and possible accessor\n//! methods for anonymous fields of that variant.  For example:\n//! ```rust\n//! use reactive_stores::Store;\n//! use reactive_graph::traits::{Read, Get};\n//!\n//! #[derive(Store)]\n//! enum Choices {\n//!    First,\n//!    Second(String),\n//! }\n//!\n//! let choice_one = Store::new(Choices::First);\n//! let choice_two = Store::new(Choices::Second(\"hello\".to_string()));\n//!\n//! assert!(choice_one.first());\n//! assert!(!choice_one.second());\n//! // Note the use of the accessor method here .second_0()\n//! assert_eq!(choice_two.second_0().unwrap().get(), \"hello\");\n//! ```\n//! #### Box\n//! [`Box<T>`](std::boxed::Box) also requires some special treatment in how you dereference elements of the Box, especially\n//! when trying to build a recursive data structure.  [DerefField](trait@DerefField) provides a [.deref_value()](DerefField::deref_field) method to access\n//! the inner value.  For example:\n//! ```rust\n//! // Note here we need to use DerefField to use deref_field() and OptionStoreExt to use unwrap()\n//! use reactive_stores::{Store, DerefField, OptionStoreExt};\n//! use reactive_graph::traits::{ Read, Get };\n//!\n//! #[derive(Store)]\n//! struct List {\n//!     value: i32,\n//!     #[store]\n//!     child: Option<Box<List>>,\n//! }\n//!\n//! let tree = Store::new(List {\n//!     value: 1,\n//!     child: Some(Box::new(List { value: 2, child: None })),\n//! });\n//!\n//! assert_eq!(tree.child().unwrap().deref_field().value().get(), 2);\n//! ```\n//! ### Implementation Notes\n//!\n//! Every struct field can be understood as an index. For example, given the following definition\n//! ```rust\n//! # use reactive_stores::{Store, Patch};\n//! #[derive(Debug, Store, Patch, Default)]\n//! struct Name {\n//!     first: String,\n//!     last: String,\n//! }\n//! ```\n//! We can think of `first` as `0` and `last` as `1`. This means that any deeply-nested field of a\n//! struct can be described as a path of indices. So, for example:\n//! ```rust\n//! # use reactive_stores::{Store, Patch};\n//! #[derive(Debug, Store, Patch, Default)]\n//! struct User {\n//!     user: Name,\n//! }\n//!\n//! #[derive(Debug, Store, Patch, Default)]\n//! struct Name {\n//!     first: String,\n//!     last: String,\n//! }\n//! ```\n//! Here, given a `User`, `first` can be understood as [`0`, `0`] and `last` is [`0`, `1`].\n//!\n//! This means we can implement a store as the combination of two things:\n//! 1) An `Arc<RwLock<T>>` that holds the actual value\n//! 2) A map from field paths to reactive \"triggers,\" which are signals that have no value but\n//!    track reactivity\n//!\n//! Accessing a field via its getters returns an iterator-like data structure that describes how to\n//! get to that subfield. Calling `.read()` returns a guard that dereferences to the value of that\n//! field in the signal inner `Arc<RwLock<_>>`, and tracks the trigger that corresponds with its\n//! path; calling `.write()` returns a writeable guard, and notifies that same trigger.\n\nuse or_poisoned::OrPoisoned;\nuse reactive_graph::{\n    owner::{ArenaItem, LocalStorage, Storage, SyncStorage},\n    signal::{\n        guards::{Plain, ReadGuard, WriteGuard},\n        ArcTrigger,\n    },\n    traits::{\n        DefinedAt, Dispose, IsDisposed, Notify, ReadUntracked, Track,\n        UntrackableGuard, Write,\n    },\n};\npub use reactive_stores_macro::{Patch, Store};\nuse rustc_hash::FxHashMap;\nuse std::{\n    any::Any,\n    fmt::Debug,\n    hash::Hash,\n    ops::DerefMut,\n    panic::Location,\n    sync::{Arc, RwLock},\n};\n\nmod arc_field;\nmod deref;\nmod field;\nmod iter;\nmod keyed;\nmod len;\nmod option;\nmod patch;\nmod path;\n#[cfg(feature = \"serde\")]\nmod serde;\n#[cfg(feature = \"slotmap\")]\nmod slotmap;\nmod store_field;\nmod subfield;\n\npub use arc_field::ArcField;\npub use deref::*;\npub use field::Field;\npub use iter::*;\npub use keyed::*;\npub use len::Len;\npub use option::*;\npub use patch::*;\npub use path::{StorePath, StorePathSegment};\npub use store_field::StoreField;\npub use subfield::Subfield;\n\n#[derive(Debug, Default)]\nstruct TriggerMap(FxHashMap<StorePath, StoreFieldTrigger>);\n\n/// The reactive trigger that can be used to track updates to a store field.\n#[derive(Debug, Clone, Default)]\npub struct StoreFieldTrigger {\n    pub(crate) this: ArcTrigger,\n    pub(crate) children: ArcTrigger,\n}\n\nimpl StoreFieldTrigger {\n    /// Creates a new trigger.\n    pub fn new() -> Self {\n        Self::default()\n    }\n}\n\nimpl TriggerMap {\n    fn get_or_insert(&mut self, key: StorePath) -> StoreFieldTrigger {\n        if let Some(trigger) = self.0.get(&key) {\n            trigger.clone()\n        } else {\n            let new = StoreFieldTrigger::new();\n            self.0.insert(key, new.clone());\n            new\n        }\n    }\n\n    #[allow(unused)]\n    fn remove(&mut self, key: &StorePath) -> Option<StoreFieldTrigger> {\n        self.0.remove(key)\n    }\n}\n\n/// Manages the keys for a keyed field, including the ability to remove and reuse keys.\npub struct FieldKeys<K> {\n    spare_keys: Vec<StorePathSegment>,\n    current_key: usize,\n    keys: FxHashMap<K, (StorePathSegment, usize)>,\n}\n\nimpl<K> FieldKeys<K>\nwhere\n    K: Debug + Hash + PartialEq + Eq,\n{\n    /// Creates a new set of keys.\n    pub fn new(from_keys: Vec<K>) -> Self {\n        let mut keys = FxHashMap::with_capacity_and_hasher(\n            from_keys.len(),\n            Default::default(),\n        );\n        for (idx, key) in from_keys.into_iter().enumerate() {\n            let segment = idx.into();\n            keys.insert(key, (segment, idx));\n        }\n\n        Self {\n            spare_keys: Vec::new(),\n            current_key: keys.len().saturating_sub(1),\n            keys,\n        }\n    }\n}\n\nimpl<K> FieldKeys<K>\nwhere\n    K: Hash + PartialEq + Eq,\n{\n    /// Returns a copy of the path segment to the value identified by the key\n    ///\n    /// # Usage\n    ///\n    /// You shouldn't call this method from your code, since it's a part of\n    /// implementation details of `reactive_stores`. This method was exposed\n    /// to implement the derive `Patch` macro for keyed fields.\n    #[doc(hidden)]\n    pub fn get(&self, key: &K) -> Option<(StorePathSegment, usize)> {\n        self.keys.get(key).copied()\n    }\n\n    fn next_key(&mut self) -> StorePathSegment {\n        self.spare_keys.pop().unwrap_or_else(|| {\n            self.current_key += 1;\n            self.current_key.into()\n        })\n    }\n\n    fn update(\n        &mut self,\n        iter: impl IntoIterator<Item = K>,\n    ) -> Vec<(usize, StorePathSegment)> {\n        let new_keys = iter\n            .into_iter()\n            .enumerate()\n            .map(|(idx, key)| (key, idx))\n            .collect::<FxHashMap<K, usize>>();\n\n        let mut index_keys = Vec::with_capacity(new_keys.len());\n\n        // remove old keys and recycle the slots\n        self.keys.retain(|key, old_entry| match new_keys.get(key) {\n            Some(idx) => {\n                old_entry.1 = *idx;\n                true\n            }\n            None => {\n                self.spare_keys.push(old_entry.0);\n                false\n            }\n        });\n\n        // add new keys\n        for (key, idx) in new_keys {\n            match self.keys.get(&key) {\n                Some((segment, idx)) => index_keys.push((*idx, *segment)),\n                None => {\n                    let path = self.next_key();\n                    self.keys.insert(key, (path, idx));\n                    index_keys.push((idx, path));\n                }\n            }\n        }\n\n        index_keys\n    }\n}\n\nimpl<K> Default for FieldKeys<K> {\n    fn default() -> Self {\n        Self {\n            spare_keys: Default::default(),\n            current_key: Default::default(),\n            keys: Default::default(),\n        }\n    }\n}\n\ntype Map<K, V> = Arc<std::sync::RwLock<std::collections::HashMap<K, V>>>;\n\n/// A map of the keys for a keyed subfield.\n#[derive(Clone, Default)]\npub struct KeyMap(\n    /// Path to subfield -> Keys in keyed subfield\n    Map<StorePath, Box<dyn Any + Send + Sync>>,\n    /// Map index -> key\n    Map<(StorePath, usize), StorePathSegment>,\n);\n\nimpl KeyMap {\n    /// Transforms the keys related to the field identified by `path`.\n    ///\n    /// # Arguments\n    ///\n    /// - **path** - path to the field with collection\n    /// - **fun** - Transforms an instance of [FieldKeys] into the result\n    ///   \n    ///   ## Return value\n    ///\n    ///   callback should return a tuple ( result, new_keys)\n    ///\n    ///   - **result** - this value will be passed as a result\n    ///   - **new_keys** - is a vector of new keys to be added into reverse mapping\n    ///     (path, idx) -> (path segment) map\n    ///     \n    ///     ### Entries\n    ///\n    ///     Entry in the vector is a tuple (idx, segment) where\n    ///\n    ///     - **idx** - index of the element in the collection\n    ///     - **segment** - key of the element in the collection\n    ///     \n    /// - **initialize** - it is the set of keys with which to initialize the\n    ///   KeyMap for this field, if there aren't keys listed yet. In all cases\n    ///   inside the library this is `|| self.latest_keys()` or `|| self.inner.latest_keys()`\n    ///\n    ///   This function will be called **only** if KeyMap doesn't have entry for\n    ///   `path`.\n    ///\n    ///   ## Returns\n    ///\n    ///   A vector of keys which will be used if KeyMap doesn't have an entry for\n    ///   given `path`\n    ///\n    /// # Returns\n    ///\n    /// - [None] if path doesn't point to the keyed field\n    /// - **result** value returned from `fun` callback\n    ///\n    /// # Usage\n    ///\n    /// You should not call this method directly from your code, as it's\n    /// an implementation detail of `reactive_stores`. This method was exposed\n    /// to implement the derive `Patch` macro for keyed fields.\n    #[doc(hidden)]\n    pub fn with_field_keys<K, T>(\n        &self,\n        path: StorePath,\n        fun: impl FnOnce(&mut FieldKeys<K>) -> (T, Vec<(usize, StorePathSegment)>),\n        initialize: impl FnOnce() -> Vec<K>,\n    ) -> Option<T>\n    where\n        K: Debug + Hash + PartialEq + Eq + Send + Sync + 'static,\n    {\n        let mut guard = self.0.write().or_poisoned();\n        let entry = guard\n            .entry(path.clone())\n            .or_insert_with(|| Box::new(FieldKeys::new(initialize())));\n\n        let entry = entry.downcast_mut::<FieldKeys<K>>()?;\n        let (result, new_keys) = fun(entry);\n        if !new_keys.is_empty() {\n            for (idx, segment) in new_keys {\n                self.1\n                    .write()\n                    .or_poisoned()\n                    .insert((path.clone(), idx), segment);\n            }\n        }\n        Some(result)\n    }\n\n    fn contains_key(&self, key: &StorePath) -> bool {\n        self.0.read().or_poisoned().contains_key(key)\n    }\n\n    fn get_key_for_index(\n        &self,\n        key: &(StorePath, usize),\n    ) -> Option<StorePathSegment> {\n        self.1.read().or_poisoned().get(key).copied()\n    }\n}\n\n/// A reference-counted container for a reactive store.\n///\n/// The type `T` should be a struct that has been annotated with `#[derive(Store)]`.\n///\n/// This adds a getter method for each field to `Store<T>`, which allow accessing reactive versions\n/// of each individual field of the struct.\npub struct ArcStore<T> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    pub(crate) value: Arc<RwLock<T>>,\n    signals: Arc<RwLock<TriggerMap>>,\n    keys: KeyMap,\n}\n\nimpl<T> ArcStore<T> {\n    /// Creates a new store from the initial value.\n    pub fn new(value: T) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            value: Arc::new(RwLock::new(value)),\n            signals: Default::default(),\n            keys: Default::default(),\n        }\n    }\n}\n\nimpl<T: Default> Default for ArcStore<T> {\n    fn default() -> Self {\n        Self::new(T::default())\n    }\n}\n\nimpl<T: Debug> Debug for ArcStore<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut f = f.debug_struct(\"ArcStore\");\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let f = f.field(\"defined_at\", &self.defined_at);\n        f.field(\"value\", &self.value)\n            .field(\"signals\", &self.signals)\n            .finish()\n    }\n}\n\nimpl<T> Clone for ArcStore<T> {\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            value: Arc::clone(&self.value),\n            signals: Arc::clone(&self.signals),\n            keys: self.keys.clone(),\n        }\n    }\n}\n\nimpl<T> DefinedAt for ArcStore<T> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T> IsDisposed for ArcStore<T> {\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        false\n    }\n}\n\nimpl<T> ReadUntracked for ArcStore<T>\nwhere\n    T: 'static,\n{\n    type Value = ReadGuard<T, Plain<T>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)\n    }\n}\n\nimpl<T> Write for ArcStore<T>\nwhere\n    T: 'static,\n{\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.writer()\n            .map(|writer| WriteGuard::new(self.clone(), writer))\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        let mut writer = self.writer()?;\n        writer.untrack();\n        Some(writer)\n    }\n}\n\nimpl<T: 'static> Track for ArcStore<T> {\n    fn track(&self) {\n        self.track_field();\n    }\n}\n\nimpl<T: 'static> Notify for ArcStore<T> {\n    fn notify(&self) {\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.notify();\n        trigger.children.notify();\n    }\n}\n\n/// An arena-allocated container for a reactive store.\n///\n/// The type `T` should be a struct that has been annotated with `#[derive(Store)]`.\n///\n/// This adds a getter method for each field to `Store<T>`, which allow accessing reactive versions\n/// of each individual field of the struct.\n///\n/// This follows the same ownership rules as arena-allocated types like\n/// [`RwSignal`](reactive_graph::signal::RwSignal).\npub struct Store<T, S = SyncStorage> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    inner: ArenaItem<ArcStore<T>, S>,\n}\n\nimpl<T> Store<T>\nwhere\n    T: Send + Sync + 'static,\n{\n    /// Creates a new store with the initial value.\n    pub fn new(value: T) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcStore::new(value)),\n        }\n    }\n}\n\nimpl<T, S> PartialEq for Store<T, S> {\n    fn eq(&self, other: &Self) -> bool {\n        self.inner == other.inner\n    }\n}\n\nimpl<T, S> Eq for Store<T, S> {}\n\nimpl<T> Store<T, LocalStorage>\nwhere\n    T: 'static,\n{\n    /// Creates a new store for a type that is `!Send`.\n    ///\n    /// This pins the value to the current thread. Accessing it from any other thread will panic.\n    pub fn new_local(value: T) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner: ArenaItem::new_with_storage(ArcStore::new(value)),\n        }\n    }\n}\n\nimpl<T> Default for Store<T>\nwhere\n    T: Default + Send + Sync + 'static,\n{\n    fn default() -> Self {\n        Self::new(T::default())\n    }\n}\n\nimpl<T> Default for Store<T, LocalStorage>\nwhere\n    T: Default + 'static,\n{\n    fn default() -> Self {\n        Self::new_local(T::default())\n    }\n}\n\nimpl<T: Debug, S> Debug for Store<T, S>\nwhere\n    S: Debug,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut f = f.debug_struct(\"Store\");\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        let f = f.field(\"defined_at\", &self.defined_at);\n        f.field(\"inner\", &self.inner).finish()\n    }\n}\n\nimpl<T, S> Clone for Store<T, S> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, S> Copy for Store<T, S> {}\n\nimpl<T, S> DefinedAt for Store<T, S> {\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<T, S> IsDisposed for Store<T, S>\nwhere\n    T: 'static,\n{\n    #[inline(always)]\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<T, S> Dispose for Store<T, S>\nwhere\n    T: 'static,\n{\n    fn dispose(self) {\n        self.inner.dispose();\n    }\n}\n\nimpl<T, S> ReadUntracked for Store<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStore<T>>,\n{\n    type Value = ReadGuard<T, Plain<T>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.inner\n            .try_get_value()\n            .and_then(|inner| inner.try_read_untracked())\n    }\n}\n\nimpl<T, S> Write for Store<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStore<T>>,\n{\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.writer().map(|writer| WriteGuard::new(*self, writer))\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        let mut writer = self.writer()?;\n        writer.untrack();\n        Some(writer)\n    }\n}\n\nimpl<T, S> Track for Store<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStore<T>>,\n{\n    fn track(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.track();\n        }\n    }\n}\n\nimpl<T, S> Notify for Store<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStore<T>>,\n{\n    fn notify(&self) {\n        if let Some(inner) = self.inner.try_get_value() {\n            inner.notify();\n        }\n    }\n}\n\nimpl<T, S> From<ArcStore<T>> for Store<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStore<T>>,\n{\n    fn from(value: ArcStore<T>) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: value.defined_at,\n            inner: ArenaItem::new_with_storage(value),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{self as reactive_stores, Patch, Store, StoreFieldIterator};\n    use reactive_graph::{\n        effect::Effect,\n        owner::StoredValue,\n        traits::{Read, ReadUntracked, Set, Track, Update, Write},\n    };\n    use std::sync::{\n        atomic::{AtomicUsize, Ordering},\n        Arc,\n    };\n\n    pub async fn tick() {\n        tokio::time::sleep(std::time::Duration::from_micros(1)).await;\n    }\n\n    #[derive(Debug, Store, Patch, Default)]\n    struct Todos {\n        user: String,\n        todos: Vec<Todo>,\n    }\n\n    #[derive(Debug, Store, Patch, Default)]\n    struct Todo {\n        label: String,\n        completed: bool,\n    }\n\n    impl Todo {\n        pub fn new(label: impl ToString) -> Self {\n            Self {\n                label: label.to_string(),\n                completed: false,\n            }\n        }\n    }\n\n    fn data() -> Todos {\n        Todos {\n            user: \"Bob\".to_string(),\n            todos: vec![\n                Todo {\n                    label: \"Create reactive store\".to_string(),\n                    completed: true,\n                },\n                Todo {\n                    label: \"???\".to_string(),\n                    completed: false,\n                },\n                Todo {\n                    label: \"Profit\".to_string(),\n                    completed: false,\n                },\n            ],\n        }\n    }\n\n    #[derive(Debug, Clone, Store, Patch, Default)]\n    struct Foo {\n        id: i32,\n        bar: Bar,\n    }\n\n    #[derive(Debug, Clone, Store, Patch, Default)]\n    struct Bar {\n        bar_signature: i32,\n        baz: Baz,\n    }\n\n    #[derive(Debug, Clone, Store, Patch, Default)]\n    struct Baz {\n        more_data: i32,\n        baw: Baw,\n    }\n\n    #[derive(Debug, Clone, Store, Patch, Default)]\n    struct Baw {\n        more_data: i32,\n        end: i32,\n    }\n\n    #[tokio::test]\n    async fn mutating_field_triggers_effect() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(data());\n        assert_eq!(store.read_untracked().todos.len(), 3);\n        assert_eq!(store.user().read_untracked().as_str(), \"Bob\");\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n                println!(\"{:?}\", *store.user().read());\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        tick().await;\n        tick().await;\n        store.user().set(\"Greg\".into());\n        tick().await;\n        store.user().set(\"Carol\".into());\n        tick().await;\n        store.user().update(|name| name.push_str(\"!!!\"));\n        tick().await;\n        // the effect reads from `user`, so it should trigger every time\n        assert_eq!(combined_count.load(Ordering::Relaxed), 4);\n    }\n\n    #[tokio::test]\n    async fn other_field_does_not_notify() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(data());\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n                println!(\"{:?}\", *store.todos().read());\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        tick().await;\n        store.user().set(\"Greg\".into());\n        tick().await;\n        store.user().set(\"Carol\".into());\n        tick().await;\n        store.user().update(|name| name.push_str(\"!!!\"));\n        tick().await;\n        // the effect reads from `todos`, so it shouldn't trigger every time\n        assert_eq!(combined_count.load(Ordering::Relaxed), 1);\n    }\n\n    #[tokio::test]\n    async fn parent_does_notify() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(data());\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n                println!(\"{:?}\", *store.todos().read());\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        tick().await;\n        tick().await;\n        store.set(Todos::default());\n        tick().await;\n        store.set(data());\n        tick().await;\n        assert_eq!(combined_count.load(Ordering::Relaxed), 3);\n    }\n\n    #[tokio::test]\n    async fn changes_do_notify_parent() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(data());\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n                println!(\"{:?}\", *store.read());\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        tick().await;\n        tick().await;\n        store.user().set(\"Greg\".into());\n        tick().await;\n        store.user().set(\"Carol\".into());\n        tick().await;\n        store.user().update(|name| name.push_str(\"!!!\"));\n        tick().await;\n        store.todos().write().clear();\n        tick().await;\n        assert_eq!(combined_count.load(Ordering::Relaxed), 5);\n    }\n\n    #[tokio::test]\n    async fn iterator_tracks_the_field() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(data());\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n                println!(\n                    \"{:?}\",\n                    store.todos().iter_unkeyed().collect::<Vec<_>>()\n                );\n                combined_count.store(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        store\n            .todos()\n            .write()\n            .push(Todo::new(\"Create reactive store?\"));\n        tick().await;\n        store.todos().write().push(Todo::new(\"???\"));\n        tick().await;\n        store.todos().write().push(Todo::new(\"Profit!\"));\n        // the effect only reads from `todos`, so it should trigger only the first time\n        assert_eq!(combined_count.load(Ordering::Relaxed), 1);\n    }\n\n    #[tokio::test]\n    async fn patching_only_notifies_changed_field() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(Todos {\n            user: \"Alice\".into(),\n            todos: vec![],\n        });\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n                println!(\"{:?}\", *store.todos().read());\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        tick().await;\n        tick().await;\n        store.patch(Todos {\n            user: \"Bob\".into(),\n            todos: vec![],\n        });\n        tick().await;\n        store.patch(Todos {\n            user: \"Carol\".into(),\n            todos: vec![],\n        });\n        tick().await;\n        assert_eq!(combined_count.load(Ordering::Relaxed), 1);\n\n        store.patch(Todos {\n            user: \"Carol\".into(),\n            todos: vec![Todo {\n                label: \"First Todo\".into(),\n                completed: false,\n            }],\n        });\n        tick().await;\n        assert_eq!(combined_count.load(Ordering::Relaxed), 2);\n    }\n\n    #[tokio::test]\n    async fn patching_only_notifies_changed_field_with_custom_patch() {\n        _ = any_spawner::Executor::init_tokio();\n\n        #[derive(Debug, Store, Patch, Default)]\n        struct CustomTodos {\n            #[patch(|this, new| *this = new)]\n            user: String,\n            todos: Vec<CustomTodo>,\n        }\n\n        #[derive(Debug, Store, Patch, Default)]\n        struct CustomTodo {\n            label: String,\n            completed: bool,\n        }\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(CustomTodos {\n            user: \"Alice\".into(),\n            todos: vec![],\n        });\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n                println!(\"{:?}\", *store.user().read());\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        tick().await;\n        tick().await;\n        store.patch(CustomTodos {\n            user: \"Bob\".into(),\n            todos: vec![],\n        });\n        tick().await;\n        assert_eq!(combined_count.load(Ordering::Relaxed), 2);\n        store.patch(CustomTodos {\n            user: \"Carol\".into(),\n            todos: vec![],\n        });\n        tick().await;\n        assert_eq!(combined_count.load(Ordering::Relaxed), 3);\n\n        store.patch(CustomTodos {\n            user: \"Carol\".into(),\n            todos: vec![CustomTodo {\n                label: \"First CustomTodo\".into(),\n                completed: false,\n            }],\n        });\n        tick().await;\n        assert_eq!(combined_count.load(Ordering::Relaxed), 3);\n    }\n\n    // regression test for https://github.com/leptos-rs/leptos/issues/3523\n    #[tokio::test]\n    async fn notifying_all_descendants() {\n        use reactive_graph::traits::*;\n\n        _ = any_spawner::Executor::init_tokio();\n\n        let store = Store::new(Foo {\n            id: 42,\n            bar: Bar {\n                bar_signature: 69,\n                baz: Baz {\n                    more_data: 9999,\n                    baw: Baw {\n                        more_data: 22,\n                        end: 1112,\n                    },\n                },\n            },\n        });\n\n        let store_runs = StoredValue::new(0);\n        let id_runs = StoredValue::new(0);\n        let bar_runs = StoredValue::new(0);\n        let bar_signature_runs = StoredValue::new(0);\n        let bar_baz_runs = StoredValue::new(0);\n        let more_data_runs = StoredValue::new(0);\n        let baz_baw_end_runs = StoredValue::new(0);\n\n        Effect::new_sync(move |_| {\n            println!(\"foo: {:?}\", store.get());\n            *store_runs.write_value() += 1;\n        });\n\n        Effect::new_sync(move |_| {\n            println!(\"foo.id: {:?}\", store.id().get());\n            *id_runs.write_value() += 1;\n        });\n\n        Effect::new_sync(move |_| {\n            println!(\"foo.bar: {:?}\", store.bar().get());\n            *bar_runs.write_value() += 1;\n        });\n\n        Effect::new_sync(move |_| {\n            println!(\n                \"foo.bar.bar_signature: {:?}\",\n                store.bar().bar_signature().get()\n            );\n            *bar_signature_runs.write_value() += 1;\n        });\n\n        Effect::new_sync(move |_| {\n            println!(\"foo.bar.baz: {:?}\", store.bar().baz().get());\n            *bar_baz_runs.write_value() += 1;\n        });\n\n        Effect::new_sync(move |_| {\n            println!(\n                \"foo.bar.baz.more_data: {:?}\",\n                store.bar().baz().more_data().get()\n            );\n            *more_data_runs.write_value() += 1;\n        });\n\n        Effect::new_sync(move |_| {\n            println!(\n                \"foo.bar.baz.baw.end: {:?}\",\n                store.bar().baz().baw().end().get()\n            );\n            *baz_baw_end_runs.write_value() += 1;\n        });\n\n        println!(\"[INITIAL EFFECT RUN]\");\n        tick().await;\n        println!(\"\\n\\n[SETTING STORE]\");\n        store.set(Default::default());\n        tick().await;\n        println!(\"\\n\\n[SETTING STORE.BAR.BAZ]\");\n        store.bar().baz().set(Default::default());\n        tick().await;\n\n        assert_eq!(store_runs.get_value(), 3);\n        assert_eq!(id_runs.get_value(), 2);\n        assert_eq!(bar_runs.get_value(), 3);\n        assert_eq!(bar_signature_runs.get_value(), 2);\n        assert_eq!(bar_baz_runs.get_value(), 3);\n        assert_eq!(more_data_runs.get_value(), 3);\n        assert_eq!(baz_baw_end_runs.get_value(), 3);\n    }\n\n    #[tokio::test]\n    async fn changing_parent_notifies_subfield() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(Foo {\n            id: 42,\n            bar: Bar {\n                bar_signature: 69,\n                baz: Baz {\n                    more_data: 9999,\n                    baw: Baw {\n                        more_data: 22,\n                        end: 1112,\n                    },\n                },\n            },\n        });\n\n        let tracked_field = store.bar().baz().more_data();\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n\n                // we only track `more`, but this should still be notified\n                // when its parent fields `bar` or `baz` change\n                println!(\"{:?}\", *tracked_field.read());\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        tick().await;\n        tick().await;\n\n        store.bar().baz().set(Baz {\n            more_data: 42,\n            baw: Baw {\n                more_data: 11,\n                end: 31,\n            },\n        });\n        tick().await;\n        store.bar().set(Bar {\n            bar_signature: 23,\n            baz: Baz {\n                more_data: 32,\n                baw: Baw {\n                    more_data: 432,\n                    end: 423,\n                },\n            },\n        });\n        tick().await;\n\n        assert_eq!(combined_count.load(Ordering::Relaxed), 3);\n    }\n\n    #[tokio::test]\n    async fn changing_parent_notifies_unkeyed_child() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(data());\n\n        let tracked_field = store.todos().at_unkeyed(0);\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n\n                // we only track `more`, but this should still be notified\n                // when its parent fields `bar` or `baz` change\n                println!(\"{:?}\", *tracked_field.read());\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        tick().await;\n        tick().await;\n\n        store.todos().write().pop();\n        tick().await;\n\n        store.todos().write().push(Todo {\n            label: \"another one\".into(),\n            completed: false,\n        });\n        tick().await;\n\n        assert_eq!(combined_count.load(Ordering::Relaxed), 3);\n    }\n\n    #[tokio::test]\n    async fn untracked_write_on_subfield_shouldnt_notify() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let name_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(data());\n\n        let tracked_field = store.user();\n\n        Effect::new_sync({\n            let name_count = Arc::clone(&name_count);\n            move |_| {\n                tracked_field.track();\n                name_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(name_count.load(Ordering::Relaxed), 1);\n\n        tracked_field.write().push('!');\n        tick().await;\n        assert_eq!(name_count.load(Ordering::Relaxed), 2);\n\n        tracked_field.write_untracked().push('!');\n        tick().await;\n        assert_eq!(name_count.load(Ordering::Relaxed), 2);\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/option.rs",
    "content": "use crate::{StoreField, Subfield};\nuse reactive_graph::traits::{FlattenOptionRefOption, Read, ReadUntracked};\nuse std::ops::Deref;\n\n/// Extends optional store fields, with the ability to unwrap or map over them.\npub trait OptionStoreExt\nwhere\n    Self: StoreField<Value = Option<Self::Output>>,\n{\n    /// The inner type of the `Option<_>` this field holds.\n    type Output;\n\n    /// Provides access to the inner value, as a subfield, unwrapping the outer value.\n    fn unwrap(self) -> Subfield<Self, Option<Self::Output>, Self::Output>;\n\n    /// Inverts a subfield of an `Option` to an `Option` of a subfield.\n    fn invert(\n        self,\n    ) -> Option<Subfield<Self, Option<Self::Output>, Self::Output>> {\n        self.map(|f| f)\n    }\n\n    /// Reactively maps over the field.\n    ///\n    /// This returns `None` if the subfield is currently `None`,\n    /// and a new store subfield with the inner value if it is `Some`. This can be used in some  \n    /// other reactive context, which will cause it to re-run if the field toggles between `None`\n    /// and `Some(_)`.\n    fn map<U>(\n        self,\n        map_fn: impl FnOnce(Subfield<Self, Option<Self::Output>, Self::Output>) -> U,\n    ) -> Option<U>;\n\n    /// Unreactively maps over the field.\n    ///\n    /// This returns `None` if the subfield is currently `None`,\n    /// and a new store subfield with the inner value if it is `Some`. This is an unreactive variant of\n    /// `[OptionStoreExt::map]`, and will not cause the reactive context to re-run if the field changes.\n    fn map_untracked<U>(\n        self,\n        map_fn: impl FnOnce(Subfield<Self, Option<Self::Output>, Self::Output>) -> U,\n    ) -> Option<U>;\n}\n\nimpl<T, S> OptionStoreExt for S\nwhere\n    S: StoreField<Value = Option<T>> + Read + ReadUntracked,\n    <S as Read>::Value: Deref<Target = Option<T>>,\n    <S as ReadUntracked>::Value: Deref<Target = Option<T>>,\n{\n    type Output = T;\n\n    fn unwrap(self) -> Subfield<Self, Option<Self::Output>, Self::Output> {\n        Subfield::new(\n            self,\n            0.into(),\n            |t| t.as_ref().unwrap(),\n            |t| t.as_mut().unwrap(),\n        )\n    }\n\n    fn map<U>(\n        self,\n        map_fn: impl FnOnce(Subfield<S, Option<T>, T>) -> U,\n    ) -> Option<U> {\n        if self.try_read().as_deref().flatten().is_some() {\n            Some(map_fn(self.unwrap()))\n        } else {\n            None\n        }\n    }\n\n    fn map_untracked<U>(\n        self,\n        map_fn: impl FnOnce(Subfield<S, Option<T>, T>) -> U,\n    ) -> Option<U> {\n        if self.try_read_untracked().as_deref().flatten().is_some() {\n            Some(map_fn(self.unwrap()))\n        } else {\n            None\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{self as reactive_stores, Patch as _, Store};\n    use any_spawner::Executor;\n    use reactive_graph::{\n        effect::Effect,\n        traits::{Get, Read, ReadUntracked, Set, Write},\n    };\n    use reactive_stores_macro::Patch;\n    use std::sync::{\n        atomic::{AtomicUsize, Ordering},\n        Arc,\n    };\n\n    pub async fn tick() {\n        Executor::tick().await;\n    }\n\n    #[derive(Debug, Clone, Store)]\n    pub struct User {\n        pub name: Option<Name>,\n    }\n\n    #[derive(Debug, Clone, Store)]\n    pub struct Name {\n        pub first_name: Option<String>,\n    }\n\n    #[tokio::test]\n    async fn substores_reachable_through_option() {\n        use crate::OptionStoreExt;\n\n        _ = any_spawner::Executor::init_tokio();\n\n        let combined_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(User { name: None });\n\n        Effect::new_sync({\n            let combined_count = Arc::clone(&combined_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"first run\");\n                } else {\n                    println!(\"next run\");\n                }\n\n                if store.name().read().is_some() {\n                    println!(\n                        \"inner value = {:?}\",\n                        *store.name().unwrap().first_name().read()\n                    );\n                } else {\n                    println!(\"no inner value\");\n                }\n\n                combined_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        store.name().set(Some(Name {\n            first_name: Some(\"Greg\".into()),\n        }));\n        tick().await;\n        store.name().set(None);\n        tick().await;\n        store.name().set(Some(Name {\n            first_name: Some(\"Bob\".into()),\n        }));\n        tick().await;\n        store\n            .name()\n            .unwrap()\n            .first_name()\n            .write()\n            .as_mut()\n            .unwrap()\n            .push_str(\"!!!\");\n        tick().await;\n        assert_eq!(combined_count.load(Ordering::Relaxed), 5);\n        assert_eq!(\n            store\n                .name()\n                .read_untracked()\n                .as_ref()\n                .unwrap()\n                .first_name\n                .as_ref()\n                .unwrap(),\n            \"Bob!!!\"\n        );\n    }\n\n    #[tokio::test]\n    async fn mapping_over_optional_store_field() {\n        use crate::OptionStoreExt;\n\n        _ = any_spawner::Executor::init_tokio();\n\n        let parent_count = Arc::new(AtomicUsize::new(0));\n        let inner_count = Arc::new(AtomicUsize::new(0));\n\n        let store = Store::new(User { name: None });\n\n        Effect::new_sync({\n            let parent_count = Arc::clone(&parent_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"parent: first run\");\n                } else {\n                    println!(\"parent: next run\");\n                }\n\n                println!(\"  is_some = {}\", store.name().read().is_some());\n                parent_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let inner_count = Arc::clone(&inner_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"inner: first run\");\n                } else {\n                    println!(\"inner: next run\");\n                }\n\n                println!(\n                    \"store inner value length = {:?}\",\n                    store.name().map(|inner| inner\n                        .first_name()\n                        .get()\n                        .unwrap_or_default()\n                        .len())\n                );\n                inner_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(parent_count.load(Ordering::Relaxed), 1);\n        assert_eq!(inner_count.load(Ordering::Relaxed), 1);\n\n        store.name().set(Some(Name {\n            first_name: Some(\"Greg\".into()),\n        }));\n        tick().await;\n        assert_eq!(parent_count.load(Ordering::Relaxed), 2);\n        assert_eq!(inner_count.load(Ordering::Relaxed), 2);\n\n        println!(\"\\nUpdating first name only\");\n        store\n            .name()\n            .unwrap()\n            .first_name()\n            .write()\n            .as_mut()\n            .unwrap()\n            .push_str(\"!!!\");\n\n        tick().await;\n        assert_eq!(parent_count.load(Ordering::Relaxed), 3);\n        assert_eq!(inner_count.load(Ordering::Relaxed), 3);\n    }\n\n    #[tokio::test]\n    async fn patch() {\n        use crate::OptionStoreExt;\n\n        _ = any_spawner::Executor::init_tokio();\n\n        #[derive(Debug, Clone, Store, Patch)]\n        struct Outer {\n            inner: Option<Inner>,\n        }\n\n        #[derive(Debug, Clone, Store, Patch)]\n        struct Inner {\n            first: String,\n            second: String,\n        }\n\n        let store = Store::new(Outer {\n            inner: Some(Inner {\n                first: \"A\".to_owned(),\n                second: \"B\".to_owned(),\n            }),\n        });\n\n        let parent_count = Arc::new(AtomicUsize::new(0));\n        let inner_first_count = Arc::new(AtomicUsize::new(0));\n        let inner_second_count = Arc::new(AtomicUsize::new(0));\n\n        Effect::new_sync({\n            let parent_count = Arc::clone(&parent_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"parent: first run\");\n                } else {\n                    println!(\"parent: next run\");\n                }\n\n                println!(\"  value = {:?}\", store.inner().get());\n                parent_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let inner_first_count = Arc::clone(&inner_first_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"inner_first: first run\");\n                } else {\n                    println!(\"inner_first: next run\");\n                }\n\n                // note: we specifically want to test whether using `.patch()`\n                // correctly limits notifications on the first field when only the second\n                // field has changed\n                //\n                // `.map()` would also track the parent field (to track when it changed from Some\n                // to None), which would mean the notification numbers were always the same\n                //\n                // so here, we'll do `.map_untracked()`, but in general in a real case you'd want\n                // to use `.map()` so that if the parent switches to None you do track that\n                println!(\n                    \"  value = {:?}\",\n                    store.inner().map_untracked(|inner| inner.first().get())\n                );\n                inner_first_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let inner_second_count = Arc::clone(&inner_second_count);\n            move |prev: Option<()>| {\n                if prev.is_none() {\n                    println!(\"inner_second: first run\");\n                } else {\n                    println!(\"inner_second: next run\");\n                }\n\n                println!(\n                    \"  value = {:?}\",\n                    store.inner().map(|inner| inner.second().get())\n                );\n                inner_second_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(parent_count.load(Ordering::Relaxed), 1);\n        assert_eq!(inner_first_count.load(Ordering::Relaxed), 1);\n        assert_eq!(inner_second_count.load(Ordering::Relaxed), 1);\n\n        println!(\"\\npatching with A/C\");\n        store.patch(Outer {\n            inner: Some(Inner {\n                first: \"A\".to_string(),\n                second: \"C\".to_string(),\n            }),\n        });\n\n        tick().await;\n        assert_eq!(parent_count.load(Ordering::Relaxed), 2);\n        assert_eq!(inner_first_count.load(Ordering::Relaxed), 1);\n        assert_eq!(inner_second_count.load(Ordering::Relaxed), 2);\n\n        store.patch(Outer { inner: None });\n\n        tick().await;\n        assert_eq!(parent_count.load(Ordering::Relaxed), 3);\n        assert_eq!(inner_first_count.load(Ordering::Relaxed), 2);\n        assert_eq!(inner_second_count.load(Ordering::Relaxed), 3);\n\n        println!(\"\\npatching with A/B\");\n        store.patch(Outer {\n            inner: Some(Inner {\n                first: \"A\".to_string(),\n                second: \"B\".to_string(),\n            }),\n        });\n\n        tick().await;\n        assert_eq!(parent_count.load(Ordering::Relaxed), 4);\n        assert_eq!(inner_first_count.load(Ordering::Relaxed), 2);\n        assert_eq!(inner_second_count.load(Ordering::Relaxed), 4);\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/patch.rs",
    "content": "use crate::{path::StorePath, KeyMap, KeyedAccess, KeyedSubfield, StoreField};\nuse indexmap::IndexMap;\nuse itertools::{EitherOrBoth, Itertools};\nuse reactive_graph::traits::{Notify, UntrackableGuard};\nuse std::{\n    borrow::Cow,\n    collections::{BTreeMap, HashMap},\n    fmt::Debug,\n    hash::Hash,\n    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},\n    num::{\n        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8,\n        NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64,\n        NonZeroU8, NonZeroUsize,\n    },\n    rc::Rc,\n    sync::Arc,\n};\n\n/// Allows updating a store or field in place with a new value.\npub trait Patch {\n    /// The type of the new value.\n    type Value;\n\n    /// Patches a store or field with a new value, only notifying fields that have changed.\n    fn patch(&self, new: Self::Value);\n}\n\nimpl<T> Patch for T\nwhere\n    T: StoreField,\n    T::Value: PatchField,\n{\n    type Value = T::Value;\n\n    fn patch(&self, new: Self::Value) {\n        let path = self.path_unkeyed().into_iter().collect::<StorePath>();\n        let keys = self.keys();\n\n        if let Some(mut writer) = self.writer() {\n            // don't track the writer for the whole store\n            writer.untrack();\n            let mut notify = |path: &StorePath| {\n                self.triggers_for_path_unkeyed(path.to_owned()).notify();\n            };\n            writer.patch_field(new, &path, &mut notify, keys.as_ref());\n        }\n    }\n}\n\nimpl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Clone,\n    for<'a> &'a T: IntoIterator,\n    Self: StoreField<Value = T>,\n    <Self as StoreField>::Value: PatchFieldKeyed<K>,\n    Inner: StoreField<Value = Prev>,\n    T: PatchFieldKeyed<K>,\n    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    Prev: 'static,\n{\n    /// This implements a custom, keyed patch for keyed subfields.\n    ///\n    /// It is used in the same way as the [`Patch`] trait, but uses a keyed data diff for\n    /// data structures that implement [`PatchFieldKeyed`].\n    pub fn patch(&self, new: T) {\n        let path = self.path_unkeyed().into_iter().collect::<StorePath>();\n        let keys = self.keys();\n\n        let structure_changed = if let Some(mut writer) = self.writer() {\n            // don't track the writer for the whole store\n            writer.untrack();\n            let mut notify = |path: &StorePath| {\n                self.triggers_for_path_unkeyed(path.to_owned()).notify();\n            };\n            writer.patch_field_keyed(\n                new,\n                &mut notify,\n                keys.as_ref(),\n                self.key_fn,\n                |key| self.path_at_key(&path, key),\n            )\n        } else {\n            false\n        };\n\n        if structure_changed {\n            // Only notify `children` (not `this`) at the collection path, so that\n            // individual keyed items — which track `this` on all ancestor paths —\n            // are not spuriously notified when only the collection order has changed.\n            let trigger = self.get_trigger_unkeyed(path.clone());\n            trigger.children.notify();\n\n            let mut ancestor_path = path;\n            while !ancestor_path.is_empty() {\n                ancestor_path.pop();\n                let inner = self.get_trigger_unkeyed(ancestor_path.clone());\n                inner.children.notify();\n            }\n        }\n\n        self.update_keys();\n    }\n}\n\n/// Allows patching a store field with some new value.\npub trait PatchField {\n    /// Patches the field with some new value, only notifying if the value has changed.\n    ///\n    /// # Arguments\n    ///\n    /// - **new** - new value\n    /// - **path** - path to the field\n    /// - **notify** - callback to notify about update\n    /// - **keys** - a reference to the KeyMap for the store that's being patched\n    fn patch_field(\n        &mut self,\n        new: Self,\n        path: &StorePath,\n        notify: &mut dyn FnMut(&StorePath),\n        keys: Option<&KeyMap>,\n    );\n}\n\n/// Allows patching a collection in a store field with a new value, after doing a keyed diff.\n///     \n/// This takes a `key_fn` that is applied to each entry in the collection and returns a\n/// unique key. Items in the old collection and new collection with the same key are treated\n/// as the same value, and the items are patched using [`PatchField`].\n///\n/// The exact notification behavior will depend on the collection type. For example, patching\n/// a vector or slice-like type should notify on the collection itself if the order of items changes.\n/// If all the same keys are present in the same order, however, the parent collection will not\n/// be notified; only the keyed items that have changed.\npub trait PatchFieldKeyed<K>\nwhere\n    Self: Sized + KeyedAccess<K>,\n    for<'a> &'a Self: IntoIterator,\n{\n    /// Patches a collection with a new value.\n    ///\n    /// Returns `true` if the structure of the collection changed (items added, removed,\n    /// or reordered). Individual item changes are notified via the `notify` callback.\n    ///\n    /// # Arguments\n    ///\n    /// - **new** - updated values\n    /// - **notify** - callback to notify about the update\n    /// - **keys** - a reference to the KeyMap for the store that's being patched\n    /// - **key_fn** - callback returning the key from an item in the collection\n    /// - **path_at_key** - callback returning a store path for the element in the collection identified by the key\n    fn patch_field_keyed(\n        &mut self,\n        new: Self,\n        notify: &mut dyn FnMut(&StorePath),\n        keys: Option<&KeyMap>,\n        key_fn: impl Fn(<&Self as IntoIterator>::Item) -> K,\n        path_at_key: impl Fn(&K) -> Option<StorePath>,\n    ) -> bool\n    where\n        K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static;\n}\n\nmacro_rules! patch_primitives {\n    ($($ty:ty),*) => {\n        $(impl PatchField for $ty {\n            fn patch_field(\n                &mut self,\n                new: Self,\n                path: &StorePath,\n                notify: &mut dyn FnMut(&StorePath),\n                _keys: Option<&KeyMap>\n            ) {\n                if new != *self {\n                    *self = new;\n                    notify(path);\n                }\n            }\n        })*\n    };\n}\n\npatch_primitives! {\n    &str,\n    String,\n    Arc<str>,\n    Rc<str>,\n    Cow<'_, str>,\n    usize,\n    u8,\n    u16,\n    u32,\n    u64,\n    u128,\n    isize,\n    i8,\n    i16,\n    i32,\n    i64,\n    i128,\n    f32,\n    f64,\n    char,\n    bool,\n    IpAddr,\n    SocketAddr,\n    SocketAddrV4,\n    SocketAddrV6,\n    Ipv4Addr,\n    Ipv6Addr,\n    NonZeroI8,\n    NonZeroU8,\n    NonZeroI16,\n    NonZeroU16,\n    NonZeroI32,\n    NonZeroU32,\n    NonZeroI64,\n    NonZeroU64,\n    NonZeroI128,\n    NonZeroU128,\n    NonZeroIsize,\n    NonZeroUsize\n}\n\nimpl<T> PatchField for Option<T>\nwhere\n    T: PatchField,\n{\n    fn patch_field(\n        &mut self,\n        new: Self,\n        path: &StorePath,\n        notify: &mut dyn FnMut(&StorePath),\n        keys: Option<&KeyMap>,\n    ) {\n        match (self, new) {\n            (None, None) => {}\n            (old @ Some(_), None) => {\n                old.take();\n                notify(path);\n            }\n            (old @ None, new @ Some(_)) => {\n                *old = new;\n                notify(path);\n            }\n            (Some(old), Some(new)) => {\n                let mut new_path = path.to_owned();\n                new_path.push(0);\n                old.patch_field(new, &new_path, notify, keys);\n            }\n        }\n    }\n}\n\nimpl<T> PatchField for Vec<T>\nwhere\n    T: PatchField,\n{\n    fn patch_field(\n        &mut self,\n        new: Self,\n        path: &StorePath,\n        notify: &mut dyn FnMut(&StorePath),\n        keys: Option<&KeyMap>,\n    ) {\n        if self.is_empty() && new.is_empty() {\n            return;\n        }\n\n        if new.is_empty() {\n            self.clear();\n            notify(path);\n        } else if self.is_empty() {\n            self.extend(new);\n            notify(path);\n        } else {\n            let mut adds = vec![];\n            let mut removes_at_end = 0;\n            let mut new_path = path.to_owned();\n            new_path.push(0);\n            for (idx, item) in\n                new.into_iter().zip_longest(self.iter_mut()).enumerate()\n            {\n                match item {\n                    EitherOrBoth::Both(new, old) => {\n                        old.patch_field(new, &new_path, notify, keys);\n                    }\n                    EitherOrBoth::Left(new) => {\n                        adds.push(new);\n                    }\n                    EitherOrBoth::Right(_) => {\n                        removes_at_end += 1;\n                    }\n                }\n                new_path.replace_last(idx + 1);\n            }\n\n            let length_changed = removes_at_end > 0 || !adds.is_empty();\n            self.truncate(self.len() - removes_at_end);\n            self.append(&mut adds);\n\n            if length_changed {\n                notify(path);\n            }\n        }\n    }\n}\n\nimpl<K, T> PatchFieldKeyed<K> for Vec<T>\nwhere\n    T: PatchField,\n{\n    fn patch_field_keyed(\n        &mut self,\n        mut new: Self,\n        notify: &mut dyn FnMut(&StorePath),\n        keys: Option<&KeyMap>,\n        key_fn: impl Fn(<&Self as IntoIterator>::Item) -> K,\n        path_at_key: impl Fn(&K) -> Option<StorePath>,\n    ) -> bool\n    where\n        K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    {\n        let mut has_changed = false;\n\n        let mut old_keyed = HashMap::new();\n        let mut new_keyed = IndexMap::new();\n\n        // first, calculate keys and indices for all the old values\n        for (idx, item) in self.drain(0..).enumerate() {\n            let key = key_fn(&item);\n            old_keyed.insert(key, (idx, item));\n        }\n\n        // then, calculate keys and indices for all the new values\n        for (idx, item) in new.drain(0..).enumerate() {\n            let key = key_fn(&item);\n            new_keyed.insert(key, (idx, item));\n        }\n\n        // if there are any old keys not included in the new keys, the list has changed\n        for old_key in old_keyed.keys() {\n            if !new_keyed.contains_key(old_key) {\n                has_changed = true;\n            }\n        }\n\n        // iterate over the new entries, rebuilding the `new` Vec (which we emptied with `drain` above)\n        //\n        // because we're using an IndexMap, this will iterate over the values in the same order\n        // as the new Vec had them\n        //\n        // for each entry, either\n        // 1) push it directly into the `new` Vec again, or\n        // 2) take the old\n        for (key, (new_idx, new_value)) in new_keyed {\n            let old_at_key = old_keyed.remove(&key);\n\n            match old_at_key {\n                None => {\n                    // add this item into the new vec\n                    new.push(new_value);\n\n                    // not found in old map, list has changed and will trigger\n                    has_changed = true;\n                }\n                // found in old map\n                Some((old_idx, old_value)) => {\n                    // if indices are different, list has changed\n                    if old_idx != new_idx {\n                        has_changed = true;\n                    }\n\n                    // if we had an old value for this key, we're actually going to push the *old*\n                    // value into the vec, and then patch it with the new value; because we're iterating\n                    // in the new order, it will be at the `new_idx`\n                    new.push(old_value);\n                    let field_to_patch = &mut new[new_idx];\n\n                    // now we need to actually patch the old item with this key with the new item\n                    // we do this by calling patch_field(); to get the correct path, we need to get the\n                    // path to the field at this key\n\n                    // we do th\n                    if let Some(path) = path_at_key(&key) {\n                        field_to_patch\n                            .patch_field(new_value, &path, notify, keys);\n                    } else {\n                        has_changed = true;\n                    }\n                }\n            }\n        }\n\n        // update the value\n        *self = new;\n\n        has_changed\n    }\n}\n\nimpl<K, V> PatchFieldKeyed<K> for HashMap<K, V>\nwhere\n    V: PatchField,\n    K: Eq + Hash,\n{\n    fn patch_field_keyed(\n        &mut self,\n        mut new: Self,\n        notify: &mut dyn FnMut(&StorePath),\n        keys: Option<&KeyMap>,\n        key_fn: impl Fn(<&Self as IntoIterator>::Item) -> K,\n        path_at_key: impl Fn(&K) -> Option<StorePath>,\n    ) -> bool\n    where\n        K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    {\n        let mut has_changed = false;\n\n        let mut old_keyed = HashMap::new();\n        let mut new_keyed = HashMap::new();\n\n        // first, calculate keys for all the old values\n        for item in self.drain() {\n            let key = key_fn((&item.0, &item.1));\n            old_keyed.insert(key, item);\n        }\n\n        // then, calculate keys and indices for all the new values\n        for item in new.drain() {\n            let key = key_fn((&item.0, &item.1));\n            new_keyed.insert(key, item);\n        }\n\n        // if there are any old keys not included in the new keys, the map has changed\n        for old_key in old_keyed.keys() {\n            if !new_keyed.contains_key(old_key) {\n                has_changed = true;\n            }\n        }\n\n        // iterate over the new entries, rebuilding the `new` map (which we emptied with `drain` above)\n        //\n        // for each entry, either\n        // 1) push it directly into the `new` map again, or\n        // 2) take the old value and patch it\n        for (key, new_value) in new_keyed {\n            let old_at_key = old_keyed.remove(&key);\n\n            match old_at_key {\n                None => {\n                    // add this item into the new map\n                    new.insert(new_value.0, new_value.1);\n\n                    // not found in old map, list has changed and will trigger\n                    has_changed = true;\n                }\n                // found in old map\n                Some(mut old_value) => {\n                    // now we need to actually patch the old item with this key with the new item\n                    // we do this by calling patch_field(); to get the correct path, we need to get the\n                    // path to the field at this key\n                    if let Some(path) = path_at_key(&key) {\n                        old_value.1.patch_field(\n                            new_value.1,\n                            &path,\n                            notify,\n                            keys,\n                        );\n                    } else {\n                        has_changed = true;\n                    }\n\n                    // and we'll insert it into the new map\n                    new.insert(new_value.0, old_value.1);\n                }\n            }\n        }\n\n        // update the value\n        *self = new;\n\n        has_changed\n    }\n}\n\nimpl<K, V> PatchFieldKeyed<K> for BTreeMap<K, V>\nwhere\n    V: PatchField + Clone,\n    K: Eq + Ord + Clone,\n{\n    fn patch_field_keyed(\n        &mut self,\n        new: Self,\n        notify: &mut dyn FnMut(&StorePath),\n        keys: Option<&KeyMap>,\n        key_fn: impl Fn(<&Self as IntoIterator>::Item) -> K,\n        path_at_key: impl Fn(&K) -> Option<StorePath>,\n    ) -> bool\n    where\n        K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,\n    {\n        let mut has_changed = false;\n        let mut old_keyed = BTreeMap::new();\n        let mut new_keyed = BTreeMap::new();\n\n        // first, calculate keys for all the old values\n        //\n        // BTreeMap doesn't have a drain method - https://github.com/rust-lang/rust/issues/81074\n        for item in self.iter() {\n            let key = key_fn(item);\n            old_keyed.insert(key, (item.0.clone(), item.1.clone()));\n        }\n\n        // then, calculate keys and indices for all the new values\n        //\n        // BTreeMap doesn't have a drain method - https://github.com/rust-lang/rust/issues/81074\n        for item in new {\n            let key = key_fn((&item.0, &item.1));\n            new_keyed.insert(key, item);\n        }\n\n        let mut new = BTreeMap::new();\n\n        // if there are any old keys not included in the new keys, the map has changed\n        for old_key in old_keyed.keys() {\n            if !new_keyed.contains_key(old_key) {\n                has_changed = true;\n            }\n        }\n\n        // iterate over the new entries, rebuilding the `new` map (which we emptied with `drain` above)\n        //\n        // for each entry, either\n        // 1) push it directly into the `new` map again, or\n        // 2) take the old value and patch it\n        for (key, new_value) in new_keyed {\n            let old_at_key = old_keyed.remove(&key);\n\n            match old_at_key {\n                None => {\n                    // add this item into the new map\n                    new.insert(new_value.0, new_value.1);\n\n                    // not found in old map, list has changed and will trigger\n                    has_changed = true;\n                }\n                // found in old map\n                Some(mut old_value) => {\n                    // now we need to actually patch the old item with this key with the new item\n                    // we do this by calling patch_field(); to get the correct path, we need to get the\n                    // path to the field at this key\n                    if let Some(path) = path_at_key(&key) {\n                        old_value.1.patch_field(\n                            new_value.1,\n                            &path,\n                            notify,\n                            keys,\n                        );\n                    } else {\n                        has_changed = true;\n                    }\n\n                    // and we'll insert it into the new map\n                    new.insert(new_value.0, old_value.1);\n                }\n            }\n        }\n\n        // update the value\n        *self = new;\n\n        has_changed\n    }\n}\n\nmacro_rules! patch_tuple {\n\t($($ty:ident),*) => {\n\t\timpl<$($ty),*> PatchField for ($($ty,)*)\n\t\twhere\n\t\t\t$($ty: PatchField),*,\n\t\t{\n            fn patch_field(\n                &mut self,\n                new: Self,\n                path: &StorePath,\n                notify: &mut dyn FnMut(&StorePath),\n                keys: Option<&KeyMap>\n            ) {\n                let mut idx = 0;\n                let mut new_path = path.to_owned();\n                new_path.push(0);\n\n                paste::paste! {\n                    #[allow(non_snake_case)]\n                    let ($($ty,)*) = self;\n                    let ($([<new_ $ty:lower>],)*) = new;\n                    $(\n                        $ty.patch_field([<new_ $ty:lower>], &new_path, notify, keys);\n                        idx += 1;\n                        new_path.replace_last(idx);\n                    )*\n                }\n            }\n        }\n    }\n}\n\nimpl PatchField for () {\n    fn patch_field(\n        &mut self,\n        _new: Self,\n        _path: &StorePath,\n        _notify: &mut dyn FnMut(&StorePath),\n        _keys: Option<&KeyMap>,\n    ) {\n    }\n}\n\npatch_tuple!(A);\npatch_tuple!(A, B);\npatch_tuple!(A, B, C);\npatch_tuple!(A, B, C, D);\npatch_tuple!(A, B, C, D, E);\npatch_tuple!(A, B, C, D, E, F);\npatch_tuple!(A, B, C, D, E, F, G);\npatch_tuple!(A, B, C, D, E, F, G, H);\npatch_tuple!(A, B, C, D, E, F, G, H, I);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);\npatch_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);\npatch_tuple!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W\n);\npatch_tuple!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X\n);\npatch_tuple!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y\n);\npatch_tuple!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,\n    Z\n);\n"
  },
  {
    "path": "reactive_stores/src/path.rs",
    "content": "/// The path of a field within some store.\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\npub struct StorePath(Vec<StorePathSegment>);\n\nimpl IntoIterator for StorePath {\n    type Item = StorePathSegment;\n    type IntoIter = std::vec::IntoIter<StorePathSegment>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.into_iter()\n    }\n}\n\nimpl<'a> IntoIterator for &'a StorePath {\n    type Item = &'a StorePathSegment;\n    type IntoIter = std::slice::Iter<'a, StorePathSegment>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.iter()\n    }\n}\n\nimpl From<Vec<StorePathSegment>> for StorePath {\n    fn from(value: Vec<StorePathSegment>) -> Self {\n        Self(value)\n    }\n}\n\nimpl StorePath {\n    /// Creates a new path.\n    pub fn new() -> Self {\n        Self(Vec::new())\n    }\n\n    /// Creates a new path with storage capacity for `capacity` segments.\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self(Vec::with_capacity(capacity))\n    }\n\n    /// Adds a new segment to the path.\n    pub fn push(&mut self, segment: impl Into<StorePathSegment>) {\n        self.0.push(segment.into());\n    }\n\n    /// Removes a segment from the path and returns it.\n    pub fn pop(&mut self) -> Option<StorePathSegment> {\n        self.0.pop()\n    }\n\n    /// Updates the last segment in the place in place.\n    pub fn replace_last(&mut self, segment: impl Into<StorePathSegment>) {\n        if let Some(last) = self.0.last_mut() {\n            *last = segment.into();\n        }\n    }\n\n    /// Returns `true` if the path contains no elements.\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// Returns the number of elements in the path.\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n}\n\n/// One segment of a [`StorePath`].\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct StorePathSegment(pub(crate) usize);\n\nimpl From<usize> for StorePathSegment {\n    fn from(value: usize) -> Self {\n        Self(value)\n    }\n}\n\nimpl From<&usize> for StorePathSegment {\n    fn from(value: &usize) -> Self {\n        Self(*value)\n    }\n}\n\nimpl FromIterator<StorePathSegment> for StorePath {\n    fn from_iter<T: IntoIterator<Item = StorePathSegment>>(iter: T) -> Self {\n        Self(Vec::from_iter(iter))\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/serde.rs",
    "content": "use crate::Store;\nuse reactive_graph::{\n    owner::{LocalStorage, SyncStorage},\n    traits::With,\n};\nuse serde::{Deserialize, Serialize, Serializer};\n\nimpl<T: Serialize> Serialize for Store<T>\nwhere\n    Store<T>: With<Value = T>,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: Serializer,\n    {\n        self.with(|item| item.serialize(serializer))\n    }\n}\n\nimpl<'de, T: Deserialize<'de> + Send + Sync + 'static> Deserialize<'de>\n    for Store<T, SyncStorage>\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        T::deserialize(deserializer).map(|inner| Store::new(inner))\n    }\n}\n\nimpl<'de, T: Deserialize<'de> + 'static> Deserialize<'de>\n    for Store<T, LocalStorage>\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        T::deserialize(deserializer).map(|inner| Store::new_local(inner))\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/slotmap.rs",
    "content": "//! SlotMap support for keyed fields based on their map types.\nuse crate::KeyedAccess;\n\nimpl<K: slotmap::Key, V> KeyedAccess<K> for slotmap::SlotMap<K, V> {\n    type Value = V;\n    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {\n        self.get(*key).expect(\"key does not exist.\")\n    }\n    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {\n        self.get_mut(*key).expect(\"key does not exist\")\n    }\n}\nimpl<K: slotmap::Key, V> KeyedAccess<K> for slotmap::DenseSlotMap<K, V> {\n    type Value = V;\n    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {\n        self.get(*key).expect(\"key does not exist.\")\n    }\n    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {\n        self.get_mut(*key).expect(\"key does not exist\")\n    }\n}\n#[allow(deprecated)]\nimpl<K: slotmap::Key, V> KeyedAccess<K> for slotmap::HopSlotMap<K, V> {\n    type Value = V;\n    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {\n        self.get(*key).expect(\"key does not exist.\")\n    }\n    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {\n        self.get_mut(*key).expect(\"key does not exist\")\n    }\n}\nimpl<K: slotmap::Key, V> KeyedAccess<K> for slotmap::SecondaryMap<K, V> {\n    type Value = V;\n    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {\n        self.get(*key).expect(\"key does not exist.\")\n    }\n    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {\n        self.get_mut(*key).expect(\"key does not exist\")\n    }\n}\nimpl<K: slotmap::Key, V> KeyedAccess<K> for slotmap::SparseSecondaryMap<K, V> {\n    type Value = V;\n    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {\n        self.get(*key).expect(\"key does not exist.\")\n    }\n    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {\n        self.get_mut(*key).expect(\"key does not exist\")\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{self as reactive_stores, tests::tick, AtKeyed, Store};\n    use reactive_graph::{\n        effect::Effect,\n        traits::{GetUntracked, ReadUntracked, Set, Track, Write},\n    };\n    use slotmap::{DefaultKey, SlotMap};\n    use std::sync::{\n        atomic::{AtomicUsize, Ordering},\n        Arc,\n    };\n\n    #[derive(Debug, Default, Store)]\n    struct TodoSlotMap {\n        #[store(key: DefaultKey = |(k,_)| k)]\n        todos: SlotMap<DefaultKey, Todo>,\n    }\n    impl TodoSlotMap {\n        pub fn add(&mut self, label: impl ToString) -> DefaultKey {\n            self.todos.insert_with_key(|key| Todo::new(key, label))\n        }\n\n        pub fn test_data() -> (Self, Vec<DefaultKey>) {\n            let mut todos = TodoSlotMap::default();\n\n            let ids = [\"A\", \"B\", \"C\"]\n                .into_iter()\n                .map(|label| todos.add(label))\n                .collect();\n\n            (todos, ids)\n        }\n    }\n\n    #[derive(Debug, Store, Default, Clone, PartialEq, Eq)]\n    struct Todo {\n        id: DefaultKey,\n        label: String,\n    }\n\n    impl Todo {\n        pub fn new(id: DefaultKey, label: impl ToString) -> Self {\n            Self {\n                id,\n                label: label.to_string(),\n            }\n        }\n    }\n\n    #[tokio::test]\n    async fn slotmap_keyed_fields_can_be_moved() {\n        _ = any_spawner::Executor::init_tokio();\n\n        let (todos, ids) = TodoSlotMap::test_data();\n        let store = Store::new(todos);\n        assert_eq!(store.read_untracked().todos.len(), 3);\n\n        // create an effect to read from each keyed field\n        let a_count = Arc::new(AtomicUsize::new(0));\n        let b_count = Arc::new(AtomicUsize::new(0));\n        let c_count = Arc::new(AtomicUsize::new(0));\n\n        let a = AtKeyed::new(store.todos(), ids[0]);\n        let b = AtKeyed::new(store.todos(), ids[1]);\n        let c = AtKeyed::new(store.todos(), ids[2]);\n\n        Effect::new_sync({\n            let a_count = Arc::clone(&a_count);\n            move || {\n                a.track();\n                a_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let b_count = Arc::clone(&b_count);\n            move || {\n                b.track();\n                b_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n        Effect::new_sync({\n            let c_count = Arc::clone(&c_count);\n            move || {\n                c.track();\n                c_count.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 1);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // writing at a key doesn't notify siblings\n        *a.label().write() = \"Foo\".into();\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 2);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<Vec<_>>(),\n            vec![\n                Todo::new(ids[0], \"Foo\"),\n                Todo::new(ids[1], \"B\"),\n                Todo::new(ids[2], \"C\"),\n            ]\n        );\n\n        a.label().set(\"Bar\".into());\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<Vec<_>>(),\n            vec![\n                Todo::new(ids[0], \"Bar\"),\n                Todo::new(ids[1], \"B\"),\n                Todo::new(ids[2], \"C\"),\n            ]\n        );\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 3);\n        assert_eq!(b_count.load(Ordering::Relaxed), 1);\n        assert_eq!(c_count.load(Ordering::Relaxed), 1);\n\n        // we can remove a key and add a new one\n        store.todos().write().remove(ids[2]);\n        let new_id = store\n            .todos()\n            .write()\n            .insert_with_key(|key| Todo::new(key, \"New\"));\n        let after = store.todos().get_untracked();\n        assert_eq!(\n            after.values().cloned().collect::<Vec<_>>(),\n            vec![\n                Todo::new(ids[0], \"Bar\"),\n                Todo::new(ids[1], \"B\"),\n                Todo::new(new_id, \"New\")\n            ]\n        );\n        tick().await;\n        assert_eq!(a_count.load(Ordering::Relaxed), 4);\n        assert_eq!(b_count.load(Ordering::Relaxed), 2);\n        assert_eq!(c_count.load(Ordering::Relaxed), 2);\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/store_field.rs",
    "content": "use crate::{\n    path::{StorePath, StorePathSegment},\n    ArcStore, KeyMap, Store, StoreFieldTrigger,\n};\nuse or_poisoned::OrPoisoned;\nuse reactive_graph::{\n    owner::Storage,\n    signal::{\n        guards::{Plain, UntrackedWriteGuard, WriteGuard},\n        ArcTrigger,\n    },\n    traits::{Track, UntrackableGuard},\n};\nuse std::{iter, ops::Deref, sync::Arc};\n\n/// Describes a type that can be accessed as a reactive store field.\npub trait StoreField: Sized {\n    /// The value this field contains.\n    type Value;\n    /// A read guard to access this field.\n    type Reader: Deref<Target = Self::Value>;\n    /// A write guard to update this field.\n    type Writer: UntrackableGuard<Target = Self::Value>;\n\n    /// Returns the trigger that tracks access and updates for this field.\n    #[track_caller]\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger;\n\n    /// Returns the trigger that tracks access and updates for this field.\n    ///\n    /// This uses *unkeyed* paths: i.e., if any field in the path is keyed, it will\n    /// try to look up the key for the item at the index given in the path, rather than\n    /// the keyed item.\n    #[track_caller]\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger;\n\n    /// The path of this field (see [`StorePath`]).\n    #[track_caller]\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment>;\n\n    /// The path of this field (see [`StorePath`]). Uses unkeyed indices for any keyed fields.\n    #[track_caller]\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        // TODO remove default impl next time we do a breaking release\n        self.path()\n    }\n\n    /// Reactively tracks this field.\n    #[track_caller]\n    fn track_field(&self) {\n        let path = self.path().into_iter().collect();\n        let trigger = self.get_trigger(path);\n        trigger.this.track();\n        trigger.children.track();\n    }\n\n    /// Returns a read guard to access this field.\n    #[track_caller]\n    fn reader(&self) -> Option<Self::Reader>;\n\n    /// Returns a write guard to update this field.\n    #[track_caller]\n    fn writer(&self) -> Option<Self::Writer>;\n\n    /// The keys for this field, if it is a keyed field.\n    #[track_caller]\n    fn keys(&self) -> Option<KeyMap>;\n\n    /// Returns triggers for this field, and all parent fields.\n    fn triggers_for_current_path(&self) -> Vec<ArcTrigger> {\n        self.triggers_for_path(self.path().into_iter().collect())\n    }\n\n    /// Returns triggers for the field at the given path, and all parent fields\n    fn triggers_for_path(&self, path: StorePath) -> Vec<ArcTrigger> {\n        let trigger = self.get_trigger(path.clone());\n        let mut full_path = path;\n\n        // build a list of triggers, starting with the full path to this node and ending with the root\n        // this will mean that the root is the final item, and this path is first\n        let mut triggers = Vec::with_capacity(full_path.len() + 2);\n        triggers.push(trigger.this.clone());\n        triggers.push(trigger.children.clone());\n        while !full_path.is_empty() {\n            full_path.pop();\n            let inner = self.get_trigger(full_path.clone());\n            triggers.push(inner.children.clone());\n        }\n\n        // when the WriteGuard is dropped, each trigger will be notified, in order\n        // reversing the list will cause the triggers to be notified starting from the root,\n        // then to each child down to this one\n        //\n        // notifying from the root down is important for things like OptionStoreExt::map()/unwrap(),\n        // where it's really important that any effects that subscribe to .is_some() run before effects\n        // that subscribe to the inner value, so that the inner effect can be canceled if the outer switches to `None`\n        // (see https://github.com/leptos-rs/leptos/issues/3704)\n        triggers.reverse();\n\n        triggers\n    }\n\n    /// Returns triggers for the field at the given path, and all parent fields\n    fn triggers_for_path_unkeyed(&self, path: StorePath) -> Vec<ArcTrigger> {\n        // see notes on triggers_for_path() for additional comments on implementation\n\n        let trigger = self.get_trigger_unkeyed(path.clone());\n        let mut full_path = path;\n\n        let mut triggers = Vec::with_capacity(full_path.len() + 2);\n        triggers.push(trigger.this.clone());\n        triggers.push(trigger.children.clone());\n        while !full_path.is_empty() {\n            full_path.pop();\n            let inner = self.get_trigger_unkeyed(full_path.clone());\n            triggers.push(inner.children.clone());\n        }\n        triggers.reverse();\n\n        triggers\n    }\n}\n\nimpl<T> StoreField for ArcStore<T>\nwhere\n    T: 'static,\n{\n    type Value = T;\n    type Reader = Plain<T>;\n    type Writer = WriteGuard<ArcTrigger, UntrackedWriteGuard<T>>;\n\n    #[track_caller]\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        let triggers = &self.signals;\n        let trigger = triggers.write().or_poisoned().get_or_insert(path);\n        trigger\n    }\n\n    #[track_caller]\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        let caller = std::panic::Location::caller();\n        let orig_path = path.clone();\n\n        let mut path = StorePath::with_capacity(orig_path.len());\n        for segment in &orig_path {\n            let parent_is_keyed = self.keys.contains_key(&path);\n\n            if parent_is_keyed {\n                let key = self\n                    .keys\n                    .get_key_for_index(&(path.clone(), segment.0))\n                    .unwrap_or_else(|| {\n                        panic!(\n                            \"could not find key for index {:?} at {}\",\n                            &(path.clone(), segment.0),\n                            caller\n                        )\n                    });\n                path.push(key);\n            } else {\n                path.push(*segment);\n            }\n        }\n        self.get_trigger(path)\n    }\n\n    #[track_caller]\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        iter::empty()\n    }\n\n    #[track_caller]\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        iter::empty()\n    }\n\n    #[track_caller]\n    fn reader(&self) -> Option<Self::Reader> {\n        Plain::try_new(Arc::clone(&self.value))\n    }\n\n    #[track_caller]\n    fn writer(&self) -> Option<Self::Writer> {\n        let trigger = self.get_trigger(Default::default());\n        let guard = UntrackedWriteGuard::try_new(Arc::clone(&self.value))?;\n        Some(WriteGuard::new(trigger.children, guard))\n    }\n\n    #[track_caller]\n    fn keys(&self) -> Option<KeyMap> {\n        Some(self.keys.clone())\n    }\n}\n\nimpl<T, S> StoreField for Store<T, S>\nwhere\n    T: 'static,\n    S: Storage<ArcStore<T>>,\n{\n    type Value = T;\n    type Reader = Plain<T>;\n    type Writer = WriteGuard<ArcTrigger, UntrackedWriteGuard<T>>;\n\n    #[track_caller]\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner\n            .try_get_value()\n            .map(|n| n.get_trigger(path))\n            .unwrap_or_default()\n    }\n\n    #[track_caller]\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner\n            .try_get_value()\n            .map(|n| n.get_trigger_unkeyed(path))\n            .unwrap_or_default()\n    }\n\n    #[track_caller]\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .try_get_value()\n            .map(|n| n.path().into_iter().collect::<Vec<_>>())\n            .unwrap_or_default()\n    }\n\n    #[track_caller]\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .try_get_value()\n            .map(|n| n.path_unkeyed().into_iter().collect::<Vec<_>>())\n            .unwrap_or_default()\n    }\n\n    #[track_caller]\n    fn reader(&self) -> Option<Self::Reader> {\n        self.inner.try_get_value().and_then(|n| n.reader())\n    }\n\n    #[track_caller]\n    fn writer(&self) -> Option<Self::Writer> {\n        self.inner.try_get_value().and_then(|n| n.writer())\n    }\n\n    #[track_caller]\n    fn keys(&self) -> Option<KeyMap> {\n        self.inner.try_get_value().and_then(|inner| inner.keys())\n    }\n}\n"
  },
  {
    "path": "reactive_stores/src/subfield.rs",
    "content": "use crate::{\n    path::{StorePath, StorePathSegment},\n    store_field::StoreField,\n    KeyMap, StoreFieldTrigger,\n};\nuse reactive_graph::{\n    signal::{\n        guards::{Mapped, MappedMut, WriteGuard},\n        ArcTrigger,\n    },\n    traits::{\n        DefinedAt, Get as _, IsDisposed, Notify, ReadUntracked, Track,\n        UntrackableGuard, Write,\n    },\n    wrappers::read::Signal,\n};\nuse std::{iter, marker::PhantomData, ops::DerefMut, panic::Location};\n\n/// Accesses a single field of a reactive structure.\n#[derive(Debug)]\npub struct Subfield<Inner, Prev, T> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    defined_at: &'static Location<'static>,\n    path_segment: StorePathSegment,\n    inner: Inner,\n    read: fn(&Prev) -> &T,\n    write: fn(&mut Prev) -> &mut T,\n    ty: PhantomData<T>,\n}\n\nimpl<Inner, Prev, T> Clone for Subfield<Inner, Prev, T>\nwhere\n    Inner: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            path_segment: self.path_segment,\n            inner: self.inner.clone(),\n            read: self.read,\n            write: self.write,\n            ty: self.ty,\n        }\n    }\n}\n\nimpl<Inner, Prev, T> Copy for Subfield<Inner, Prev, T> where Inner: Copy {}\n\nimpl<Inner, Prev, T> Subfield<Inner, Prev, T> {\n    /// Creates an accessor for a single field of the inner structure.\n    #[track_caller]\n    pub fn new(\n        inner: Inner,\n        path_segment: StorePathSegment,\n        read: fn(&Prev) -> &T,\n        write: fn(&mut Prev) -> &mut T,\n    ) -> Self {\n        Self {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: Location::caller(),\n            inner,\n            path_segment,\n            read,\n            write,\n            ty: PhantomData,\n        }\n    }\n}\n\nimpl<Inner, Prev, T> StoreField for Subfield<Inner, Prev, T>\nwhere\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n{\n    type Value = T;\n    type Reader = Mapped<Inner::Reader, T>;\n    type Writer = MappedMut<WriteGuard<Vec<ArcTrigger>, Inner::Writer>, T>;\n\n    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .path()\n            .into_iter()\n            .chain(iter::once(self.path_segment))\n    }\n\n    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {\n        self.inner\n            .path_unkeyed()\n            .into_iter()\n            .chain(iter::once(self.path_segment))\n    }\n\n    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger(path)\n    }\n\n    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {\n        self.inner.get_trigger_unkeyed(path)\n    }\n\n    fn reader(&self) -> Option<Self::Reader> {\n        let inner = self.inner.reader()?;\n        Some(Mapped::new_with_guard(inner, self.read))\n    }\n\n    fn writer(&self) -> Option<Self::Writer> {\n        let mut parent = self.inner.writer()?;\n\n        // we will manually include all the parent and ancestor `children` triggers\n        // in triggers_for_current_path() below. we want to untrack the parent writer\n        // so that it doesn't notify on the parent's `this` trigger, which would notify our\n        // siblings too\n        parent.untrack();\n        let triggers = self.triggers_for_current_path();\n        let guard = WriteGuard::new(triggers, parent);\n        Some(MappedMut::new(guard, self.read, self.write))\n    }\n\n    #[inline(always)]\n    fn keys(&self) -> Option<KeyMap> {\n        self.inner.keys()\n    }\n\n    #[track_caller]\n    fn track_field(&self) {\n        let mut full_path = self.path().into_iter().collect::<StorePath>();\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.track();\n        trigger.children.track();\n\n        // tracks `this` for all ancestors: i.e., it will track any change that is made\n        // directly to one of its ancestors, but not a change made to a *child* of an ancestor\n        // (which would end up with every subfield tracking its own siblings, because they are\n        // children of its parent)\n        while !full_path.is_empty() {\n            full_path.pop();\n            let inner = self.get_trigger(full_path.clone());\n            inner.this.track();\n        }\n    }\n}\n\nimpl<Inner, Prev, T> DefinedAt for Subfield<Inner, Prev, T>\nwhere\n    Inner: StoreField<Value = Prev>,\n{\n    fn defined_at(&self) -> Option<&'static Location<'static>> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            Some(self.defined_at)\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            None\n        }\n    }\n}\n\nimpl<Inner, Prev, T> IsDisposed for Subfield<Inner, Prev, T>\nwhere\n    Inner: IsDisposed,\n{\n    fn is_disposed(&self) -> bool {\n        self.inner.is_disposed()\n    }\n}\n\nimpl<Inner, Prev, T> Notify for Subfield<Inner, Prev, T>\nwhere\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n{\n    #[track_caller]\n    fn notify(&self) {\n        let trigger = self.get_trigger(self.path().into_iter().collect());\n        trigger.this.notify();\n        trigger.children.notify();\n    }\n}\n\nimpl<Inner, Prev, T> Track for Subfield<Inner, Prev, T>\nwhere\n    Inner: StoreField<Value = Prev> + Track + 'static,\n    Prev: 'static,\n    T: 'static,\n{\n    #[track_caller]\n    fn track(&self) {\n        self.track_field();\n    }\n}\n\nimpl<Inner, Prev, T> ReadUntracked for Subfield<Inner, Prev, T>\nwhere\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n{\n    type Value = <Self as StoreField>::Reader;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        self.reader()\n    }\n}\n\nimpl<Inner, Prev, T> Write for Subfield<Inner, Prev, T>\nwhere\n    T: 'static,\n    Inner: StoreField<Value = Prev>,\n    Prev: 'static,\n{\n    type Value = T;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.writer()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.writer().map(|mut writer| {\n            writer.untrack();\n            writer\n        })\n    }\n}\n\nimpl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for Signal<T>\nwhere\n    Inner: StoreField<Value = Prev> + Track + Send + Sync + 'static,\n    Prev: 'static,\n    T: Send + Sync + Clone + 'static,\n{\n    fn from(subfield: Subfield<Inner, Prev, T>) -> Self {\n        Signal::derive(move || subfield.get())\n    }\n}\n"
  },
  {
    "path": "reactive_stores_macro/Cargo.toml",
    "content": "[package]\nname = \"reactive_stores_macro\"\nversion = \"0.4.1\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Stores for holding deeply-nested reactive state while maintaining fine-grained reactive tracking.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[lib]\nproc-macro = true\n\n[dependencies]\nconvert_case = { workspace = true, default-features = true }\nproc-macro-error2 = { workspace = true, default-features = true }\nproc-macro2 = { workspace = true, default-features = true }\nquote = { workspace = true, default-features = true }\nsyn = { features = [\"full\"], workspace = true, default-features = true }\n"
  },
  {
    "path": "reactive_stores_macro/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "reactive_stores_macro/README.md",
    "content": "This crate provides macro that are helpful or required when using the `reactive_stores` crate.\n"
  },
  {
    "path": "reactive_stores_macro/src/lib.rs",
    "content": "use convert_case::{Case, Casing};\nuse proc_macro2::{Span, TokenStream};\nuse proc_macro_error2::{abort, abort_call_site, proc_macro_error, OptionExt};\nuse quote::{quote, ToTokens};\nuse syn::{\n    parse::{Parse, ParseStream, Parser},\n    punctuated::Punctuated,\n    token::Comma,\n    ExprClosure, Field, Fields, GenericParam, Generics, Ident, Index, Meta,\n    Result, Token, Type, TypeParam, Variant, Visibility, WhereClause,\n};\n\n#[proc_macro_error]\n#[proc_macro_derive(Store, attributes(store))]\npub fn derive_store(input: proc_macro::TokenStream) -> proc_macro::TokenStream {\n    syn::parse_macro_input!(input as Model)\n        .into_token_stream()\n        .into()\n}\n\n#[proc_macro_error]\n#[proc_macro_derive(Patch, attributes(store, patch))]\npub fn derive_patch(input: proc_macro::TokenStream) -> proc_macro::TokenStream {\n    syn::parse_macro_input!(input as PatchModel)\n        .into_token_stream()\n        .into()\n}\n\n/// Removes all constraints from generics arguments list.\n///\n/// # Example\n///\n/// ```rust,ignore\n/// struct Data<\n///     'a,\n///     T1: ToString + PatchField,\n///     T2: PatchField,\n///     T3: 'static + PatchField,\n///     T4,\n/// >\n/// where\n///     T3: ToString,\n///     T4: ToString + PatchField,\n/// {\n///     data1: &'a T1,\n///     data2: T2,\n///     data3: T3,\n///     data4: T4,\n/// }\n/// ```\n///\n/// For the struct above the `[syn::DeriveInput::parse]` will return the instance of [syn::Generics]\n/// which will conceptually look like this\n///\n/// ```text\n/// Generics:\n///     params:\n///        [\n///           'a,\n///            T1: ToString + PatchField,\n///            T2: PatchField,\n///            T3: 'static + PatchField,\n///            T4,\n///        ]\n///      where_clause:\n///        [\n///            T3: ToString,\n///            T4: ToString + PatchField,\n///        ]\n/// ```\n///\n/// This method would return a new instance of [syn::Generics] which will conceptually look like this\n///\n/// ```text\n/// Generics:\n///     params:\n///       [\n///           'a,\n///           T1,\n///           T2,\n///           T3,\n///           T4,\n///       ]\n///      where_clause:\n///       []\n/// ```\n///\n/// This is useful when you want to use a generic arguments list for `impl` sections for type definitions.\nfn remove_constraint_from_generics(generics: &Generics) -> Generics {\n    let mut new_generics = generics.clone();\n\n    // remove contraints directly placed in the generic arguments list\n    //\n    // For generics for `struct A<T: MyTrait>` the `T: MyTrait` becomes `T`\n    for param in new_generics.params.iter_mut() {\n        match param {\n            GenericParam::Lifetime(lifetime) => {\n                lifetime.bounds.clear(); // remove bounds\n                lifetime.colon_token = None;\n            }\n            GenericParam::Type(type_param) => {\n                type_param.bounds.clear(); // remove bounds\n                type_param.colon_token = None;\n                type_param.eq_token = None;\n                type_param.default = None;\n            }\n            GenericParam::Const(const_param) => {\n                // replaces const generic with type param without bounds which is basically an `ident` token\n                *param = GenericParam::Type(TypeParam {\n                    attrs: const_param.attrs.clone(),\n                    ident: const_param.ident.clone(),\n                    colon_token: None,\n                    bounds: Punctuated::new(),\n                    eq_token: None,\n                    default: None,\n                });\n            }\n        }\n    }\n\n    new_generics.where_clause = None; // remove where clause\n\n    new_generics\n}\n\nstruct Model {\n    vis: Visibility,\n    name: Ident,\n    generics: Generics,\n    ty: ModelTy,\n}\n\nenum ModelTy {\n    Struct { fields: Vec<Field> },\n    Enum { variants: Vec<Variant> },\n}\n\nimpl Parse for Model {\n    fn parse(input: ParseStream) -> Result<Self> {\n        let input = syn::DeriveInput::parse(input)?;\n\n        let ty = match input.data {\n            syn::Data::Struct(s) => {\n                let fields = match s.fields {\n                    syn::Fields::Unit => {\n                        abort!(s.semi_token, \"unit structs are not supported\");\n                    }\n                    syn::Fields::Named(fields) => {\n                        fields.named.into_iter().collect::<Vec<_>>()\n                    }\n                    syn::Fields::Unnamed(fields) => {\n                        fields.unnamed.into_iter().collect::<Vec<_>>()\n                    }\n                };\n\n                ModelTy::Struct { fields }\n            }\n            syn::Data::Enum(e) => ModelTy::Enum {\n                variants: e.variants.into_iter().collect(),\n            },\n            _ => {\n                abort_call_site!(\n                    \"only structs and enums can be used with `Store`\"\n                );\n            }\n        };\n\n        Ok(Self {\n            vis: input.vis,\n            generics: input.generics,\n            name: input.ident,\n            ty,\n        })\n    }\n}\n\n#[derive(Clone)]\nenum SubfieldMode {\n    Keyed(Box<ExprClosure>, Box<Type>),\n    Skip,\n}\n\nimpl Parse for SubfieldMode {\n    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {\n        let mode: Ident = input.parse()?;\n        if mode == \"key\" {\n            let _col: Token![:] = input.parse()?;\n            let ty: Type = input.parse()?;\n            let _eq: Token![=] = input.parse()?;\n            let closure: ExprClosure = input.parse()?;\n            Ok(SubfieldMode::Keyed(Box::new(closure), Box::new(ty)))\n        } else if mode == \"skip\" {\n            Ok(SubfieldMode::Skip)\n        } else {\n            Err(input.error(\"expected `key: <Type> = <closure>`\"))\n        }\n    }\n}\n\nimpl ToTokens for Model {\n    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {\n        let library_path = quote! { reactive_stores };\n        let Model {\n            vis,\n            name,\n            generics,\n            ty,\n        } = &self;\n        let any_store_field = Ident::new(\"AnyStoreField\", Span::call_site());\n        let trait_name = Ident::new(&format!(\"{name}StoreFields\"), name.span());\n        let clear_generics = remove_constraint_from_generics(generics);\n        let params = &generics.params;\n        let clear_params = &clear_generics.params;\n        let generics_with_orig = quote! { <#any_store_field, #params> };\n        let where_with_orig = {\n            generics\n                .where_clause\n                .as_ref()\n                .map(|w| {\n                    let WhereClause {\n                        where_token,\n                        predicates,\n                    } = &w;\n                    quote! {\n                        #where_token\n                            #any_store_field: #library_path::StoreField<Value = #name < #clear_params > >,\n                            #predicates\n                    }\n                })\n                .unwrap_or_else(|| quote! { where #any_store_field: #library_path::StoreField<Value = #name < #clear_params > > })\n        };\n\n        // define an extension trait that matches this struct\n        // and implement that trait for all StoreFields\n        let (trait_fields, read_fields): (Vec<_>, Vec<_>) = ty.to_field_data(\n            &library_path,\n            generics,\n            &clear_generics,\n            &any_store_field,\n            name,\n        );\n\n        // read access\n        tokens.extend(quote! {\n            #vis trait #trait_name <AnyStoreField, #params>\n            #where_with_orig\n            {\n                #(#trait_fields)*\n            }\n\n            impl #generics_with_orig #trait_name <AnyStoreField, #clear_params> for AnyStoreField\n            #where_with_orig\n            {\n               #(#read_fields)*\n            }\n        });\n    }\n}\n\nimpl ModelTy {\n    fn to_field_data(\n        &self,\n        library_path: &TokenStream,\n        generics: &Generics,\n        clear_generics: &Generics,\n        any_store_field: &Ident,\n        name: &Ident,\n    ) -> (Vec<TokenStream>, Vec<TokenStream>) {\n        match self {\n            ModelTy::Struct { fields } => fields\n                .iter()\n                .enumerate()\n                .map(|(idx, field)| {\n                    let Field {\n                        ident, ty, attrs, ..\n                    } = &field;\n                    let modes = attrs\n                        .iter()\n                        .find_map(|attr| {\n                            attr.meta.path().is_ident(\"store\").then(|| {\n                                match &attr.meta {\n                                    Meta::List(list) => {\n                                        match Punctuated::<\n                                                SubfieldMode,\n                                                Comma,\n                                            >::parse_terminated\n                                                .parse2(list.tokens.clone())\n                                            {\n                                                Ok(modes) => Some(\n                                                    modes\n                                                        .iter()\n                                                        .cloned()\n                                                        .collect::<Vec<_>>(),\n                                                ),\n                                                Err(e) => abort!(list, e),\n                                            }\n                                    }\n                                    _ => None,\n                                }\n                            })\n                        })\n                        .flatten();\n\n                    (\n                        field_to_tokens(\n                            idx,\n                            false,\n                            modes.as_deref(),\n                            library_path,\n                            ident.as_ref(),\n                            generics,\n                            clear_generics,\n                            any_store_field,\n                            name,\n                            ty,\n                        ),\n                        field_to_tokens(\n                            idx,\n                            true,\n                            modes.as_deref(),\n                            library_path,\n                            ident.as_ref(),\n                            generics,\n                            clear_generics,\n                            any_store_field,\n                            name,\n                            ty,\n                        ),\n                    )\n                })\n                .unzip(),\n            ModelTy::Enum { variants } => variants\n                .iter()\n                .map(|variant| {\n                    let Variant { ident, fields, .. } = variant;\n\n                    (\n                        variant_to_tokens(\n                            false,\n                            library_path,\n                            ident,\n                            generics,\n                            clear_generics,\n                            any_store_field,\n                            name,\n                            fields,\n                        ),\n                        variant_to_tokens(\n                            true,\n                            library_path,\n                            ident,\n                            generics,\n                            clear_generics,\n                            any_store_field,\n                            name,\n                            fields,\n                        ),\n                    )\n                })\n                .unzip(),\n        }\n    }\n}\n\n#[allow(clippy::too_many_arguments)]\nfn field_to_tokens(\n    idx: usize,\n    include_body: bool,\n    modes: Option<&[SubfieldMode]>,\n    library_path: &proc_macro2::TokenStream,\n    orig_ident: Option<&Ident>,\n    _generics: &Generics,\n    clear_generics: &Generics,\n    any_store_field: &Ident,\n    name: &Ident,\n    ty: &Type,\n) -> proc_macro2::TokenStream {\n    let ident = if orig_ident.is_none() {\n        let idx = Ident::new(&format!(\"field{idx}\"), Span::call_site());\n        quote! { #idx }\n    } else {\n        quote! { #orig_ident }\n    };\n    let locator = if orig_ident.is_none() {\n        let idx = Index::from(idx);\n        quote! { #idx }\n    } else {\n        quote! { #ident }\n    };\n\n    if let Some(modes) = modes {\n        if modes.len() == 1 {\n            let mode = &modes[0];\n            match mode {\n                SubfieldMode::Keyed(keyed_by, key_ty) => {\n                    let signature = quote! {\n                        #[track_caller]\n                        fn #ident(self) ->  #library_path::KeyedSubfield<#any_store_field, #name #clear_generics, #key_ty, #ty>\n                    };\n                    return if include_body {\n                        quote! {\n                            #signature {\n                                #library_path::KeyedSubfield::new(\n                                    self,\n                                    #idx.into(),\n                                    #keyed_by,\n                                    |prev| &prev.#locator,\n                                    |prev| &mut prev.#locator,\n                                )\n                            }\n                        }\n                    } else {\n                        quote! { #signature; }\n                    };\n                }\n                SubfieldMode::Skip => return quote! {},\n            }\n        } else {\n            abort!(\n                orig_ident\n                    .map(|ident| ident.span())\n                    .unwrap_or_else(Span::call_site),\n                \"multiple modes not currently supported\"\n            );\n        }\n    }\n\n    // default subfield\n    if include_body {\n        quote! {\n            fn #ident(self) ->  #library_path::Subfield<#any_store_field, #name #clear_generics, #ty> {\n                #library_path::Subfield::new(\n                    self,\n                    #idx.into(),\n                    |prev| &prev.#locator,\n                    |prev| &mut prev.#locator,\n                )\n            }\n        }\n    } else {\n        quote! {\n            fn #ident(self) ->  #library_path::Subfield<#any_store_field, #name #clear_generics, #ty>;\n        }\n    }\n}\n\n#[allow(clippy::too_many_arguments)]\nfn variant_to_tokens(\n    include_body: bool,\n    library_path: &proc_macro2::TokenStream,\n    ident: &Ident,\n    _generics: &Generics,\n    clear_generics: &Generics,\n    any_store_field: &Ident,\n    name: &Ident,\n    fields: &Fields,\n) -> proc_macro2::TokenStream {\n    // the method name will always be the snake_cased ident\n    let orig_ident = &ident;\n    let ident =\n        Ident::new(&ident.to_string().to_case(Case::Snake), ident.span());\n\n    match fields {\n        // For unit enum fields, we will just return a `bool` subfield, which is\n        // true when this field matches\n        Fields::Unit => {\n            // default subfield\n            if include_body {\n                quote! {\n                    fn #ident(self) -> bool {\n                        match #library_path::StoreField::reader(&self) {\n                            Some(reader) => {\n                                #library_path::StoreField::track_field(&self);\n                                matches!(&*reader, #name::#orig_ident)\n                            },\n                            None => false\n                        }\n                    }\n                }\n            } else {\n                quote! {\n                    fn #ident(self) -> bool;\n                }\n            }\n        }\n        // If an enum branch has named fields, we create N + 1 methods:\n        // 1 `bool` subfield, which is true when this field matches\n        // N `Option<T>` subfields for each of the named fields\n        Fields::Named(fields) => {\n            let mut tokens = if include_body {\n                quote! {\n                    fn #ident(self) -> bool {\n                        match #library_path::StoreField::reader(&self) {\n                            Some(reader) => {\n                                #library_path::StoreField::track_field(&self);\n                                matches!(&*reader, #name::#orig_ident { .. })\n                            },\n                            None => false\n                        }\n                    }\n                }\n            } else {\n                quote! {\n                    fn #ident(self) -> bool;\n                }\n            };\n\n            tokens.extend(fields\n                .named\n                .iter()\n                .map(|field| {\n                    let field_ident = field.ident.as_ref().unwrap();\n                    let field_ty = &field.ty;\n                    let combined_ident = Ident::new(\n                        &format!(\"{ident}_{field_ident}\"),\n                        field_ident.span(),\n                    );\n\n                    // default subfield\n                    if include_body {\n                        quote! {\n                            fn #combined_ident(self) -> Option<#library_path::Subfield<#any_store_field, #name #clear_generics, #field_ty>> {\n                                #library_path::StoreField::track_field(&self);\n                                let reader = #library_path::StoreField::reader(&self);\n                                let matches = reader\n                                    .map(|reader| matches!(&*reader, #name::#orig_ident { .. }))\n                                    .unwrap_or(false);\n                                if matches {\n                                    Some(#library_path::Subfield::new(\n                                        self,\n                                        0.into(),\n                                        |prev| {\n                                            match prev {\n                                                #name::#orig_ident { #field_ident, .. } => Some(#field_ident),\n                                                _ => None,\n                                            }\n                                            .expect(\"accessed an enum field that is no longer matched\")\n                                        },\n                                        |prev| {\n                                            match prev {\n                                                #name::#orig_ident { #field_ident, .. } => Some(#field_ident),\n                                                _ => None,\n                                            }\n                                            .expect(\"accessed an enum field that is no longer matched\")\n                                        },\n                                    ))\n                                } else {\n                                    None\n                                }\n                            }\n                        }\n                    } else {\n                        quote! {\n                            fn #combined_ident(self) -> Option<#library_path::Subfield<#any_store_field, #name #clear_generics, #field_ty>>;\n                        }\n                    }\n                }));\n\n            tokens\n        }\n        // If an enum branch has unnamed fields, we create N + 1 methods:\n        // 1 `bool` subfield, which is true when this field matches\n        // N `Option<T>` subfields for each of the unnamed fields\n        Fields::Unnamed(fields) => {\n            let mut tokens = if include_body {\n                quote! {\n                    fn #ident(self) -> bool {\n                        match #library_path::StoreField::reader(&self) {\n                            Some(reader) => {\n                                #library_path::StoreField::track_field(&self);\n                                matches!(&*reader, #name::#orig_ident { .. })\n                            },\n                            None => false\n                        }\n                    }\n                }\n            } else {\n                quote! {\n                    fn #ident(self) -> bool;\n                }\n            };\n\n            let number_of_fields = fields.unnamed.len();\n\n            tokens.extend(fields\n                .unnamed\n                .iter()\n                .enumerate()\n                .map(|(idx, field)| {\n                    let field_ident = idx;\n                    let field_ty = &field.ty;\n                    let combined_ident = Ident::new(\n                        &format!(\"{ident}_{field_ident}\"),\n                        ident.span(),\n                    );\n\n                    let ignore_before = (0..idx).map(|_| quote! { _, });\n                    let ignore_before2 = ignore_before.clone();\n                    let ignore_after = (idx..number_of_fields.saturating_sub(1)).map(|_| quote !{_, });\n                    let ignore_after2 = ignore_after.clone();\n\n                    // default subfield\n                    if include_body {\n                        quote! {\n                            fn #combined_ident(self) -> Option<#library_path::Subfield<#any_store_field, #name #clear_generics, #field_ty>> {\n                                #library_path::StoreField::track_field(&self);\n                                let reader = #library_path::StoreField::reader(&self);\n                                let matches = reader\n                                    .map(|reader| matches!(&*reader, #name::#orig_ident(..)))\n                                    .unwrap_or(false);\n                                if matches {\n                                    Some(#library_path::Subfield::new(\n                                        self,\n                                        0.into(),\n                                        |prev| {\n                                            match prev {\n                                                #name::#orig_ident(#(#ignore_before)* this, #(#ignore_after)*) => Some(this),\n                                                _ => None,\n                                            }\n                                            .expect(\"accessed an enum field that is no longer matched\")\n                                        },\n                                        |prev| {\n                                            match prev {\n                                                #name::#orig_ident(#(#ignore_before2)* this, #(#ignore_after2)*) => Some(this),\n                                                _ => None,\n                                            }\n                                            .expect(\"accessed an enum field that is no longer matched\")\n                                        },\n                                    ))\n                                } else {\n                                    None\n                                }\n                            }\n                        }\n                    } else {\n                        quote! {\n                            fn #combined_ident(self) -> Option<#library_path::Subfield<#any_store_field, #name #clear_generics, #field_ty>>;\n                        }\n                    }\n                }));\n\n            tokens\n        }\n    }\n}\n\nstruct PatchModel {\n    pub name: Ident,\n    pub generics: Generics,\n    pub ty: PatchModelTy,\n}\n\nenum PatchModelTy {\n    Struct {\n        fields: Vec<Field>,\n    },\n    #[allow(dead_code)]\n    Enum {\n        variants: Vec<Variant>,\n    },\n}\n\nimpl Parse for PatchModel {\n    fn parse(input: ParseStream) -> Result<Self> {\n        let input = syn::DeriveInput::parse(input)?;\n\n        let ty = match input.data {\n            syn::Data::Struct(s) => {\n                let fields = match s.fields {\n                    syn::Fields::Unit => {\n                        abort!(s.semi_token, \"unit structs are not supported\");\n                    }\n                    syn::Fields::Named(fields) => {\n                        fields.named.into_iter().collect::<Vec<_>>()\n                    }\n                    syn::Fields::Unnamed(fields) => {\n                        fields.unnamed.into_iter().collect::<Vec<_>>()\n                    }\n                };\n\n                PatchModelTy::Struct { fields }\n            }\n            syn::Data::Enum(_e) => {\n                abort_call_site!(\"only structs can be used with `Patch`\");\n\n                // TODO: support enums later on\n                // PatchModelTy::Enum {\n                //     variants: e.variants.into_iter().collect(),\n                // }\n            }\n            _ => {\n                abort_call_site!(\n                    \"only structs and enums can be used with `Store`\"\n                );\n            }\n        };\n\n        Ok(Self {\n            name: input.ident,\n            generics: input.generics,\n            ty,\n        })\n    }\n}\n\nimpl ToTokens for PatchModel {\n    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {\n        let library_path = quote! { reactive_stores };\n        let PatchModel { name, generics, ty } = &self;\n\n        let fields = match ty {\n            PatchModelTy::Struct { fields } => {\n                fields.iter().enumerate().map(|(idx, field)| {\n                    let Field {\n                        attrs, ident, ..\n                    } = &field;\n                    let locator = match &ident {\n                        Some(ident) => Either::Left(ident),\n                        None => Either::Right(Index::from(idx)),\n                    };\n                    let closure = attrs\n                        .iter()\n                        .find_map(|attr| {\n                            attr.meta.path().is_ident(\"patch\").then(\n                                || match &attr.meta {\n                                    Meta::List(list) => {\n                                        match Punctuated::<\n                                                ExprClosure,\n                                                Comma,\n                                            >::parse_terminated\n                                                .parse2(list.tokens.clone())\n                                            {\n                                                Ok(closures) => {\n                                                    let closure = closures.iter().next().cloned().expect_or_abort(\"should have ONE closure\");\n                                                    if closure.inputs.len() != 2 {\n                                                        abort!(closure.inputs, \"patch closure should have TWO params as in #[patch(|this, new| ...)]\");\n                                                    }\n                                                    closure\n                                                },\n                                                Err(e) => abort!(list, e),\n                                            }\n                                    }\n                                    _ => abort!(attr.meta, \"needs to be as `#[patch(|this, new| ...)]`\"),\n                                },\n                            )\n                        });\n                    let keyed = attrs\n                        .iter()\n                        .find_map(|attr| {\n                            attr.meta.path().is_ident(\"store\").then(|| {\n                                match &attr.meta {\n                                    Meta::List(list) => {\n                                        let subfields = match Punctuated::<\n                                                SubfieldMode,\n                                                Comma,\n                                            >::parse_terminated\n                                                .parse2(list.tokens.clone())\n                                            {\n                                                Ok(modes) => Some(\n                                                    modes\n                                                        .iter()\n                                                        .cloned()\n                                                        .collect::<Vec<_>>(),\n                                                ),\n                                                Err(e) => abort!(list, e),\n                                            }.unwrap_or_default();\n                                            subfields.into_iter().find_map(|subfield| match subfield {\n                                                SubfieldMode::Keyed(closure, _ty) => Some(closure),\n                                                SubfieldMode::Skip => None,\n                                            })\n                                    }\n                                    _ => None,\n                                }\n                            })\n                        }).flatten();\n\n                    if let Some(closure) = closure {\n                        let params = closure.inputs;\n                        let body = closure.body;\n                        quote! {\n                            if new.#locator != self.#locator {\n                                _ = {\n                                    let (#params) = (&mut self.#locator, new.#locator);\n                                    #body\n                                };\n                                notify(&new_path);\n                            }\n                            new_path.replace_last(#idx + 1);\n                        }\n                    } else if let Some(closure) = keyed {\n                        quote! {\n                            #library_path::PatchFieldKeyed::patch_field_keyed(\n                                &mut self.#locator,\n                                new.#locator,\n                                notify,\n                                keys,\n                                #closure,\n                                |key| {\n                                    let keys = keys.as_ref()?;\n                                    let segment = keys\n                                        .with_field_keys(\n                                            path.clone(),\n                                            |keys| (keys.get(key), vec![]),\n                                            || vec![],\n                                        )\n                                        .flatten()\n                                        .map(|(_, idx)| idx)?;\n                                    let mut path = path.clone();\n                                    path.push(segment);\n                                    Some(path)\n                                }\n                            );\n                            new_path.replace_last(#idx + 1);\n                        }\n                    } else {\n                        quote! {\n                            #library_path::PatchField::patch_field(\n                                &mut self.#locator,\n                                new.#locator,\n                                &new_path,\n                                notify,\n                                keys\n                            );\n                            new_path.replace_last(#idx + 1);\n                        }\n                    }\n                }).collect::<Vec<_>>()\n            }\n            PatchModelTy::Enum { variants: _ } => {\n                unreachable!(\"not implemented currently\")\n            }\n        };\n\n        let clear_generics = remove_constraint_from_generics(generics);\n        let params = clear_generics.params;\n        let where_clause = &generics.where_clause;\n\n        // read access\n        tokens.extend(quote! {\n            impl #generics #library_path::PatchField for #name <#params>\n               #where_clause\n            {\n                fn patch_field(\n                    &mut self,\n                    new: Self,\n                    path: &#library_path::StorePath,\n                    notify: &mut dyn FnMut(&#library_path::StorePath),\n                    keys: Option<&#library_path::KeyMap>,\n                ) {\n                    let mut new_path = path.clone();\n                    new_path.push(0);\n                    #(#fields)*\n                }\n            }\n        });\n    }\n}\n\nenum Either<A, B> {\n    Left(A),\n    Right(B),\n}\n\nimpl<A: ToTokens, B: ToTokens> ToTokens for Either<A, B> {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        match self {\n            Either::Left(a) => a.to_tokens(tokens),\n            Either::Right(b) => b.to_tokens(tokens),\n        }\n    }\n}\n"
  },
  {
    "path": "router/Cargo.toml",
    "content": "[package]\nname = \"leptos_router\"\nversion = \"0.8.12\"\nauthors = [\"Greg Johnston\", \"Ben Wishovich\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Router for the Leptos web framework.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nleptos = { workspace = true }\nleptos_router_macro = { workspace = true }\nany_spawner = { workspace = true }\neither_of = { workspace = true }\nor_poisoned = { workspace = true }\nreactive_graph = { workspace = true }\ntachys = { workspace = true, features = [\"reactive_graph\"] }\nfutures = { workspace = true, default-features = true }\nurl = { workspace = true, default-features = true }\njs-sys = { workspace = true, default-features = true }\nwasm-bindgen = { workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\nsend_wrapper = { workspace = true, default-features = true }\nthiserror = { workspace = true, default-features = true }\npercent-encoding = { optional = true, workspace = true, default-features = true }\ngloo-net = { workspace = true, default-features = true }\n\n[dependencies.web-sys]\nfeatures = [\n  \"Document\",\n  \"Window\",\n  \"console\",\n  # History/Routing\n  \"History\",\n  \"HtmlAnchorElement\",\n  \"Location\",\n  \"MouseEvent\",\n  \"Url\",\n  # Form\n  \"FormData\",\n  \"HtmlButtonElement\",\n  \"HtmlFormElement\",\n  \"HtmlInputElement\",\n  \"SubmitEvent\",\n  \"Url\",\n  \"UrlSearchParams\",\n  # Fetching in Hydrate Mode\n  \"Headers\",\n  \"Request\",\n  \"RequestInit\",\n  \"RequestMode\",\n  \"Response\",\n]\nworkspace = true\ndefault-features = true\n\n[build-dependencies]\nrustc_version = { workspace = true, default-features = true }\n\n[features]\ntracing = [\"dep:tracing\"]\nssr = [\"dep:percent-encoding\"]\nnightly = []\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\"]\nmax_combination_size = 2\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = [\n  'cfg(leptos_debuginfo)',\n  'cfg(erase_components)',\n  'cfg(rustc_nightly)',\n] }\n"
  },
  {
    "path": "router/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "router/build.rs",
    "content": "use rustc_version::{version_meta, Channel};\n\nfn main() {\n    // Set cfg flags depending on release channel\n    if matches!(version_meta().unwrap().channel, Channel::Nightly) {\n        println!(\"cargo:rustc-cfg=rustc_nightly\");\n    }\n}\n"
  },
  {
    "path": "router/src/components.rs",
    "content": "pub use super::{form::*, link::*};\n#[cfg(feature = \"ssr\")]\nuse crate::location::RequestUrl;\npub use crate::nested_router::Outlet;\nuse crate::{\n    flat_router::FlatRoutesView,\n    hooks::{use_matched, use_navigate},\n    location::{\n        BrowserUrl, Location, LocationChange, LocationProvider, State, Url,\n    },\n    navigate::NavigateOptions,\n    nested_router::NestedRoutesView,\n    resolve_path::resolve_path,\n    ChooseView, MatchNestedRoutes, NestedRoute, PossibleRouteMatch, RouteDefs,\n    SsrMode,\n};\nuse either_of::EitherOf3;\nuse leptos::{children, prelude::*};\nuse reactive_graph::{\n    owner::{provide_context, use_context, Owner},\n    signal::ArcRwSignal,\n    traits::{GetUntracked, ReadUntracked, Set},\n    wrappers::write::SignalSetter,\n};\nuse std::{\n    borrow::Cow,\n    fmt::{Debug, Display},\n    mem,\n    sync::Arc,\n    time::Duration,\n};\n\n/// A wrapper that allows passing route definitions as children to a component like [`Routes`],\n/// [`FlatRoutes`], [`ParentRoute`], or [`ProtectedParentRoute`].\n#[derive(Clone, Debug)]\npub struct RouteChildren<Children>(Children);\n\nimpl<Children> RouteChildren<Children> {\n    /// Extracts the inner route definition.\n    pub fn into_inner(self) -> Children {\n        self.0\n    }\n}\n\nimpl<F, Children> ToChildren<F> for RouteChildren<Children>\nwhere\n    F: FnOnce() -> Children,\n{\n    fn to_children(f: F) -> Self {\n        RouteChildren(f())\n    }\n}\n\n#[component(transparent)]\npub fn Router<Chil>(\n    /// The base URL for the router. Defaults to `\"\"`.\n    #[prop(optional, into)]\n    base: Option<Cow<'static, str>>,\n    /// A signal that will be set while the navigation process is underway.\n    #[prop(optional, into)]\n    set_is_routing: Option<SignalSetter<bool>>,\n    // TODO trailing slashes\n    ///// How trailing slashes should be handled in [`Route`] paths.\n    //#[prop(optional)]\n    //trailing_slash: TrailingSlash,\n    /// The `<Router/>` should usually wrap your whole page. It can contain\n    /// any elements, and should include a [`Routes`] component somewhere\n    /// to define and display [`Route`]s.\n    children: TypedChildren<Chil>,\n) -> impl IntoView\nwhere\n    Chil: IntoView,\n{\n    #[cfg(feature = \"ssr\")]\n    let (location_provider, current_url, redirect_hook) = {\n        let req = use_context::<RequestUrl>().expect(\"no RequestUrl provided\");\n        let parsed = req.parse().expect(\"could not parse RequestUrl\");\n        let current_url = ArcRwSignal::new(parsed);\n\n        (None, current_url, Box::new(move |_: &str| {}))\n    };\n\n    #[cfg(not(feature = \"ssr\"))]\n    let (location_provider, current_url, redirect_hook) = {\n        let owner = Owner::current();\n        let location =\n            BrowserUrl::new().expect(\"could not access browser navigation\"); // TODO options here\n        location.init(base.clone());\n        provide_context(location.clone());\n        let current_url = location.as_url().clone();\n\n        let redirect_hook = Box::new(move |loc: &str| {\n            if let Some(owner) = &owner {\n                owner.with(|| BrowserUrl::redirect(loc));\n            }\n        });\n\n        (Some(location), current_url, redirect_hook)\n    };\n    // provide router context\n    let state = ArcRwSignal::new(State::new(None));\n    let location = Location::new(current_url.read_only(), state.read_only());\n\n    // set server function redirect hook\n    _ = server_fn::redirect::set_redirect_hook(redirect_hook);\n\n    provide_context(RouterContext {\n        base,\n        current_url,\n        location,\n        state,\n        set_is_routing,\n        query_mutations: Default::default(),\n        location_provider,\n    });\n\n    let children = children.into_inner();\n    children()\n}\n\n#[derive(Clone)]\npub(crate) struct RouterContext {\n    pub base: Option<Cow<'static, str>>,\n    pub current_url: ArcRwSignal<Url>,\n    pub location: Location,\n    pub state: ArcRwSignal<State>,\n    pub set_is_routing: Option<SignalSetter<bool>>,\n    pub query_mutations:\n        ArcStoredValue<Vec<(Oco<'static, str>, Option<String>)>>,\n    pub location_provider: Option<BrowserUrl>,\n}\n\nimpl RouterContext {\n    pub fn navigate(&self, path: &str, options: NavigateOptions) {\n        let current = self.current_url.read_untracked();\n        let resolved_to = if options.resolve {\n            resolve_path(\n                self.base.as_deref().unwrap_or_default(),\n                path,\n                // TODO this should be relative to the current *Route*, I think...\n                Some(current.path()),\n            )\n        } else {\n            resolve_path(\"\", path, None)\n        };\n\n        let mut url = match BrowserUrl::parse(&resolved_to) {\n            Ok(url) => url,\n            Err(e) => {\n                leptos::logging::error!(\"Error parsing URL: {e:?}\");\n                return;\n            }\n        };\n        let query_mutations =\n            mem::take(&mut *self.query_mutations.write_value());\n        if !query_mutations.is_empty() {\n            for (key, value) in query_mutations {\n                if let Some(value) = value {\n                    url.search_params_mut().replace(key, value);\n                } else {\n                    url.search_params_mut().remove(&key);\n                }\n            }\n            *url.search_mut() = url\n                .search_params()\n                .to_query_string()\n                .trim_start_matches('?')\n                .into()\n        }\n\n        if url.origin() != current.origin() {\n            window().location().set_href(path).unwrap();\n            return;\n        }\n\n        // update state signal, if necessary\n        if options.state != self.state.get_untracked() {\n            self.state.set(options.state.clone());\n        }\n\n        // update URL signal, if necessary\n        let value = url.to_full_path();\n        if current != url {\n            drop(current);\n            self.current_url.set(url);\n        }\n\n        if let Some(location_provider) = &self.location_provider {\n            location_provider.complete_navigation(&LocationChange {\n                value,\n                replace: options.replace,\n                scroll: options.scroll,\n                state: options.state,\n            });\n        }\n    }\n\n    pub fn resolve_path<'a>(\n        &'a self,\n        path: &'a str,\n        from: Option<&'a str>,\n    ) -> Cow<'a, str> {\n        let base = self.base.as_deref().unwrap_or_default();\n        resolve_path(base, path, from)\n    }\n}\n\nimpl Debug for RouterContext {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"RouterContext\")\n            .field(\"base\", &self.base)\n            .field(\"current_url\", &self.current_url)\n            .field(\"location\", &self.location)\n            .finish_non_exhaustive()\n    }\n}\n\n#[component(transparent)]\npub fn Routes<Defs, FallbackFn, Fallback>(\n    /// A function that returns the view that should be shown if no route is matched.\n    fallback: FallbackFn,\n    /// Whether to use the View Transition API during navigation.\n    #[prop(optional)]\n    transition: bool,\n    /// The route definitions. This should consist of one or more [`ParentRoute`] or [`Route`]\n    /// components.\n    children: RouteChildren<Defs>,\n) -> impl IntoView\nwhere\n    Defs: MatchNestedRoutes + Clone + Send + 'static,\n    FallbackFn: FnOnce() -> Fallback + Clone + Send + 'static,\n    Fallback: IntoView + 'static,\n{\n    let location = use_context::<BrowserUrl>();\n    let RouterContext {\n        current_url,\n        base,\n        set_is_routing,\n        ..\n    } = use_context()\n        .expect(\"<Routes> should be used inside a <Router> component\");\n    let base = base.map(|base| {\n        let mut base = Oco::from(base);\n        base.upgrade_inplace();\n        base\n    });\n    let routes = RouteDefs::new_with_base(\n        children.into_inner(),\n        base.clone().unwrap_or_default(),\n    );\n    let outer_owner =\n        Owner::current().expect(\"creating Routes, but no Owner was found\");\n    move || {\n        current_url.track();\n        outer_owner.with(|| {\n            current_url.read_untracked().provide_server_action_error()\n        });\n        NestedRoutesView {\n            location: location.clone(),\n            routes: routes.clone(),\n            outer_owner: outer_owner.clone(),\n            current_url: current_url.clone(),\n            base: base.clone(),\n            fallback: fallback.clone(),\n            set_is_routing,\n            transition,\n        }\n    }\n}\n\n#[component(transparent)]\npub fn FlatRoutes<Defs, FallbackFn, Fallback>(\n    /// A function that returns the view that should be shown if no route is matched.\n    fallback: FallbackFn,\n    /// Whether to use the View Transition API during navigation.\n    #[prop(optional)]\n    transition: bool,\n    /// The route definitions. This should consist of one or more [`ParentRoute`] or [`Route`]\n    /// components.\n    children: RouteChildren<Defs>,\n) -> impl IntoView\nwhere\n    Defs: MatchNestedRoutes + Clone + Send + 'static,\n    FallbackFn: FnOnce() -> Fallback + Clone + Send + 'static,\n    Fallback: IntoView + 'static,\n{\n    let location = use_context::<BrowserUrl>();\n    let RouterContext {\n        current_url,\n        base,\n        set_is_routing,\n        ..\n    } = use_context()\n        .expect(\"<FlatRoutes> should be used inside a <Router> component\");\n\n    // TODO base\n    #[allow(unused)]\n    let base = base.map(|base| {\n        let mut base = Oco::from(base);\n        base.upgrade_inplace();\n        base\n    });\n    let routes = RouteDefs::new_with_base(\n        children.into_inner(),\n        base.clone().unwrap_or_default(),\n    );\n\n    let outer_owner =\n        Owner::current().expect(\"creating Router, but no Owner was found\");\n\n    move || {\n        current_url.track();\n        outer_owner.with(|| {\n            current_url.read_untracked().provide_server_action_error()\n        });\n        FlatRoutesView {\n            current_url: current_url.clone(),\n            location: location.clone(),\n            routes: routes.clone(),\n            fallback: fallback.clone(),\n            outer_owner: outer_owner.clone(),\n            set_is_routing,\n            transition,\n        }\n    }\n}\n\n/// Describes a portion of the nested layout of the app, specifying the route it should match\n/// and the element it should display.\n#[component(transparent)]\npub fn Route<Segments, View>(\n    /// The path fragment that this route should match. This can be created using the\n    /// [`path`](crate::path) macro, or path segments ([`StaticSegment`](crate::StaticSegment),\n    /// [`ParamSegment`](crate::ParamSegment), [`WildcardSegment`](crate::WildcardSegment), and\n    /// [`OptionalParamSegment`](crate::OptionalParamSegment)).\n    path: Segments,\n    /// The view for this route.\n    view: View,\n    /// The mode that this route prefers during server-side rendering.\n    /// Defaults to out-of-order streaming.\n    #[prop(optional)]\n    ssr: SsrMode,\n) -> <NestedRoute<Segments, (), (), View> as IntoMaybeErased>::Output\nwhere\n    View: ChooseView + Clone + 'static,\n    Segments: PossibleRouteMatch + Clone + Send + 'static,\n{\n    NestedRoute::new(path, view)\n        .ssr_mode(ssr)\n        .into_maybe_erased()\n}\n\n/// Describes a portion of the nested layout of the app, specifying the route it should match\n/// and the element it should display.\n#[component(transparent)]\npub fn ParentRoute<Segments, View, Children>(\n    /// The path fragment that this route should match. This can be created using the\n    /// [`path`](crate::path) macro, or path segments ([`StaticSegment`](crate::StaticSegment),\n    /// [`ParamSegment`](crate::ParamSegment), [`WildcardSegment`](crate::WildcardSegment), and\n    /// [`OptionalParamSegment`](crate::OptionalParamSegment)).\n    path: Segments,\n    /// The view for this route.\n    view: View,\n    /// Nested child routes.\n    children: RouteChildren<Children>,\n    /// The mode that this route prefers during server-side rendering.\n    /// Defaults to out-of-order streaming.\n    #[prop(optional)]\n    ssr: SsrMode,\n) -> <NestedRoute<Segments, Children, (), View> as IntoMaybeErased>::Output\nwhere\n    View: ChooseView + Clone + 'static,\n    Children: MatchNestedRoutes + Send + Clone + 'static,\n    Segments: PossibleRouteMatch + Clone + Send + 'static,\n{\n    let children = children.into_inner();\n    NestedRoute::new(path, view)\n        .ssr_mode(ssr)\n        .child(children)\n        .into_maybe_erased()\n}\n\n/// With the `impl Fn` in the return signature, IntoMaybeErased::Output isn't accepted by the compiler, so changing return type depending on the erasure flag.\nmacro_rules! define_protected_route {\n    ($ret:ty) => {\n        /// Describes a route that is guarded by a certain condition. This works the same way as\n        /// [`<Route/>`], except that if the `condition` function evaluates to `Some(false)`, it\n        /// redirects to `redirect_path` instead of displaying its `view`.\n        #[component(transparent)]\n        pub fn ProtectedRoute<Segments, ViewFn, View, C, PathFn, P>(\n            /// The path fragment that this route should match. This can be created using the\n            /// [`path`](crate::path) macro, or path segments ([`StaticSegment`](crate::StaticSegment),\n            /// [`ParamSegment`](crate::ParamSegment), [`WildcardSegment`](crate::WildcardSegment), and\n            /// [`OptionalParamSegment`](crate::OptionalParamSegment)).\n            path: Segments,\n            /// The view for this route.\n            view: ViewFn,\n            /// A function that returns `Option<bool>`, where `Some(true)` means that the user can access\n            /// the page, `Some(false)` means the user cannot access the page, and `None` means this\n            /// information is still loading.\n            condition: C,\n            /// The path that will be redirected to if the condition is `Some(false)`.\n            redirect_path: PathFn,\n            /// Will be displayed while the condition is pending. By default this is the empty view.\n            #[prop(optional, into)]\n            fallback: children::ViewFn,\n            /// The mode that this route prefers during server-side rendering.\n            /// Defaults to out-of-order streaming.\n            #[prop(optional)]\n            ssr: SsrMode,\n        ) -> $ret\n        where\n            Segments: PossibleRouteMatch + Clone + Send + 'static,\n            ViewFn: Fn() -> View + Send + Clone + 'static,\n            View: IntoView + 'static,\n            C: Fn() -> Option<bool> + Send + Clone + 'static,\n            PathFn: Fn() -> P + Send + Clone + 'static,\n            P: Display + 'static,\n        {\n            let fallback = move || fallback.run();\n            let view = move || {\n                let condition = condition.clone();\n                let redirect_path = redirect_path.clone();\n                let view = view.clone();\n                let fallback = fallback.clone();\n                (view! {\n                    <Transition fallback=fallback.clone()>\n                        {move || {\n                            let condition = condition();\n                            let view = view.clone();\n                            let redirect_path = redirect_path.clone();\n                            let fallback = fallback.clone();\n                            Unsuspend::new(move || match condition {\n                                Some(true) => EitherOf3::A(view()),\n                                #[allow(clippy::unit_arg)]\n                                Some(false) => {\n                                    EitherOf3::B(view! { <Redirect path=redirect_path()/> }.into_inner())\n                                }\n                                None => EitherOf3::C(fallback()),\n                            })\n                        }}\n\n                    </Transition>\n                })\n                .into_any()\n            };\n            NestedRoute::new(path, view).ssr_mode(ssr).into_maybe_erased()\n        }\n    };\n}\n\n#[cfg(erase_components)]\ndefine_protected_route!(crate::any_nested_route::AnyNestedRoute);\n#[cfg(not(erase_components))]\ndefine_protected_route!(NestedRoute<Segments, (), (), impl Fn() -> AnyView + Send + Clone>);\n\n/// With the `impl Fn` in the return signature, IntoMaybeErased::Output isn't accepted by the compiler, so changing return type depending on the erasure flag.\nmacro_rules! define_protected_parent_route {\n    ($ret:ty) => {\n        #[component(transparent)]\n        pub fn ProtectedParentRoute<\n            Segments,\n            ViewFn,\n            View,\n            C,\n            PathFn,\n            P,\n            Children,\n        >(\n            /// The path fragment that this route should match. This can be created using the\n            /// [`path`](crate::path) macro, or path segments ([`StaticSegment`](crate::StaticSegment),\n            /// [`ParamSegment`](crate::ParamSegment), [`WildcardSegment`](crate::WildcardSegment), and\n            /// [`OptionalParamSegment`](crate::OptionalParamSegment)).\n            path: Segments,\n            /// The view for this route.\n            view: ViewFn,\n            /// A function that returns `Option<bool>`, where `Some(true)` means that the user can access\n            /// the page, `Some(false)` means the user cannot access the page, and `None` means this\n            /// information is still loading.\n            condition: C,\n            /// Will be displayed while the condition is pending. By default this is the empty view.\n            #[prop(optional, into)]\n            fallback: children::ViewFn,\n            /// The path that will be redirected to if the condition is `Some(false)`.\n            redirect_path: PathFn,\n            /// Nested child routes.\n            children: RouteChildren<Children>,\n            /// The mode that this route prefers during server-side rendering.\n            /// Defaults to out-of-order streaming.\n            #[prop(optional)]\n            ssr: SsrMode,\n        ) -> $ret\n        where\n            Segments: PossibleRouteMatch + Clone + Send + 'static,\n            Children: MatchNestedRoutes + Send + Clone + 'static,\n            ViewFn: Fn() -> View + Send + Clone + 'static,\n            View: IntoView + 'static,\n            C: Fn() -> Option<bool> + Send + Clone + 'static,\n            PathFn: Fn() -> P + Send + Clone + 'static,\n            P: Display + 'static,\n        {\n            let fallback = move || fallback.run();\n            let children = children.into_inner();\n            let view = move || {\n                let condition = condition.clone();\n                let redirect_path = redirect_path.clone();\n                let fallback = fallback.clone();\n                let view = view.clone();\n                let owner = Owner::current().unwrap();\n                let view = {\n                    let fallback = fallback.clone();\n                    move || {\n                        let condition = condition();\n                        let view = view.clone();\n                        let redirect_path = redirect_path.clone();\n                        let fallback = fallback.clone();\n                        let owner = owner.clone();\n                        Unsuspend::new(move || match condition {\n                            // reset the owner so that things like providing context work\n                            // otherwise, this will be a child owner nested within the Transition, not\n                            // the parent owner of the Outlet\n                            //\n                            // clippy: not redundant, a FnOnce vs FnMut issue\n                            #[allow(clippy::redundant_closure)]\n                            Some(true) => EitherOf3::A(owner.with(|| view())),\n                            #[allow(clippy::unit_arg)]\n                            Some(false) => EitherOf3::B(\n                                view! { <Redirect path=redirect_path()/> }\n                                    .into_inner(),\n                            ),\n                            None => EitherOf3::C(fallback()),\n                        })\n                    }\n                };\n                (view! { <Transition fallback>{view}</Transition> }).into_any()\n            };\n            NestedRoute::new(path, view)\n                .ssr_mode(ssr)\n                .child(children)\n                .into_maybe_erased()\n        }\n    };\n}\n\n#[cfg(erase_components)]\ndefine_protected_parent_route!(crate::any_nested_route::AnyNestedRoute);\n#[cfg(not(erase_components))]\ndefine_protected_parent_route!(NestedRoute<Segments, Children, (), impl Fn() -> AnyView + Send + Clone>);\n\n/// Redirects the user to a new URL, whether on the client side or on the server\n/// side. If rendered on the server, this sets a `302` status code and sets a `Location`\n/// header. If rendered in the browser, it uses client-side navigation to redirect.\n/// In either case, it resolves the route relative to the current route. (To use\n/// an absolute path, prefix it with `/`).\n///\n/// **Note**: Support for server-side redirects is provided by the server framework\n/// integrations ([`leptos_actix`] and [`leptos_axum`]. If you’re not using one of those\n/// integrations, you should manually provide a way of redirecting on the server\n/// using [`provide_server_redirect`].\n///\n/// [`leptos_actix`]: <https://docs.rs/leptos_actix/>\n/// [`leptos_axum`]: <https://docs.rs/leptos_axum/>\n#[component(transparent)]\npub fn Redirect<P>(\n    /// The relative path to which the user should be redirected.\n    path: P,\n    /// Navigation options to be used on the client side.\n    #[prop(optional)]\n    #[allow(unused)]\n    options: Option<NavigateOptions>,\n) where\n    P: core::fmt::Display + 'static,\n{\n    // TODO resolve relative path\n    let path = path.to_string();\n\n    // redirect on the server\n    if let Some(redirect_fn) = use_context::<ServerRedirectFunction>() {\n        (redirect_fn.f)(&resolve_path(\n            \"\",\n            &path,\n            Some(&use_matched().get_untracked()),\n        ));\n    }\n    // redirect on the client\n    else {\n        if cfg!(feature = \"ssr\") {\n            #[cfg(feature = \"tracing\")]\n            tracing::warn!(\n                \"Calling <Redirect/> without a ServerRedirectFunction \\\n                 provided, in SSR mode.\"\n            );\n\n            #[cfg(not(feature = \"tracing\"))]\n            eprintln!(\n                \"Calling <Redirect/> without a ServerRedirectFunction \\\n                 provided, in SSR mode.\"\n            );\n            return;\n        }\n        let navigate = use_navigate();\n        navigate(&path, options.unwrap_or_default());\n    }\n}\n\n/// Wrapping type for a function provided as context to allow for\n/// server-side redirects. See [`provide_server_redirect`]\n/// and [`Redirect`].\n#[derive(Clone)]\npub struct ServerRedirectFunction {\n    f: Arc<dyn Fn(&str) + Send + Sync>,\n}\n\nimpl core::fmt::Debug for ServerRedirectFunction {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"ServerRedirectFunction\").finish()\n    }\n}\n\n/// Provides a function that can be used to redirect the user to another\n/// absolute path, on the server. This should set a `302` status code and an\n/// appropriate `Location` header.\npub fn provide_server_redirect(handler: impl Fn(&str) + Send + Sync + 'static) {\n    provide_context(ServerRedirectFunction {\n        f: Arc::new(handler),\n    })\n}\n\n/// A visible indicator that the router is in the process of navigating\n/// to another route.\n///\n/// This is used when `<Router set_is_routing>` has been provided, to\n/// provide some visual indicator that the page is currently loading\n/// async data, so that it is does not appear to have frozen. It can be\n/// styled independently.\n#[component]\npub fn RoutingProgress(\n    /// Whether the router is currently loading the new page.\n    #[prop(into)]\n    is_routing: Signal<bool>,\n    /// The maximum expected time for loading, which is used to\n    /// calibrate the animation process.\n    #[prop(optional, into)]\n    max_time: std::time::Duration,\n    /// The time to show the full progress bar after page has loaded, before hiding it. (Defaults to 100ms.)\n    #[prop(default = std::time::Duration::from_millis(250))]\n    before_hiding: std::time::Duration,\n) -> impl IntoView {\n    const INCREMENT_EVERY_MS: f32 = 5.0;\n    let expected_increments =\n        max_time.as_secs_f32() / (INCREMENT_EVERY_MS / 1000.0);\n    let percent_per_increment = 100.0 / expected_increments;\n\n    let (is_showing, set_is_showing) = signal(false);\n    let (progress, set_progress) = signal(0.0);\n\n    StoredValue::new(RenderEffect::new(\n        move |prev: Option<Option<IntervalHandle>>| {\n            if is_routing.get() && !is_showing.get() {\n                set_is_showing.set(true);\n                set_interval_with_handle(\n                    move || {\n                        set_progress.update(|n| *n += percent_per_increment);\n                    },\n                    Duration::from_millis(INCREMENT_EVERY_MS as u64),\n                )\n                .ok()\n            } else if is_routing.get() && is_showing.get() {\n                set_progress.set(0.0);\n                prev?\n            } else {\n                set_progress.set(100.0);\n                set_timeout(\n                    move || {\n                        set_progress.set(0.0);\n                        set_is_showing.set(false);\n                    },\n                    before_hiding,\n                );\n                if let Some(Some(interval)) = prev {\n                    interval.clear();\n                }\n                None\n            }\n        },\n    ));\n\n    view! {\n        <Show when=move || is_showing.get() fallback=|| ()>\n            <progress min=\"0\" max=\"100\" value=move || progress.get()></progress>\n        </Show>\n    }\n}\n"
  },
  {
    "path": "router/src/flat_router.rs",
    "content": "use crate::{\n    hooks::Matched,\n    location::{LocationProvider, Url},\n    matching::{MatchParams, RouteDefs},\n    params::ParamsMap,\n    view_transition::start_view_transition,\n    ChooseView, MatchInterface, MatchNestedRoutes, PathSegment, RouteList,\n    RouteListing, RouteMatchId,\n};\nuse any_spawner::Executor;\nuse either_of::Either;\nuse futures::FutureExt;\nuse leptos::attr::{any_attribute::AnyAttribute, Attribute};\nuse reactive_graph::{\n    computed::{ArcMemo, ScopedFuture},\n    owner::{provide_context, Owner},\n    signal::ArcRwSignal,\n    traits::{GetUntracked, ReadUntracked, Set},\n    transition::AsyncTransition,\n    wrappers::write::SignalSetter,\n};\nuse std::{cell::RefCell, iter, mem, rc::Rc};\nuse tachys::{\n    hydration::Cursor,\n    reactive_graph::OwnedView,\n    ssr::StreamBuilder,\n    view::{\n        add_attr::AddAnyAttr,\n        any_view::{AnyView, AnyViewState, IntoAny},\n        MarkBranch, Mountable, Position, PositionState, Render, RenderHtml,\n    },\n};\n\npub(crate) struct FlatRoutesView<Loc, Defs, FalFn> {\n    pub current_url: ArcRwSignal<Url>,\n    pub location: Option<Loc>,\n    pub routes: RouteDefs<Defs>,\n    pub fallback: FalFn,\n    pub outer_owner: Owner,\n    pub set_is_routing: Option<SignalSetter<bool>>,\n    pub transition: bool,\n}\n\n/// Retained view state for the flat router.\npub(crate) struct FlatRoutesViewState {\n    #[allow(clippy::type_complexity)]\n    view: AnyViewState,\n    id: Option<RouteMatchId>,\n    owner: Owner,\n    params: ArcRwSignal<ParamsMap>,\n    path: String,\n    url: ArcRwSignal<Url>,\n    matched: ArcRwSignal<String>,\n}\n\nimpl Mountable for FlatRoutesViewState {\n    fn unmount(&mut self) {\n        self.view.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &leptos::tachys::renderer::types::Element,\n        marker: Option<&leptos::tachys::renderer::types::Node>,\n    ) {\n        self.view.mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.view.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<tachys::renderer::types::Element> {\n        self.view.elements()\n    }\n}\n\nimpl<Loc, Defs, FalFn, Fal> Render for FlatRoutesView<Loc, Defs, FalFn>\nwhere\n    Loc: LocationProvider,\n    Defs: MatchNestedRoutes + 'static,\n    FalFn: FnOnce() -> Fal + Send,\n    Fal: IntoAny,\n{\n    type State = Rc<RefCell<FlatRoutesViewState>>;\n\n    fn build(self) -> Self::State {\n        let FlatRoutesView {\n            current_url,\n            routes,\n            fallback,\n            outer_owner,\n            ..\n        } = self;\n        let current_url = current_url.read_untracked();\n\n        // we always need to match the new route\n        let new_match = routes.match_route(current_url.path());\n        let id = new_match.as_ref().map(|n| n.as_id());\n        let matched = ArcRwSignal::new(\n            new_match\n                .as_ref()\n                .map(|n| n.as_matched().to_owned())\n                .unwrap_or_default(),\n        );\n\n        // create default starting points for owner, url, path, and params\n        // these will be held in state so that future navigations can update or replace them\n        let owner = outer_owner.child();\n        let url = ArcRwSignal::new(current_url.to_owned());\n        let path = current_url.path().to_string();\n        let params = ArcRwSignal::new(\n            new_match\n                .as_ref()\n                .map(|n| n.to_params().into_iter().collect())\n                .unwrap_or_default(),\n        );\n        let params_memo = ArcMemo::from(params.clone());\n\n        // release URL lock\n        drop(current_url);\n\n        match new_match {\n            None => Rc::new(RefCell::new(FlatRoutesViewState {\n                view: fallback().into_any().build(),\n                id,\n                owner,\n                params,\n                path,\n                url,\n                matched,\n            })),\n            Some(new_match) => {\n                let (view, child) = new_match.into_view_and_child();\n\n                #[cfg(debug_assertions)]\n                if child.is_some() {\n                    panic!(\n                        \"<FlatRoutes> should not be used with nested routes.\"\n                    );\n                }\n\n                let mut view = Box::pin(owner.with(|| {\n                    provide_context(params_memo);\n                    provide_context(url.clone());\n                    provide_context(Matched(ArcMemo::from(matched.clone())));\n\n                    ScopedFuture::new(async move {\n                        OwnedView::new(view.choose().await)\n                    })\n                }));\n\n                match view.as_mut().now_or_never() {\n                    Some(view) => Rc::new(RefCell::new(FlatRoutesViewState {\n                        view: view.into_any().build(),\n                        id,\n                        owner,\n                        params,\n                        path,\n                        url,\n                        matched,\n                    })),\n                    None => {\n                        let state =\n                            Rc::new(RefCell::new(FlatRoutesViewState {\n                                view: ().into_any().build(),\n                                id,\n                                owner,\n                                params,\n                                path,\n                                url,\n                                matched,\n                            }));\n\n                        Executor::spawn_local({\n                            let state = Rc::clone(&state);\n                            async move {\n                                let view = view.await;\n                                view.into_any()\n                                    .rebuild(&mut state.borrow_mut().view);\n                            }\n                        });\n\n                        state\n                    }\n                }\n            }\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let FlatRoutesView {\n            current_url,\n            location,\n            routes,\n            fallback,\n            outer_owner,\n            set_is_routing,\n            transition,\n        } = self;\n        let url_snapshot = current_url.read_untracked();\n\n        // if the path is the same, we do not need to re-route\n        // we can just update the search query and go about our day\n        let mut initial_state = state.borrow_mut();\n        if url_snapshot.path() == initial_state.path {\n            initial_state.url.set(url_snapshot.to_owned());\n            if let Some(location) = location {\n                location.ready_to_complete();\n            }\n            return;\n        }\n\n        // since the path didn't match, we'll update the retained path for future diffing\n        initial_state.path.clear();\n        initial_state.path.push_str(url_snapshot.path());\n\n        // otherwise, match the new route\n        let new_match = routes.match_route(url_snapshot.path());\n        let new_id = new_match.as_ref().map(|n| n.as_id());\n        let matched_string = new_match\n            .as_ref()\n            .map(|n| n.as_matched().to_owned())\n            .unwrap_or_default();\n        let matched_params = new_match\n            .as_ref()\n            .map(|n| n.to_params().into_iter().collect())\n            .unwrap_or_default();\n\n        // if it's the same route, we just update the params\n        if new_id == initial_state.id {\n            initial_state.params.set(matched_params);\n            initial_state.matched.set(matched_string);\n            if let Some(location) = location {\n                location.ready_to_complete();\n            }\n            return;\n        }\n\n        // otherwise, we need to update the retained path for diffing\n        initial_state.id = new_id;\n\n        // otherwise, it's a new route, so we'll need to\n        // 1) create a new owner, URL signal, and params signal\n        // 2) render the fallback or new route\n        let owner = outer_owner.child();\n        let url = ArcRwSignal::new(url_snapshot.to_owned());\n        let params = ArcRwSignal::new(matched_params);\n        let params_memo = ArcMemo::from(params.clone());\n        let old_owner = mem::replace(&mut initial_state.owner, owner.clone());\n        let old_url = mem::replace(&mut initial_state.url, url.clone());\n        let old_params =\n            mem::replace(&mut initial_state.params, params.clone());\n        let new_matched = ArcRwSignal::new(matched_string);\n        let old_matched =\n            mem::replace(&mut initial_state.matched, new_matched.clone());\n\n        // we drop the route state here, in case there is a <Redirect/> or similar that occurs\n        // while rendering either the fallback or the new route\n        drop(initial_state);\n\n        match new_match {\n            // render fallback\n            None => {\n                owner.with(|| {\n                    provide_context(url);\n                    provide_context(params_memo);\n                    provide_context(Matched(ArcMemo::from(new_matched)));\n                    fallback().into_any().rebuild(&mut state.borrow_mut().view)\n                });\n                if let Some(location) = location {\n                    location.ready_to_complete();\n                }\n            }\n            Some(new_match) => {\n                let (view, child) = new_match.into_view_and_child();\n\n                #[cfg(debug_assertions)]\n                if child.is_some() {\n                    panic!(\n                        \"<FlatRoutes> should not be used with nested routes.\"\n                    );\n                }\n\n                let spawned_path = url_snapshot.path().to_string();\n\n                let is_back = location\n                    .as_ref()\n                    .map(|nav| nav.is_back().get_untracked())\n                    .unwrap_or(false);\n                Executor::spawn_local(owner.with(|| {\n                    provide_context(url);\n                    provide_context(params_memo);\n                    provide_context(Matched(ArcMemo::from(new_matched)));\n\n                    ScopedFuture::new({\n                        let state = Rc::clone(state);\n                        async move {\n                            let view = OwnedView::new(\n                                if let Some(set_is_routing) = set_is_routing {\n                                    set_is_routing.set(true);\n                                    let value =\n                                        AsyncTransition::run(|| view.choose())\n                                            .await;\n                                    set_is_routing.set(false);\n                                    value\n                                } else {\n                                    view.choose().await\n                                },\n                            );\n\n                            // only update the route if it's still the current path\n                            // i.e., if we've navigated away before this has loaded, do nothing\n                            if current_url.read_untracked().path()\n                                == spawned_path\n                            {\n                                let rebuild = move || {\n                                    view.into_any()\n                                        .rebuild(&mut state.borrow_mut().view);\n                                };\n                                if transition {\n                                    start_view_transition(0, is_back, rebuild);\n                                } else {\n                                    rebuild();\n                                }\n                            }\n\n                            if let Some(location) = location {\n                                location.ready_to_complete();\n                            }\n                            drop(old_owner);\n                            drop(old_params);\n                            drop(old_url);\n                            drop(old_matched);\n                        }\n                    })\n                }));\n            }\n        }\n    }\n}\n\nimpl<Loc, Defs, FalFn, Fal> AddAnyAttr for FlatRoutesView<Loc, Defs, FalFn>\nwhere\n    Loc: LocationProvider + Send,\n    Defs: MatchNestedRoutes + Send + 'static,\n    FalFn: FnOnce() -> Fal + Send + 'static,\n    Fal: RenderHtml + 'static,\n{\n    type Output<SomeNewAttr: leptos::attr::Attribute> =\n        FlatRoutesView<Loc, Defs, FalFn>;\n\n    fn add_any_attr<NewAttr: leptos::attr::Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        todo!()\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct MatchedRoute(pub String, pub AnyView);\n\nimpl MatchedRoute {\n    fn branch_name(&self) -> String {\n        format!(\"{:?}\", self.1.as_type_id())\n    }\n}\n\nimpl Render for MatchedRoute {\n    type State = <AnyView as Render>::State;\n\n    fn build(self) -> Self::State {\n        self.1.build()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.1.rebuild(state);\n    }\n}\n\nimpl AddAnyAttr for MatchedRoute {\n    type Output<SomeNewAttr: Attribute> = Self;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let MatchedRoute(id, view) = self;\n        MatchedRoute(id, view.add_any_attr(attr).into_any())\n    }\n}\n\nimpl RenderHtml for MatchedRoute {\n    type AsyncOutput = Self;\n    type Owned = Self;\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {\n        self.1.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        let MatchedRoute(id, view) = self;\n        let view = view.resolve().await;\n        MatchedRoute(id, view)\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        let branch_name = (mark_branches && escape).then(|| self.branch_name());\n        if let Some(bn) = &branch_name {\n            buf.open_branch(bn);\n        }\n        self.1.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n        if let Some(bn) = &branch_name {\n            buf.close_branch(bn);\n            if *position == Position::NextChildAfterText {\n                *position = Position::NextChild;\n            }\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let branch_name = (mark_branches && escape).then(|| self.branch_name());\n        if let Some(bn) = &branch_name {\n            buf.open_branch(bn);\n        }\n        self.1.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n        if let Some(bn) = &branch_name {\n            buf.close_branch(bn);\n            if *position == Position::NextChildAfterText {\n                *position = Position::NextChild;\n            }\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        self.1.hydrate::<FROM_SERVER>(cursor, position)\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        self.1.hydrate_async(cursor, position).await\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl<Loc, Defs, FalFn, Fal> FlatRoutesView<Loc, Defs, FalFn>\nwhere\n    Loc: LocationProvider + Send,\n    Defs: MatchNestedRoutes + Send + 'static,\n    FalFn: FnOnce() -> Fal + Send,\n    Fal: RenderHtml + 'static,\n{\n    fn choose_ssr(self) -> OwnedView<AnyView> {\n        let current_url = self.current_url.read_untracked();\n        let new_match = self.routes.match_route(current_url.path());\n        let owner = self.outer_owner.child();\n        let url = ArcRwSignal::new(current_url.to_owned());\n        let params = ArcRwSignal::new(\n            new_match\n                .as_ref()\n                .map(|n| n.to_params().into_iter().collect::<ParamsMap>())\n                .unwrap_or_default(),\n        );\n        let matched = ArcRwSignal::new(\n            new_match\n                .as_ref()\n                .map(|n| n.as_matched().to_owned())\n                .unwrap_or_default(),\n        );\n        let params_memo = ArcMemo::from(params.clone());\n\n        // release URL lock\n        drop(current_url);\n\n        let view = match new_match {\n            None => (self.fallback)().into_any(),\n            Some(new_match) => {\n                let id = new_match.as_matched().to_string();\n                let (view, _) = new_match.into_view_and_child();\n                let view = owner\n                    .with(|| {\n                        provide_context(url);\n                        provide_context(params_memo);\n                        provide_context(Matched(ArcMemo::from(matched)));\n\n                        ScopedFuture::new(async move { view.choose().await })\n                    })\n                    .now_or_never()\n                    .expect(\"async route used in SSR\");\n                let view = MatchedRoute(id, view);\n                view.into_any()\n            }\n        };\n\n        OwnedView::new_with_owner(view, owner)\n    }\n}\n\nimpl<Loc, Defs, FalFn, Fal> RenderHtml for FlatRoutesView<Loc, Defs, FalFn>\nwhere\n    Loc: LocationProvider + Send,\n    Defs: MatchNestedRoutes + Send + 'static,\n    FalFn: FnOnce() -> Fal + Send + 'static,\n    Fal: RenderHtml + 'static,\n{\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = <Either<Fal, AnyView> as RenderHtml>::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // if this is being run on the server for the first time, generating all possible routes\n        if RouteList::is_generating() {\n            // add routes\n            let (base, routes) = self.routes.generate_routes();\n            let routes = routes\n                .into_iter()\n                .map(|data| {\n                    let path = base\n                        .into_iter()\n                        .flat_map(|base| {\n                            iter::once(PathSegment::Static(\n                                base.to_string().into(),\n                            ))\n                        })\n                        .chain(data.segments)\n                        .collect::<Vec<_>>();\n                    RouteListing::new(\n                        path,\n                        data.ssr_mode,\n                        data.methods,\n                        data.regenerate,\n                    )\n                })\n                .collect::<Vec<_>>();\n\n            // add fallback\n            // TODO fix: causes overlapping route issues on Axum\n            /*routes.push(RouteListing::new(\n                [PathSegment::Static(\n                    base.unwrap_or_default().to_string().into(),\n                )],\n                SsrMode::Async,\n                [\n                    Method::Get,\n                    Method::Post,\n                    Method::Put,\n                    Method::Patch,\n                    Method::Delete,\n                ],\n                None,\n            ));*/\n\n            RouteList::register(RouteList::from(routes));\n        } else {\n            let view = self.choose_ssr();\n            view.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let view = self.choose_ssr();\n        view.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    #[track_caller]\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let FlatRoutesView {\n            current_url,\n            routes,\n            fallback,\n            outer_owner,\n            ..\n        } = self;\n        let current_url = current_url.read_untracked();\n\n        // we always need to match the new route\n        let new_match = routes.match_route(current_url.path());\n        let id = new_match.as_ref().map(|n| n.as_id());\n        let matched = ArcRwSignal::new(\n            new_match\n                .as_ref()\n                .map(|n| n.as_matched().to_owned())\n                .unwrap_or_default(),\n        );\n\n        // create default starting points for owner, url, path, and params\n        // these will be held in state so that future navigations can update or replace them\n        let owner = outer_owner.child();\n        let url = ArcRwSignal::new(current_url.to_owned());\n        let path = current_url.path().to_string();\n        let params = ArcRwSignal::new(\n            new_match\n                .as_ref()\n                .map(|n| n.to_params().into_iter().collect())\n                .unwrap_or_default(),\n        );\n        let params_memo = ArcMemo::from(params.clone());\n\n        // release URL lock\n        drop(current_url);\n\n        match new_match {\n            None => Rc::new(RefCell::new(FlatRoutesViewState {\n                view: fallback()\n                    .into_any()\n                    .hydrate::<FROM_SERVER>(cursor, position),\n                id,\n                owner,\n                params,\n                path,\n                url,\n                matched,\n            })),\n            Some(new_match) => {\n                let (view, child) = new_match.into_view_and_child();\n\n                #[cfg(debug_assertions)]\n                if child.is_some() {\n                    panic!(\n                        \"<FlatRoutes> should not be used with nested routes.\"\n                    );\n                }\n\n                let mut view = Box::pin(owner.with(|| {\n                    provide_context(params_memo);\n                    provide_context(url.clone());\n                    provide_context(Matched(ArcMemo::from(matched.clone())));\n\n                    ScopedFuture::new(async move {\n                        OwnedView::new(view.choose().await)\n                    })\n                }));\n\n                match view.as_mut().now_or_never() {\n                    Some(view) => Rc::new(RefCell::new(FlatRoutesViewState {\n                        view: view\n                            .into_any()\n                            .hydrate::<FROM_SERVER>(cursor, position),\n                        id,\n                        owner,\n                        params,\n                        path,\n                        url,\n                        matched,\n                    })),\n                    None => {\n                        panic!(\n                            \"lazy routes should not be used with \\\n                             hydrate_body(); use hydrate_lazy() instead\"\n                        );\n                    }\n                }\n            }\n        }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let FlatRoutesView {\n            current_url,\n            routes,\n            fallback,\n            outer_owner,\n            ..\n        } = self;\n        let current_url = current_url.read_untracked();\n\n        // we always need to match the new route\n        let new_match = routes.match_route(current_url.path());\n        let id = new_match.as_ref().map(|n| n.as_id());\n        let matched = ArcRwSignal::new(\n            new_match\n                .as_ref()\n                .map(|n| n.as_matched().to_owned())\n                .unwrap_or_default(),\n        );\n\n        // create default starting points for owner, url, path, and params\n        // these will be held in state so that future navigations can update or replace them\n        let owner = outer_owner.child();\n        let url = ArcRwSignal::new(current_url.to_owned());\n        let path = current_url.path().to_string();\n        let params = ArcRwSignal::new(\n            new_match\n                .as_ref()\n                .map(|n| n.to_params().into_iter().collect())\n                .unwrap_or_default(),\n        );\n        let params_memo = ArcMemo::from(params.clone());\n\n        // release URL lock\n        drop(current_url);\n\n        match new_match {\n            None => Rc::new(RefCell::new(FlatRoutesViewState {\n                view: fallback()\n                    .into_any()\n                    .hydrate_async(cursor, position)\n                    .await,\n                id,\n                owner,\n                params,\n                path,\n                url,\n                matched,\n            })),\n            Some(new_match) => {\n                let (view, child) = new_match.into_view_and_child();\n\n                #[cfg(debug_assertions)]\n                if child.is_some() {\n                    panic!(\n                        \"<FlatRoutes> should not be used with nested routes.\"\n                    );\n                }\n\n                let view = Box::pin(owner.with(|| {\n                    provide_context(params_memo);\n                    provide_context(url.clone());\n                    provide_context(Matched(ArcMemo::from(matched.clone())));\n\n                    ScopedFuture::new(async move {\n                        OwnedView::new(view.choose().await)\n                    })\n                }));\n\n                let view = view.await;\n\n                Rc::new(RefCell::new(FlatRoutesViewState {\n                    view: view.into_any().hydrate_async(cursor, position).await,\n                    id,\n                    owner,\n                    params,\n                    path,\n                    url,\n                    matched,\n                }))\n            }\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n"
  },
  {
    "path": "router/src/form.rs",
    "content": "use crate::{\n    components::ToHref,\n    hooks::{has_router, use_navigate, use_resolved_path},\n    location::{BrowserUrl, LocationProvider},\n    NavigateOptions,\n};\nuse leptos::{ev, html::form, logging::*, prelude::*, task::spawn_local};\nuse std::{error::Error, sync::Arc};\nuse wasm_bindgen::{JsCast, UnwrapThrowExt};\nuse web_sys::{FormData, RequestRedirect, Response};\n\ntype OnFormData = Arc<dyn Fn(&FormData)>;\ntype OnResponse = Arc<dyn Fn(&Response)>;\ntype OnError = Arc<dyn Fn(&gloo_net::Error)>;\n\n/// An HTML [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) progressively\n/// enhanced to use client-side routing.\n#[component]\npub fn Form<A>(\n    /// [`method`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#attr-method)\n    /// is the HTTP method to submit the form with (`get` or `post`).\n    #[prop(optional)]\n    method: Option<&'static str>,\n    /// [`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#attr-action)\n    /// is the URL that processes the form submission. Takes a [`String`], [`&str`], or a reactive\n    /// function that returns a [`String`].\n    action: A,\n    /// [`enctype`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#attr-enctype)\n    /// is the MIME type of the form submission if `method` is `post`.\n    #[prop(optional)]\n    enctype: Option<String>,\n    /// A signal that will be incremented whenever the form is submitted with `post`. This can useful\n    /// for reactively updating a [Resource] or another signal whenever the form has been submitted.\n    #[prop(optional)]\n    version: Option<RwSignal<usize>>,\n    /// A signal that will be set if the form submission ends in an error.\n    #[prop(optional)]\n    error: Option<RwSignal<Option<Box<dyn Error + Send + Sync>>>>,\n    /// A callback will be called with the [`FormData`](web_sys::FormData) when the form is submitted.\n    #[prop(optional)]\n    on_form_data: Option<OnFormData>,\n    /// A callback will be called with the [`Response`](web_sys::Response) the server sends in response\n    /// to a form submission.\n    #[prop(optional)]\n    on_response: Option<OnResponse>,\n    /// A callback will be called if the attempt to submit the form results in an error.\n    #[prop(optional)]\n    on_error: Option<OnError>,\n    /// Sets whether the page should be scrolled to the top when the form is submitted.\n    #[prop(optional)]\n    noscroll: bool,\n    /// Sets whether the page should replace the current location in the history when the form is submitted.\n    #[prop(optional)]\n    replace: bool,\n    /// Component children; should include the HTML of the form elements.\n    children: Children,\n) -> impl IntoView\nwhere\n    A: ToHref + Send + Sync + 'static,\n{\n    async fn post_form_data(\n        action: &str,\n        form_data: FormData,\n    ) -> Result<gloo_net::http::Response, gloo_net::Error> {\n        gloo_net::http::Request::post(action)\n            .header(\"Accept\", \"application/json\")\n            .redirect(RequestRedirect::Follow)\n            .body(form_data)?\n            .send()\n            .await\n    }\n\n    async fn post_params(\n        action: &str,\n        enctype: &str,\n        params: web_sys::UrlSearchParams,\n    ) -> Result<gloo_net::http::Response, gloo_net::Error> {\n        gloo_net::http::Request::post(action)\n            .header(\"Accept\", \"application/json\")\n            .header(\"Content-Type\", enctype)\n            .redirect(RequestRedirect::Follow)\n            .body(params)?\n            .send()\n            .await\n    }\n\n    fn inner(\n        has_router: bool,\n        method: Option<&'static str>,\n        action: ArcMemo<String>,\n        enctype: Option<String>,\n        version: Option<RwSignal<usize>>,\n        error: Option<RwSignal<Option<Box<dyn Error + Send + Sync>>>>,\n        on_form_data: Option<OnFormData>,\n        on_response: Option<OnResponse>,\n        on_error: Option<OnError>,\n        children: Children,\n        noscroll: bool,\n        replace: bool,\n    ) -> impl IntoView {\n        let action_version = version;\n        let navigate = has_router.then(use_navigate);\n        let on_submit = {\n            move |ev: web_sys::SubmitEvent| {\n                let navigate = navigate.clone();\n                if ev.default_prevented() {\n                    return;\n                }\n                let navigate_options = NavigateOptions {\n                    scroll: !noscroll,\n                    replace,\n                    ..Default::default()\n                };\n\n                let (form, method, action, enctype) =\n                    extract_form_attributes(&ev);\n\n                let form_data =\n                    web_sys::FormData::new_with_form(&form).unwrap_throw();\n                if let Some(on_form_data) = on_form_data.clone() {\n                    on_form_data(&form_data);\n                }\n                let params =\n                    web_sys::UrlSearchParams::new_with_str_sequence_sequence(\n                        &form_data,\n                    )\n                    .unwrap_throw();\n                // multipart POST (setting Context-Type breaks the request)\n                if method == \"post\" && enctype == \"multipart/form-data\" {\n                    ev.prevent_default();\n                    ev.stop_propagation();\n\n                    let on_response = on_response.clone();\n                    let on_error = on_error.clone();\n                    spawn_local(async move {\n                        let res = post_form_data(&action, form_data).await;\n                        match res {\n                            Err(e) => {\n                                error!(\"<Form/> error while POSTing: {e:#?}\");\n                                if let Some(on_error) = on_error {\n                                    on_error(&e);\n                                }\n                                if let Some(error) = error {\n                                    error.try_set(Some(Box::new(e)));\n                                }\n                            }\n                            Ok(resp) => {\n                                let resp = web_sys::Response::from(resp);\n                                if let Some(version) = action_version {\n                                    version.update(|n| *n += 1);\n                                }\n                                if let Some(error) = error {\n                                    error.try_set(None);\n                                }\n                                if let Some(on_response) = on_response.clone() {\n                                    on_response(&resp);\n                                }\n                                // Check all the logical 3xx responses that might\n                                // get returned from a server function\n                                if resp.redirected() {\n                                    let resp_url = &resp.url();\n                                    match BrowserUrl::parse(resp_url.as_str()) {\n                                        Ok(url) => {\n                                            if url.origin()\n                                                != current_window_origin()\n                                                || navigate.is_none()\n                                            {\n                                                _ = window()\n                                                    .location()\n                                                    .set_href(\n                                                        resp_url.as_str(),\n                                                    );\n                                            } else {\n                                                #[allow(\n                                                    clippy::unnecessary_unwrap\n                                                )]\n                                                let navigate =\n                                                    navigate.unwrap();\n                                                navigate(\n                                                    &format!(\n                                                        \"{}{}{}\",\n                                                        url.path(),\n                                                        if url\n                                                            .search()\n                                                            .is_empty()\n                                                        {\n                                                            \"\"\n                                                        } else {\n                                                            \"?\"\n                                                        },\n                                                        url.search(),\n                                                    ),\n                                                    navigate_options,\n                                                )\n                                            }\n                                        }\n                                        Err(e) => warn!(\"{:?}\", e),\n                                    }\n                                }\n                            }\n                        }\n                    });\n                }\n                // POST\n                else if method == \"post\" {\n                    ev.prevent_default();\n                    ev.stop_propagation();\n\n                    let on_response = on_response.clone();\n                    let on_error = on_error.clone();\n                    spawn_local(async move {\n                        let res = post_params(&action, &enctype, params).await;\n                        match res {\n                            Err(e) => {\n                                error!(\"<Form/> error while POSTing: {e:#?}\");\n                                if let Some(on_error) = on_error {\n                                    on_error(&e);\n                                }\n                                if let Some(error) = error {\n                                    error.try_set(Some(Box::new(e)));\n                                }\n                            }\n                            Ok(resp) => {\n                                let resp = web_sys::Response::from(resp);\n                                if let Some(version) = action_version {\n                                    version.update(|n| *n += 1);\n                                }\n                                if let Some(error) = error {\n                                    error.try_set(None);\n                                }\n                                if let Some(on_response) = on_response.clone() {\n                                    on_response(&resp);\n                                }\n                                // Check all the logical 3xx responses that might\n                                // get returned from a server function\n                                if resp.redirected() {\n                                    let resp_url = &resp.url();\n                                    match BrowserUrl::parse(resp_url.as_str()) {\n                                        Ok(url) => {\n                                            if url.origin()\n                                                != current_window_origin()\n                                                || navigate.is_none()\n                                            {\n                                                _ = window()\n                                                    .location()\n                                                    .set_href(\n                                                        resp_url.as_str(),\n                                                    );\n                                            } else {\n                                                #[allow(\n                                                    clippy::unnecessary_unwrap\n                                                )]\n                                                let navigate =\n                                                    navigate.unwrap();\n                                                navigate(\n                                                    &format!(\n                                                        \"{}{}{}\",\n                                                        url.path(),\n                                                        if url\n                                                            .search()\n                                                            .is_empty()\n                                                        {\n                                                            \"\"\n                                                        } else {\n                                                            \"?\"\n                                                        },\n                                                        url.search(),\n                                                    ),\n                                                    navigate_options,\n                                                )\n                                            }\n                                        }\n                                        Err(e) => warn!(\"{:?}\", e),\n                                    }\n                                }\n                            }\n                        }\n                    });\n                }\n                // otherwise, GET\n                else {\n                    let params =\n                        params.to_string().as_string().unwrap_or_default();\n                    if let Some(navigate) = navigate {\n                        navigate(\n                            &format!(\"{action}?{params}\"),\n                            navigate_options,\n                        );\n                    } else {\n                        _ = window()\n                            .location()\n                            .set_href(&format!(\"{action}?{params}\"));\n                    }\n                    ev.prevent_default();\n                    ev.stop_propagation();\n                }\n            }\n        };\n\n        let method = method.unwrap_or(\"get\");\n\n        form()\n            .attr(\"method\", method)\n            .attr(\"action\", move || action.get())\n            .attr(\"enctype\", enctype)\n            .on(ev::submit, on_submit)\n            .child(children())\n    }\n\n    let has_router = has_router();\n    let action = if has_router {\n        use_resolved_path(move || action.to_href()())\n    } else {\n        ArcMemo::new(move |_| action.to_href()())\n    };\n    inner(\n        has_router,\n        method,\n        action,\n        enctype,\n        version,\n        error,\n        on_form_data,\n        on_response,\n        on_error,\n        children,\n        noscroll,\n        replace,\n    )\n}\n\nfn current_window_origin() -> String {\n    let location = window().location();\n    let protocol = location.protocol().unwrap_or_default();\n    let hostname = location.hostname().unwrap_or_default();\n    let port = location.port().unwrap_or_default();\n    format!(\n        \"{}//{}{}{}\",\n        protocol,\n        hostname,\n        if port.is_empty() { \"\" } else { \":\" },\n        port\n    )\n}\n\nfn extract_form_attributes(\n    ev: &web_sys::Event,\n) -> (web_sys::HtmlFormElement, String, String, String) {\n    let submitter = ev.unchecked_ref::<web_sys::SubmitEvent>().submitter();\n    match &submitter {\n        Some(el) => {\n            if let Some(form) = el.dyn_ref::<web_sys::HtmlFormElement>() {\n                (\n                    form.clone(),\n                    form.get_attribute(\"method\")\n                        .unwrap_or_else(|| \"get\".to_string())\n                        .to_lowercase(),\n                    form.get_attribute(\"action\")\n                        .unwrap_or_default()\n                        .to_lowercase(),\n                    form.get_attribute(\"enctype\")\n                        .unwrap_or_else(|| {\n                            \"application/x-www-form-urlencoded\".to_string()\n                        })\n                        .to_lowercase(),\n                )\n            } else if let Some(input) =\n                el.dyn_ref::<web_sys::HtmlInputElement>()\n            {\n                let form = ev\n                    .target()\n                    .unwrap()\n                    .unchecked_into::<web_sys::HtmlFormElement>();\n                (\n                    form.clone(),\n                    input.get_attribute(\"method\").unwrap_or_else(|| {\n                        form.get_attribute(\"method\")\n                            .unwrap_or_else(|| \"get\".to_string())\n                            .to_lowercase()\n                    }),\n                    input.get_attribute(\"action\").unwrap_or_else(|| {\n                        form.get_attribute(\"action\")\n                            .unwrap_or_default()\n                            .to_lowercase()\n                    }),\n                    input.get_attribute(\"enctype\").unwrap_or_else(|| {\n                        form.get_attribute(\"enctype\")\n                            .unwrap_or_else(|| {\n                                \"application/x-www-form-urlencoded\".to_string()\n                            })\n                            .to_lowercase()\n                    }),\n                )\n            } else if let Some(button) =\n                el.dyn_ref::<web_sys::HtmlButtonElement>()\n            {\n                let form = ev\n                    .target()\n                    .unwrap()\n                    .unchecked_into::<web_sys::HtmlFormElement>();\n                (\n                    form.clone(),\n                    button.get_attribute(\"method\").unwrap_or_else(|| {\n                        form.get_attribute(\"method\")\n                            .unwrap_or_else(|| \"get\".to_string())\n                            .to_lowercase()\n                    }),\n                    button.get_attribute(\"action\").unwrap_or_else(|| {\n                        form.get_attribute(\"action\")\n                            .unwrap_or_default()\n                            .to_lowercase()\n                    }),\n                    button.get_attribute(\"enctype\").unwrap_or_else(|| {\n                        form.get_attribute(\"enctype\")\n                            .unwrap_or_else(|| {\n                                \"application/x-www-form-urlencoded\".to_string()\n                            })\n                            .to_lowercase()\n                    }),\n                )\n            } else {\n                leptos::logging::debug_warn!(\n                    \"<Form/> cannot be submitted from a tag other than \\\n                     <form>, <input>, or <button>\"\n                );\n                panic!()\n            }\n        }\n        None => match ev.target() {\n            None => {\n                leptos::logging::debug_warn!(\n                    \"<Form/> SubmitEvent fired without a target.\"\n                );\n                panic!()\n            }\n            Some(form) => {\n                let form = form.unchecked_into::<web_sys::HtmlFormElement>();\n                (\n                    form.clone(),\n                    form.get_attribute(\"method\")\n                        .unwrap_or_else(|| \"get\".to_string()),\n                    form.get_attribute(\"action\").unwrap_or_default(),\n                    form.get_attribute(\"enctype\").unwrap_or_else(|| {\n                        \"application/x-www-form-urlencoded\".to_string()\n                    }),\n                )\n            }\n        },\n    }\n}\n"
  },
  {
    "path": "router/src/generate_route_list.rs",
    "content": "use crate::{\n    matching::PathSegment,\n    static_routes::{\n        RegenerationFn, ResolvedStaticPath, StaticPath, StaticRoute,\n    },\n    Method, SsrMode,\n};\nuse futures::future::join_all;\nuse reactive_graph::owner::Owner;\nuse std::{\n    cell::{Cell, RefCell},\n    collections::HashSet,\n    future::Future,\n    mem,\n};\nuse tachys::view::RenderHtml;\n\n#[derive(Clone, Debug, Default)]\n/// A route that this application can serve.\npub struct RouteListing {\n    path: Vec<PathSegment>,\n    mode: SsrMode,\n    methods: HashSet<Method>,\n    regenerate: Vec<RegenerationFn>,\n}\n\nimpl RouteListing {\n    /// Create a route listing from its parts.\n    pub fn new(\n        path: impl IntoIterator<Item = PathSegment>,\n        mode: SsrMode,\n        methods: impl IntoIterator<Item = Method>,\n        regenerate: impl IntoIterator<Item = RegenerationFn>,\n    ) -> Self {\n        Self {\n            path: path.into_iter().collect(),\n            mode,\n            methods: methods.into_iter().collect(),\n            regenerate: regenerate.into_iter().collect(),\n        }\n    }\n\n    /// Create a route listing from a path, with the other fields set to default values.\n    pub fn from_path(path: impl IntoIterator<Item = PathSegment>) -> Self {\n        Self::new(path, SsrMode::Async, [], [])\n    }\n\n    /// The path this route handles.\n    pub fn path(&self) -> &[PathSegment] {\n        &self.path\n    }\n\n    /// The rendering mode for this path.\n    pub fn mode(&self) -> &SsrMode {\n        &self.mode\n    }\n\n    /// The HTTP request methods this path can handle.\n    pub fn methods(&self) -> impl Iterator<Item = Method> + '_ {\n        self.methods.iter().copied()\n    }\n\n    /// The set of regeneration functions that should be applied to this route, if it is statically\n    /// generated (either up front or incrementally).\n    pub fn regenerate(&self) -> &[RegenerationFn] {\n        &self.regenerate\n    }\n\n    /// Whether this route is statically rendered.\n    #[inline(always)]\n    pub fn static_route(&self) -> Option<&StaticRoute> {\n        match self.mode {\n            SsrMode::Static(ref route) => Some(route),\n            _ => None,\n        }\n    }\n\n    /// Generates the set of static paths for this route listing, depending on prerendered params.\n    pub async fn into_static_paths(self) -> Option<Vec<ResolvedStaticPath>> {\n        let params = self.static_route()?.to_prerendered_params().await;\n        Some(StaticPath::new(self.path).into_paths(params))\n    }\n\n    /// Generates static files for this route listing.\n    pub async fn generate_static_files<Fut, WriterFut>(\n        mut self,\n        render_fn: impl Fn(&ResolvedStaticPath) -> Fut + Send + Clone + 'static,\n        writer: impl Fn(&ResolvedStaticPath, &Owner, String) -> WriterFut\n            + Send\n            + Clone\n            + 'static,\n        was_404: impl Fn(&Owner) -> bool + Send + Clone + 'static,\n    ) where\n        Fut: Future<Output = (Owner, String)> + Send + 'static,\n        WriterFut: Future<Output = Result<(), std::io::Error>> + Send + 'static,\n    {\n        if let SsrMode::Static(_) = self.mode() {\n            let (all_initial_tx, all_initial_rx) = std::sync::mpsc::channel();\n\n            let render_fn = render_fn.clone();\n            let regenerate = mem::take(&mut self.regenerate);\n            let paths = self.into_static_paths().await.unwrap_or_default();\n\n            for path in paths {\n                // Err(_) here would just mean they've dropped the rx and are no longer awaiting\n                // it; we're only using it to notify them it's done so it doesn't matter in that\n                // case\n                _ = all_initial_tx.send(path.build(\n                    render_fn.clone(),\n                    writer.clone(),\n                    was_404.clone(),\n                    regenerate.clone(),\n                ));\n            }\n\n            join_all(all_initial_rx.try_iter()).await;\n        }\n    }\n\n    /*\n        /// Build a route statically, will return `Ok(true)` on success or `Ok(false)` when the route\n        /// is not marked as statically rendered. All route parameters to use when resolving all paths\n        /// to render should be passed in the `params` argument.\n        pub async fn build_static<IV>(\n            &self,\n            options: &LeptosOptions,\n            app_fn: impl Fn() -> IV + Send + 'static + Clone,\n            additional_context: impl Fn() + Send + 'static + Clone,\n            params: &StaticParamsMap,\n        ) -> Result<bool, std::io::Error>\n        where\n            IV: IntoView + 'static,\n        {\n            match self.mode {\n                SsrMode::Static(route) => {\n                    let mut path = StaticPath::new(self.path.clone());\n                    for path in path.into_paths(params) {\n                        /*path.write(\n                            options,\n                            app_fn.clone(),\n                            additional_context.clone(),\n                        )\n                        .await?;*/ println!()\n                    }\n                    Ok(true)\n                }\n                _ => Ok(false),\n            }\n        }\n    */\n}\n\n/// A set of routes generated from the route definitions.\n#[derive(Debug, Default, Clone)]\npub struct RouteList(Vec<RouteListing>);\n\nimpl From<Vec<RouteListing>> for RouteList {\n    fn from(value: Vec<RouteListing>) -> Self {\n        Self(value)\n    }\n}\n\nimpl RouteList {\n    /// Adds a route listing.\n    pub fn push(&mut self, data: RouteListing) {\n        self.0.push(data);\n    }\n}\n\nimpl RouteList {\n    /// Creates an empty list of routes.\n    pub fn new() -> Self {\n        Self(Vec::new())\n    }\n\n    /// Returns the list of routes.\n    pub fn into_inner(self) -> Vec<RouteListing> {\n        self.0\n    }\n\n    /// Returns and iterator over the list of routes.\n    pub fn iter(&self) -> impl Iterator<Item = &RouteListing> {\n        self.0.iter()\n    }\n\n    /// Generates a list of resolved static paths based on the inner list of route listings.\n    pub async fn into_static_paths(self) -> Vec<ResolvedStaticPath> {\n        futures::future::join_all(\n            self.into_inner()\n                .into_iter()\n                .map(|route_listing| route_listing.into_static_paths()),\n        )\n        .await\n        .into_iter()\n        .flatten()\n        .flatten()\n        .collect::<Vec<_>>()\n    }\n\n    /// Generates static files for the inner list of route listings.\n    pub async fn generate_static_files<Fut, WriterFut>(\n        self,\n        render_fn: impl Fn(&ResolvedStaticPath) -> Fut + Send + Clone + 'static,\n        writer: impl Fn(&ResolvedStaticPath, &Owner, String) -> WriterFut\n            + Send\n            + Clone\n            + 'static,\n        was_404: impl Fn(&Owner) -> bool + Send + Clone + 'static,\n    ) where\n        Fut: Future<Output = (Owner, String)> + Send + 'static,\n        WriterFut: Future<Output = Result<(), std::io::Error>> + Send + 'static,\n    {\n        join_all(self.into_inner().into_iter().map(|route| {\n            route.generate_static_files(\n                render_fn.clone(),\n                writer.clone(),\n                was_404.clone(),\n            )\n        }))\n        .await;\n    }\n}\n\nimpl RouteList {\n    // this is used to indicate to the Router that we are generating\n    // a RouteList for server path generation\n    thread_local! {\n        static IS_GENERATING: Cell<bool> = const { Cell::new(false) };\n        static GENERATED: RefCell<Option<RouteList>> = const { RefCell::new(None) };\n    }\n\n    /// Creates a list of routes, based on route definitions in the given app.\n    pub fn generate<T>(app: impl FnOnce() -> T) -> Option<Self>\n    where\n        T: RenderHtml,\n    {\n        let _resource_guard = leptos::server::SuppressResourceLoad::new();\n        Self::IS_GENERATING.set(true);\n        // run the app once, but throw away the HTML\n        // the router won't actually route, but will fill the listing\n        _ = app().to_html();\n        Self::IS_GENERATING.set(false);\n        Self::GENERATED.take()\n    }\n\n    /// Returns `true` if we are currently in a [`RouteList::generate`] call.\n    pub fn is_generating() -> bool {\n        Self::IS_GENERATING.get()\n    }\n\n    /// Sets the given routes as the list of generated routes.\n    pub fn register(routes: RouteList) {\n        Self::GENERATED.with_borrow_mut(|inner| *inner = Some(routes));\n    }\n}\n"
  },
  {
    "path": "router/src/hooks.rs",
    "content": "use crate::{\n    components::RouterContext,\n    location::{Location, Url},\n    navigate::NavigateOptions,\n    params::{Params, ParamsError, ParamsMap},\n};\nuse leptos::{leptos_dom::helpers::request_animation_frame, oco::Oco};\nuse reactive_graph::{\n    computed::{ArcMemo, Memo},\n    owner::{expect_context, use_context},\n    signal::{ArcRwSignal, ReadSignal},\n    traits::{Get, GetUntracked, ReadUntracked, With, WriteValue},\n    wrappers::write::SignalSetter,\n};\nuse std::{\n    str::FromStr,\n    sync::atomic::{AtomicBool, Ordering},\n};\n\n/// See [`query_signal`].\n#[track_caller]\n#[deprecated = \"This has been renamed to `query_signal` to match Rust naming \\\n                conventions.\"]\npub fn create_query_signal<T>(\n    key: impl Into<Oco<'static, str>>,\n) -> (Memo<Option<T>>, SignalSetter<Option<T>>)\nwhere\n    T: FromStr + ToString + PartialEq + Send + Sync,\n{\n    query_signal(key)\n}\n\n/// See [`query_signal_with_options`].\n#[track_caller]\n#[deprecated = \"This has been renamed to `query_signal_with_options` to mtch \\\n                Rust naming conventions.\"]\npub fn create_query_signal_with_options<T>(\n    key: impl Into<Oco<'static, str>>,\n    nav_options: NavigateOptions,\n) -> (Memo<Option<T>>, SignalSetter<Option<T>>)\nwhere\n    T: FromStr + ToString + PartialEq + Send + Sync,\n{\n    query_signal_with_options(key, nav_options)\n}\n\n/// Constructs a signal synchronized with a specific URL query parameter.\n///\n/// The function creates a bidirectional sync mechanism between the state encapsulated in a signal and a URL query parameter.\n/// This means that any change to the state will update the URL, and vice versa, making the function especially useful\n/// for maintaining state consistency across page reloads.\n///\n/// The `key` argument is the unique identifier for the query parameter to be synced with the state.\n/// It is important to note that only one state can be tied to a specific key at any given time.\n///\n/// The function operates with types that can be parsed from and formatted into strings, denoted by `T`.\n/// If the parsing fails for any reason, the function treats the value as `None`.\n/// The URL parameter can be cleared by setting the signal to `None`.\n///\n/// ```rust\n/// use leptos::prelude::*;\n/// use leptos_router::hooks::query_signal;\n///\n/// #[component]\n/// pub fn SimpleQueryCounter() -> impl IntoView {\n///     let (count, set_count) = query_signal::<i32>(\"count\");\n///     let clear = move |_| set_count.set(None);\n///     let decrement =\n///         move |_| set_count.set(Some(count.get().unwrap_or(0) - 1));\n///     let increment =\n///         move |_| set_count.set(Some(count.get().unwrap_or(0) + 1));\n///\n///     view! {\n///         <div>\n///             <button on:click=clear>\"Clear\"</button>\n///             <button on:click=decrement>\"-1\"</button>\n///             <span>\"Value: \" {move || count.get().unwrap_or(0)} \"!\"</span>\n///             <button on:click=increment>\"+1\"</button>\n///         </div>\n///     }\n/// }\n/// ```\n#[track_caller]\npub fn query_signal<T>(\n    key: impl Into<Oco<'static, str>>,\n) -> (Memo<Option<T>>, SignalSetter<Option<T>>)\nwhere\n    T: FromStr + ToString + PartialEq + Send + Sync,\n{\n    query_signal_with_options::<T>(key, NavigateOptions::default())\n}\n\n/// Constructs a signal synchronized with a specific URL query parameter.\n///\n/// This is the same as [`query_signal`], but allows you to specify additional navigation options.\n#[track_caller]\npub fn query_signal_with_options<T>(\n    key: impl Into<Oco<'static, str>>,\n    nav_options: NavigateOptions,\n) -> (Memo<Option<T>>, SignalSetter<Option<T>>)\nwhere\n    T: FromStr + ToString + PartialEq + Send + Sync,\n{\n    static IS_NAVIGATING: AtomicBool = AtomicBool::new(false);\n\n    let mut key: Oco<'static, str> = key.into();\n    let query_map = use_query_map();\n    let navigate = use_navigate();\n    let location = use_location();\n    let RouterContext {\n        query_mutations, ..\n    } = expect_context();\n\n    let get = Memo::new({\n        let key = key.clone_inplace();\n        move |_| {\n            query_map.with(|map| {\n                map.get_str(&key).and_then(|value| value.parse().ok())\n            })\n        }\n    });\n\n    let set = SignalSetter::map(move |value: Option<T>| {\n        let path = location.pathname.get_untracked();\n        let hash = location.hash.get_untracked();\n        let qs = location.query.read_untracked().to_query_string();\n        let new_url = format!(\"{path}{qs}{hash}\");\n        query_mutations\n            .write_value()\n            .push((key.clone(), value.as_ref().map(ToString::to_string)));\n\n        if !IS_NAVIGATING.load(Ordering::Relaxed) {\n            IS_NAVIGATING.store(true, Ordering::Relaxed);\n            request_animation_frame({\n                let navigate = navigate.clone();\n                let nav_options = nav_options.clone();\n                move || {\n                    navigate(&new_url, nav_options.clone());\n                    IS_NAVIGATING.store(false, Ordering::Relaxed)\n                }\n            })\n        }\n    });\n\n    (get, set)\n}\n\n#[track_caller]\npub(crate) fn has_router() -> bool {\n    use_context::<RouterContext>().is_some()\n}\n\n/*\n/// Returns the current [`RouterContext`], containing information about the router's state.\n#[track_caller]\npub(crate) fn use_router() -> RouterContext {\n    if let Some(router) = use_context::<RouterContext>() {\n        router\n    } else {\n        leptos::leptos_dom::debug_warn!(\n            \"You must call use_router() within a <Router/> component {:?}\",\n            std::panic::Location::caller()\n        );\n        panic!(\"You must call use_router() within a <Router/> component\");\n    }\n}\n*/\n\n/// Returns the current [`Location`], which contains reactive variables\n#[track_caller]\npub fn use_location() -> Location {\n    let RouterContext { location, .. } =\n        use_context().expect(\"Tried to access Location outside a <Router>.\");\n    location\n}\n\npub(crate) type RawParamsMap = ArcMemo<ParamsMap>;\n\n#[track_caller]\nfn use_params_raw() -> RawParamsMap {\n    use_context().expect(\n        \"Tried to access params outside the context of a matched <Route>.\",\n    )\n}\n\n/// Returns a raw key-value map of route params.\n#[track_caller]\npub fn use_params_map() -> Memo<ParamsMap> {\n    use_params_raw().into()\n}\n\n/// Returns the current route params, parsed into the given type, or an error.\n#[track_caller]\npub fn use_params<T>() -> Memo<Result<T, ParamsError>>\nwhere\n    T: Params + PartialEq + Send + Sync + 'static,\n{\n    // TODO this can be optimized in future to map over the signal, rather than cloning\n    let params = use_params_raw();\n    Memo::new(move |_| params.with(T::from_map))\n}\n\n#[track_caller]\nfn use_url_raw() -> ArcRwSignal<Url> {\n    use_context().unwrap_or_else(|| {\n        let RouterContext { current_url, .. } = use_context().expect(\n            \"Tried to access reactive URL outside a <Router> component.\",\n        );\n        current_url\n    })\n}\n\n/// Gives reactive access to the current URL.\n#[track_caller]\npub fn use_url() -> ReadSignal<Url> {\n    use_url_raw().read_only().into()\n}\n\n/// Returns a raw key-value map of the URL search query.\n#[track_caller]\npub fn use_query_map() -> Memo<ParamsMap> {\n    let url = use_url_raw();\n    Memo::new(move |_| url.with(|url| url.search_params().clone()))\n}\n\n/// Returns the current URL search query, parsed into the given type, or an error.\n#[track_caller]\npub fn use_query<T>() -> Memo<Result<T, ParamsError>>\nwhere\n    T: Params + PartialEq + Send + Sync + 'static,\n{\n    let url = use_url_raw();\n    Memo::new(move |_| url.with(|url| T::from_map(url.search_params())))\n}\n\n#[derive(Debug, Clone)]\npub(crate) struct Matched(pub ArcMemo<String>);\n\n/// Resolves the given path relative to the current route.\n#[track_caller]\npub(crate) fn use_resolved_path(\n    path: impl Fn() -> String + Send + Sync + 'static,\n) -> ArcMemo<String> {\n    let router = use_context::<RouterContext>()\n        .expect(\"called use_resolved_path outside a <Router>\");\n    // TODO make this work with flat routes too?\n    let matched = use_context::<Matched>().map(|n| n.0);\n    ArcMemo::new(move |_| {\n        let path = path();\n        if path.starts_with('/') {\n            path\n        } else {\n            router\n                .resolve_path(\n                    &path,\n                    matched.as_ref().map(|n| n.get()).as_deref(),\n                )\n                .to_string()\n        }\n    })\n}\n\n/// Returns a function that can be used to navigate to a new route.\n///\n/// This should only be called on the client; it does nothing during\n/// server rendering.\n///\n/// ```rust\n/// # if false { // can't actually navigate, no <Router/>\n/// let navigate = leptos_router::hooks::use_navigate();\n/// navigate(\"/\", Default::default());\n/// # }\n/// ```\n#[track_caller]\npub fn use_navigate() -> impl Fn(&str, NavigateOptions) + Clone {\n    let cx = use_context::<RouterContext>()\n        .expect(\"You cannot call `use_navigate` outside a <Router>.\");\n    move |path: &str, options: NavigateOptions| cx.navigate(path, options)\n}\n\n/// Returns a reactive string that contains the route that was matched for\n/// this [`Route`](crate::components::Route).\n#[track_caller]\npub fn use_matched() -> Memo<String> {\n    use_context::<Matched>()\n        .expect(\"use_matched called outside a matched Route\")\n        .0\n        .into()\n}\n"
  },
  {
    "path": "router/src/lib.rs",
    "content": "//! # Leptos Router\n//!\n//! Leptos Router is a router and state management tool for web applications\n//! written in Rust using the Leptos web framework.\n//! It is ”isomorphic”, i.e., it can be used for client-side applications/single-page\n//! apps (SPAs), server-side rendering/multi-page apps (MPAs), or to synchronize\n//! state between the two.\n//!\n//! ## Philosophy\n//!\n//! Leptos Router is built on a few simple principles:\n//! 1. **URL drives state.** For web applications, the URL should be the ultimate\n//!    source of truth for most of your app’s state. (It’s called a **Universal\n//!    Resource Locator** for a reason!)\n//!\n//! 2. **Nested routing.** A URL can match multiple routes that exist in a nested tree\n//!    and are rendered by different components. This means you can navigate between siblings\n//!    in this tree without re-rendering or triggering any change in the parent routes.\n//!\n//! 3. **Progressive enhancement.** The [`A`](crate::components::A) and\n//!    [`Form`](crate::components::Form) components resolve any relative\n//!    nested routes, render actual `<a>` and `<form>` elements, and (when possible)\n//!    upgrading them to handle those navigations with client-side routing. If you’re using\n//!    them with server-side rendering (with or without hydration), they just work,\n//!    whether JS/WASM have loaded or not.\n//!\n//! ## Example\n//!\n//! ```rust\n//! use leptos::prelude::*;\n//! use leptos_router::components::*;\n//! use leptos_router::path;\n//! use leptos_router::hooks::use_params_map;\n//!\n//! #[component]\n//! pub fn RouterExample() -> impl IntoView {\n//!   view! {\n//!\n//!     <div id=\"root\">\n//!       // we wrap the whole app in a <Router/> to allow client-side navigation\n//!       // from our nav links below\n//!       <Router>\n//!         <main>\n//!           // <Routes/> both defines our routes and shows them on the page\n//!           <Routes fallback=|| \"Not found.\">\n//!             // our root route: the contact list is always shown\n//!             <ParentRoute\n//!               path=path!(\"\")\n//!               view=ContactList\n//!             >\n//!               // users like /gbj or /bob\n//!               <Route\n//!                 path=path!(\":id\")\n//!                 view=Contact\n//!               />\n//!               // a fallback if the /:id segment is missing from the URL\n//!               <Route\n//!                 path=path!(\"\")\n//!                 view=move || view! { <p class=\"contact\">\"Select a contact.\"</p> }\n//!               />\n//!             </ParentRoute>\n//!           </Routes>\n//!         </main>\n//!       </Router>\n//!     </div>\n//!   }\n//! }\n//!\n//! type ContactSummary = (); // TODO!\n//! type Contact = (); // TODO!()\n//!\n//! // contact_data reruns whenever the :id param changes\n//! async fn contact_data(id: String) -> Contact {\n//!   todo!()\n//! }\n//!\n//! // contact_list_data *doesn't* rerun when the :id changes,\n//! // because that param is nested lower than the <ContactList/> route\n//! async fn contact_list_data() -> Vec<ContactSummary> {\n//!   todo!()\n//! }\n//!\n//! #[component]\n//! fn ContactList() -> impl IntoView {\n//!   // loads the contact list data once; doesn't reload when nested routes change\n//!   let contacts = Resource::new(|| (), |_| contact_list_data());\n//!   view! {\n//!\n//!     <div>\n//!       // show the contacts\n//!       <ul>\n//!         {move || contacts.get().map(|contacts| view! { <li>\"todo contact info\"</li> } )}\n//!       </ul>\n//!\n//!       // insert the nested child route here\n//!       <Outlet/>\n//!     </div>\n//!   }\n//! }\n//!\n//! #[component]\n//! fn Contact() -> impl IntoView {\n//!   let params = use_params_map();\n//!   let data = Resource::new(\n//!     move || params.read().get(\"id\").unwrap_or_default(),\n//!     move |id| contact_data(id)\n//!   );\n//!   // ... return some view\n//! }\n//! ```\n//!\n//! You can find examples of additional APIs in the [`router`] example.\n//!\n//! # Feature Flags\n//! - `ssr` Server-side rendering: Generate an HTML string (typically on the server)\n//! - `nightly`: On `nightly` Rust, enables the function-call syntax for signal getters and setters.\n//! - `tracing`: Enables support for the `tracing` crate.\n//!\n//! [`Leptos`]: <https://github.com/leptos-rs/leptos>\n//! [`router`]: <https://github.com/leptos-rs/leptos/blob/main/examples/router/src/lib.rs>\n\n#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(auto_traits))]\n#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(negative_impls))]\n\n/// Components for route definition and for enhanced links and forms.\npub mod components;\n/// An optimized \"flat\" router without nested routes.\npub mod flat_router;\nmod form;\nmod generate_route_list;\n/// Hooks that can be used to access router state inside your components.\npub mod hooks;\nmod link;\n/// Utilities for accessing the current location.\npub mod location;\nmod matching;\nmod method;\nmod navigate;\n/// A nested router that supports multiple levels of route definitions.\npub mod nested_router;\n/// Support for maps of parameters in the path or in the query.\npub mod params;\nmod ssr_mode;\n/// Support for static routing.\npub mod static_routes;\n\npub use generate_route_list::*;\n#[doc(inline)]\npub use leptos_router_macro::{lazy_route, path};\npub use matching::*;\npub use method::*;\npub use navigate::*;\npub use ssr_mode::*;\n\npub(crate) mod view_transition {\n    use js_sys::{Function, Promise, Reflect};\n    use leptos::leptos_dom::helpers::document;\n    use wasm_bindgen::{closure::Closure, intern, JsCast, JsValue};\n\n    pub fn start_view_transition(\n        level: u8,\n        is_back_navigation: bool,\n        fun: impl FnOnce() + 'static,\n    ) {\n        let document = document();\n        let document_element = document.document_element().unwrap();\n        let class_list = document_element.class_list();\n        let svt = Reflect::get(\n            &document,\n            &JsValue::from_str(intern(\"startViewTransition\")),\n        )\n        .and_then(|svt| svt.dyn_into::<Function>());\n        _ = class_list.add_1(&format!(\"router-outlet-{level}\"));\n        if is_back_navigation {\n            _ = class_list.add_1(\"router-back\");\n        }\n        match svt {\n            Ok(svt) => {\n                let cb = Closure::once_into_js(Box::new(move || {\n                    fun();\n                }));\n                match svt.call1(\n                    document.unchecked_ref(),\n                    cb.as_ref().unchecked_ref(),\n                ) {\n                    Ok(view_transition) => {\n                        let class_list = document_element.class_list();\n                        let finished = Reflect::get(\n                            &view_transition,\n                            &JsValue::from_str(\"finished\"),\n                        )\n                        .expect(\"no `finished` property on ViewTransition\")\n                        .unchecked_into::<Promise>();\n                        let cb = Closure::new(Box::new(move |_| {\n                            if is_back_navigation {\n                                class_list.remove_1(\"router-back\").unwrap();\n                            }\n                            class_list\n                                .remove_1(&format!(\"router-outlet-{level}\"))\n                                .unwrap();\n                        })\n                            as Box<dyn FnMut(JsValue)>);\n                        _ = finished.then(&cb);\n                        cb.into_js_value();\n                    }\n                    Err(e) => {\n                        web_sys::console::log_1(&e);\n                    }\n                }\n            }\n            Err(_) => {\n                leptos::logging::warn!(\n                    \"NOTE: View transitions are not supported in this \\\n                     browser; unless you provide a polyfill, view transitions \\\n                     will not be applied.\"\n                );\n                fun();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "router/src/link.rs",
    "content": "use crate::{components::RouterContext, hooks::use_resolved_path};\nuse leptos::{children::Children, oco::Oco, prelude::*};\nuse reactive_graph::{computed::ArcMemo, owner::use_context};\nuse std::{borrow::Cow, rc::Rc};\n\n/// Describes a value that is either a static or a reactive URL, i.e.,\n/// a [`String`], a [`&str`], or a reactive `Fn() -> String`.\npub trait ToHref {\n    /// Converts the (static or reactive) URL into a function that can be called to\n    /// return the URL.\n    fn to_href(&self) -> Box<dyn Fn() -> String + '_>;\n}\n\nimpl ToHref for &str {\n    fn to_href(&self) -> Box<dyn Fn() -> String> {\n        let s = self.to_string();\n        Box::new(move || s.clone())\n    }\n}\n\nimpl ToHref for String {\n    fn to_href(&self) -> Box<dyn Fn() -> String> {\n        let s = self.clone();\n        Box::new(move || s.clone())\n    }\n}\n\nimpl ToHref for Cow<'_, str> {\n    fn to_href(&self) -> Box<dyn Fn() -> String + '_> {\n        let s = self.to_string();\n        Box::new(move || s.clone())\n    }\n}\n\nimpl ToHref for Oco<'_, str> {\n    fn to_href(&self) -> Box<dyn Fn() -> String + '_> {\n        let s = self.to_string();\n        Box::new(move || s.clone())\n    }\n}\n\nimpl ToHref for Rc<str> {\n    fn to_href(&self) -> Box<dyn Fn() -> String + '_> {\n        let s = self.to_string();\n        Box::new(move || s.clone())\n    }\n}\n\nimpl<F> ToHref for F\nwhere\n    F: Fn() -> String + 'static,\n{\n    fn to_href(&self) -> Box<dyn Fn() -> String + '_> {\n        Box::new(self)\n    }\n}\n\n/// An HTML [`a`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)\n/// progressively enhanced to use client-side routing.\n///\n/// Client-side routing also works with ordinary HTML `<a>` tags, but `<A>` does two additional things:\n/// 1) Correctly resolves relative nested routes. Relative routing with ordinary `<a>` tags can be tricky.\n///    For example, if you have a route like `/post/:id`, `<A href=\"1\">` will generate the correct relative\n///    route, but `<a href=\"1\">` likely will not (depending on where it appears in your view.)\n/// 2) Sets the `aria-current` attribute if this link is the active link (i.e., it’s a link to the page you’re on).\n///    This is helpful for accessibility and for styling. For example, maybe you want to set the link a\n///    different color if it’s a link to the page you’re currently on.\n///\n/// ### Additional Attributes\n///\n/// You can add additional HTML attributes to the `<a>` element created by this component using the attribute\n/// spreading syntax for components. For example, to add a class, you can use `attr:class=\"my-link\"`.\n/// Alternately, you can add any number of HTML attributes (include `class`) after a `{..}` marker.\n///\n/// ```rust\n/// # use leptos::prelude::*; use leptos_router::components::A;\n/// # fn spread_example() -> impl IntoView {\n/// view! {\n///   <A href=\"/about\" attr:class=\"my-link\" {..} id=\"foo\">\"Some link\"</A>\n///   <A href=\"/about\" {..} class=\"my-link\" id=\"bar\">\"Another link\"</A>\n///   <A href=\"/about\" {..} class:my-link=true id=\"baz\">\"One more\"</A>\n/// }\n/// # }\n/// ```\n///\n/// For more information on this attribute spreading syntax, [see here](https://book.leptos.dev/view/03_components.html#spreading-attributes-onto-components).\n///\n/// ### DOM Properties\n///\n/// `<a>` elements can take several additional DOM properties with special meanings.\n/// - **`prop:state`**: An object of any type that will be pushed to router state.\n/// - **`prop:replace`**: If `true`, the link will not add to the browser's history (so, pressing `Back`\n/// will skip this page.)\n///\n/// Previously, this component took these as component props. Now, they can be added using the\n/// `prop:` syntax, and will be added directly to the DOM. They can work with either `<a>` elements\n/// or the `<A/>` component.\n#[component]\npub fn A<H>(\n    /// Used to calculate the link's `href` attribute. Will be resolved relative\n    /// to the current route.\n    href: H,\n    /// Where to display the linked URL, as the name for a browsing context (a tab, window, or `<iframe>`).\n    #[prop(optional, into)]\n    target: Option<Oco<'static, str>>,\n    /// If `true`, the link is marked active when the location matches exactly;\n    /// if false, link is marked active if the current route starts with it.\n    #[prop(optional)]\n    exact: bool,\n    /// If `true`, and when `href` has a trailing slash, `aria-current` be only be set if `current_url` also has\n    /// a trailing slash.\n    #[prop(optional)]\n    strict_trailing_slash: bool,\n    /// If `true`, the router will scroll to the top of the window at the end of navigation. Defaults to `true`.\n    #[prop(default = true)]\n    scroll: bool,\n    /// The nodes or elements to be shown inside the link.\n    children: Children,\n) -> impl IntoView + 'static\nwhere\n    H: ToHref + Send + Sync + 'static,\n{\n    fn inner(\n        href: ArcMemo<String>,\n        target: Option<Oco<'static, str>>,\n        exact: bool,\n        children: Children,\n        strict_trailing_slash: bool,\n        scroll: bool,\n    ) -> impl IntoView {\n        let RouterContext { current_url, .. } =\n            use_context().expect(\"tried to use <A/> outside a <Router/>.\");\n        let is_active = {\n            let href = href.clone();\n            move || {\n                let path = normalize_path(&href.read());\n                current_url.with(|loc| {\n                    let loc = loc.path();\n                    if exact {\n                        loc == path\n                    } else {\n                        is_active_for(&path, loc, strict_trailing_slash)\n                    }\n                })\n            }\n        };\n\n        view! {\n            <a\n                href=move || href.get()\n                target=target\n                aria-current=move || if is_active() { Some(\"page\") } else { None }\n                data-noscroll=!scroll\n            >\n\n                {children()}\n            </a>\n        }\n    }\n\n    let href = use_resolved_path(move || href.to_href()());\n    inner(href, target, exact, children, strict_trailing_slash, scroll)\n}\n\n// Test if `href` is active for `location`.  Assumes _both_ `href` and `location` begin with a `'/'`.\nfn is_active_for(\n    href: &str,\n    location: &str,\n    strict_trailing_slash: bool,\n) -> bool {\n    let mut href_f = href.split('/');\n    // location _must_ be consumed first to avoid draining href_f early\n    // also using enumerate to special case _the first two_ so that the allowance for ignoring the comparison\n    // with the loc fragment on an emtpy href fragment for non root related parts.\n    std::iter::zip(location.split('/'), href_f.by_ref())\n        .enumerate()\n        .all(|(c, (loc_p, href_p))| {\n            loc_p == href_p || href_p.is_empty() && c > 1\n        })\n        && match href_f.next() {\n            // when no href fragments remain, location is definitely somewhere nested inside href\n            None => true,\n            // when an outstanding href fragment is an empty string, default `strict_trailing_slash` setting will\n            // have the typical expected case where href=\"/item/\" is active for location=\"/item\", but when toggled\n            // to true it becomes inactive; please refer to test case comments for explanation.\n            Some(\"\") => !strict_trailing_slash,\n            // inactive when href fragments remain (otherwise false postive for href=\"/item/one\", location=\"/item\")\n            _ => false,\n        }\n}\n\n// Resolve `\"..\"` segments in the path. Assume path is either empty or starts with a `'/'``.\nfn normalize_path(path: &str) -> String {\n    // Return only on the only condition where leading slash\n    // is allowed to be missing.\n    if path.is_empty() {\n        return String::new();\n    }\n    let mut del = 0;\n    let mut it = path\n        .split(['?', '#'])\n        .next()\n        .unwrap_or_default()\n        .split(['/'])\n        .rev()\n        .peekable();\n\n    let init = if it.peek() == Some(&\"..\") {\n        String::from(\"/\")\n    } else {\n        String::new()\n    };\n    let mut path = it\n        .filter(|v| {\n            if *v == \"..\" {\n                del += 1;\n                false\n            } else if *v == \".\" {\n                false\n            } else if del > 0 {\n                del -= 1;\n                false\n            } else {\n                true\n            }\n        })\n        // We cannot reverse before the fold again bc the filter\n        // would be forwards again.\n        .fold(init, |mut p, v| {\n            p.reserve(v.len() + 1);\n            p.insert(0, '/');\n            p.insert_str(0, v);\n            p\n        });\n    path.truncate(path.len().saturating_sub(1));\n\n    // Path starts with '/' giving it an extra empty segment after the split\n    // Which should not be removed.\n    if !path.starts_with('/') {\n        path.insert(0, '/');\n    }\n    path\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{is_active_for, normalize_path};\n\n    #[test]\n    fn is_active_for_matched() {\n        [false, true].into_iter().for_each(|f| {\n            // root\n            assert!(is_active_for(\"/\", \"/\", f));\n\n            // both at one level for all combinations of trailing slashes\n            assert!(is_active_for(\"/item\", \"/item\", f));\n            // assert!(is_active_for(\"/item/\", \"/item\", f));\n            assert!(is_active_for(\"/item\", \"/item/\", f));\n            assert!(is_active_for(\"/item/\", \"/item/\", f));\n\n            // plus sub one level for all combinations of trailing slashes\n            assert!(is_active_for(\"/item\", \"/item/one\", f));\n            assert!(is_active_for(\"/item\", \"/item/one/\", f));\n            assert!(is_active_for(\"/item/\", \"/item/one\", f));\n            assert!(is_active_for(\"/item/\", \"/item/one/\", f));\n\n            // both at two levels for all combinations of trailing slashes\n            assert!(is_active_for(\"/item/1\", \"/item/1\", f));\n            // assert!(is_active_for(\"/item/1/\", \"/item/1\", f));\n            assert!(is_active_for(\"/item/1\", \"/item/1/\", f));\n            assert!(is_active_for(\"/item/1/\", \"/item/1/\", f));\n\n            // plus sub various levels for all combinations of trailing slashes\n            assert!(is_active_for(\"/item/1\", \"/item/1/two\", f));\n            assert!(is_active_for(\"/item/1\", \"/item/1/three/four/\", f));\n            assert!(is_active_for(\"/item/1/\", \"/item/1/three/four\", f));\n            assert!(is_active_for(\"/item/1/\", \"/item/1/two/\", f));\n\n            // both at various levels for various trailing slashes\n            assert!(is_active_for(\"/item/1/two/three\", \"/item/1/two/three\", f));\n            assert!(is_active_for(\n                \"/item/1/two/three/444\",\n                \"/item/1/two/three/444/\",\n                f\n            ));\n            // assert!(is_active_for(\n            //     \"/item/1/two/three/444/FIVE/\",\n            //     \"/item/1/two/three/444/FIVE\",\n            //     f\n            // ));\n            assert!(is_active_for(\n                \"/item/1/two/three/444/FIVE/final/\",\n                \"/item/1/two/three/444/FIVE/final/\",\n                f\n            ));\n\n            // sub various levels for various trailing slashes\n            assert!(is_active_for(\n                \"/item/1/two/three\",\n                \"/item/1/two/three/three/two/1/item\",\n                f\n            ));\n            assert!(is_active_for(\n                \"/item/1/two/three/444\",\n                \"/item/1/two/three/444/just_one_more/\",\n                f\n            ));\n            assert!(is_active_for(\n                \"/item/1/two/three/444/final/\",\n                \"/item/1/two/three/444/final/just/kidding\",\n                f\n            ));\n\n            // edge/weird/unexpected cases?\n\n            // since empty fragments are not checked, these all highlight\n            assert!(is_active_for(\n                \"/item/////\",\n                \"/item/one/two/three/four/\",\n                f\n            ));\n            assert!(is_active_for(\n                \"/item/////\",\n                \"/item/1/two/three/three/two/1/item\",\n                f\n            ));\n            assert!(is_active_for(\n                \"/item/1///three//1\",\n                \"/item/1/two/three/three/two/1/item\",\n                f\n            ));\n\n            // artifact of the checking algorithm, as it assumes empty segments denote termination of sort, so\n            // omission acts like a wildcard that isn't checked.\n            assert!(is_active_for(\n                \"/item//foo\",\n                \"/item/this_is_not_empty/foo/bar/baz\",\n                f\n            ));\n        });\n\n        // Refer to comment on the similar scenario on the next test case for explanation, as this assumes the\n        // \"typical\" case where the strict trailing slash flag is unset or false.\n        assert!(is_active_for(\"/item/\", \"/item\", false));\n        assert!(is_active_for(\"/item/1/\", \"/item/1\", false));\n        assert!(is_active_for(\n            \"/item/1/two/three/444/FIVE/\",\n            \"/item/1/two/three/444/FIVE\",\n            false\n        ));\n    }\n\n    #[test]\n    fn is_active_for_mismatched() {\n        [false, true].into_iter().for_each(|f| {\n            // href=\"/\"\n            assert!(!is_active_for(\"/\", \"/item\", f));\n            assert!(!is_active_for(\"/\", \"/somewhere/\", f));\n            assert!(!is_active_for(\"/\", \"/else/where\", f));\n            assert!(!is_active_for(\"/\", \"/no/where/\", f));\n\n            // non root href but location at root\n            assert!(!is_active_for(\"/somewhere\", \"/\", f));\n            assert!(!is_active_for(\"/somewhere/\", \"/\", f));\n            assert!(!is_active_for(\"/else/where\", \"/\", f));\n            assert!(!is_active_for(\"/no/where/\", \"/\", f));\n\n            // mismatch either side all combinations of trailing slashes\n            assert!(!is_active_for(\"/level\", \"/item\", f));\n            assert!(!is_active_for(\"/level\", \"/item/\", f));\n            assert!(!is_active_for(\"/level/\", \"/item\", f));\n            assert!(!is_active_for(\"/level/\", \"/item/\", f));\n\n            // one level parent for all combinations of trailing slashes\n            assert!(!is_active_for(\"/item/one\", \"/item\", f));\n            assert!(!is_active_for(\"/item/one/\", \"/item\", f));\n            assert!(!is_active_for(\"/item/one\", \"/item/\", f));\n            assert!(!is_active_for(\"/item/one/\", \"/item/\", f));\n\n            // various parent levels for all combinations of trailing slashes\n            assert!(!is_active_for(\"/item/1/two\", \"/item/1\", f));\n            assert!(!is_active_for(\"/item/1/three/four/\", \"/item/1\", f));\n            assert!(!is_active_for(\"/item/1/three/four\", \"/item/\", f));\n            assert!(!is_active_for(\"/item/1/two/\", \"/item/\", f));\n\n            // sub various levels for various trailing slashes\n            assert!(!is_active_for(\n                \"/item/1/two/three/three/two/1/item\",\n                \"/item/1/two/three\",\n                f\n            ));\n            assert!(!is_active_for(\n                \"/item/1/two/three/444/just_one_more/\",\n                \"/item/1/two/three/444\",\n                f\n            ));\n            assert!(!is_active_for(\n                \"/item/1/two/three/444/final/just/kidding\",\n                \"/item/1/two/three/444/final/\",\n                f\n            ));\n\n            // edge/weird/unexpected cases?\n\n            // default trailing slash has the expected behavior of non-matching of any non-root location\n            // this checks as if `href=\"/\"`\n            assert!(!is_active_for(\n                \"//////\",\n                \"/item/1/two/three/three/two/1/item\",\n                f\n            ));\n            // some weird root location?\n            assert!(!is_active_for(\n                \"/item/1/two/three/three/two/1/item\",\n                \"//////\",\n                f\n            ));\n\n            assert!(!is_active_for(\n                \"/item/one/two/three/four/\",\n                \"/item/////\",\n                f\n            ));\n            assert!(!is_active_for(\n                \"/item/one/two/three/four/\",\n                \"/item////four/\",\n                f\n            ));\n        });\n\n        // The following tests enables the `strict_trailing_slash` flag, which allows the less common\n        // interpretation of `/item/` being a resource with proper subitems while `/item` just simply browsing\n        // the flat `item` while still currently at `/`, as the user hasn't \"initiate the descent\" into it\n        // (e.g. a certain filesystem tried to implement a feature where a directory can be opened as a file),\n        // it may be argued that when user is simply checking what `/item` is by going to that location, they\n        // are still active at `/` - only by actually going into `/item/` that they are truly active there.\n        //\n        // In any case, the algorithm currently assumes the more \"typical\" case where the non-slash version is\n        // an \"alias\" of the trailing-slash version (so aria-current is set), as \"ordinarily\" this is the case\n        // expected by \"ordinary\" end-users who almost never encounter this particular scenario.\n\n        assert!(!is_active_for(\"/item/\", \"/item\", true));\n        assert!(!is_active_for(\"/item/1/\", \"/item/1\", true));\n        assert!(!is_active_for(\n            \"/item/1/two/three/444/FIVE/\",\n            \"/item/1/two/three/444/FIVE\",\n            true\n        ));\n\n        // That said, in this particular scenario, the definition above should result the following be asserted\n        // as true, but then it follows that every scenario may be true as the root was special cased - in\n        // which case it becomes a bit meaningless?\n        //\n        // assert!(is_active_for(\"/\", \"/item\", true));\n        //\n        // Perhaps there needs to be a flag such that aria-curent applies only the _same level_, e.g\n        // assert!(is_same_level(\"/\", \"/\"))\n        // assert!(is_same_level(\"/\", \"/anything\"))\n        // assert!(!is_same_level(\"/\", \"/some/\"))\n        // assert!(!is_same_level(\"/\", \"/some/level\"))\n        // assert!(is_same_level(\"/some/\", \"/some/\"))\n        // assert!(is_same_level(\"/some/\", \"/some/level\"))\n        // assert!(!is_same_level(\"/some/\", \"/some/level/\"))\n        // assert!(!is_same_level(\"/some/\", \"/some/level/deeper\"))\n    }\n\n    #[test]\n    fn normalize_path_test() {\n        // Make sure it doesn't touch already normalized urls.\n        assert!(normalize_path(\"\") == \"\".to_string());\n        assert!(normalize_path(\"/\") == \"/\".to_string());\n        assert!(normalize_path(\"/some\") == \"/some\".to_string());\n        assert!(normalize_path(\"/some/\") == \"/some/\".to_string());\n\n        // Correctly removes \"..\" segments.\n        assert!(normalize_path(\"/some/../another\") == \"/another\".to_string());\n        assert!(\n            normalize_path(\"/one/two/../three/../../four\")\n                == \"/four\".to_string()\n        );\n\n        // Correctly sets trailing slash if last segement is \"..\".\n        assert!(normalize_path(\"/one/two/..\") == \"/one/\".to_string());\n        assert!(normalize_path(\"/one/two/../\") == \"/one/\".to_string());\n\n        // Level outside of the url.\n        assert!(normalize_path(\"/..\") == \"/\".to_string());\n        assert!(normalize_path(\"/../\") == \"/\".to_string());\n\n        // Going into negative levels and coming back into the positives.\n        assert!(\n            normalize_path(\"/one/../../two/three\") == \"/two/three\".to_string()\n        );\n        assert!(\n            normalize_path(\"/one/../../two/three/\")\n                == \"/two/three/\".to_string()\n        );\n    }\n}\n"
  },
  {
    "path": "router/src/location/history.rs",
    "content": "use super::{handle_anchor_click, LocationChange, LocationProvider, Url};\nuse crate::{hooks::use_navigate, params::ParamsMap};\nuse core::fmt;\nuse futures::channel::oneshot;\nuse js_sys::{try_iter, Array, JsString};\nuse leptos::{ev, prelude::*};\nuse or_poisoned::OrPoisoned;\nuse reactive_graph::{\n    signal::ArcRwSignal,\n    traits::{ReadUntracked, Set},\n};\nuse std::{\n    borrow::Cow,\n    string::String,\n    sync::{Arc, Mutex},\n};\nuse tachys::dom::{document, window};\nuse wasm_bindgen::{JsCast, JsValue};\nuse web_sys::UrlSearchParams;\n\n#[derive(Clone)]\npub struct BrowserUrl {\n    url: ArcRwSignal<Url>,\n    pub(crate) pending_navigation: Arc<Mutex<Option<oneshot::Sender<()>>>>,\n    pub(crate) path_stack: ArcStoredValue<Vec<Url>>,\n    pub(crate) is_back: ArcRwSignal<bool>,\n}\n\nimpl fmt::Debug for BrowserUrl {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"BrowserUrl\").finish_non_exhaustive()\n    }\n}\n\nimpl BrowserUrl {\n    fn scroll_to_el(loc_scroll: bool) {\n        if let Ok(hash) = window().location().hash() {\n            if !hash.is_empty() {\n                let hash = js_sys::decode_uri(&hash[1..])\n                    .ok()\n                    .and_then(|decoded| decoded.as_string())\n                    .unwrap_or(hash);\n                let el = document().get_element_by_id(&hash);\n                if let Some(el) = el {\n                    el.scroll_into_view();\n                    return;\n                }\n            }\n        }\n\n        // scroll to top\n        if loc_scroll {\n            window().scroll_to_with_x_and_y(0.0, 0.0);\n        }\n    }\n}\n\nimpl LocationProvider for BrowserUrl {\n    type Error = JsValue;\n\n    fn new() -> Result<Self, JsValue> {\n        let url = ArcRwSignal::new(Self::current()?);\n        let path_stack = ArcStoredValue::new(\n            Self::current().map(|n| vec![n]).unwrap_or_default(),\n        );\n        Ok(Self {\n            url,\n            pending_navigation: Default::default(),\n            path_stack,\n            is_back: Default::default(),\n        })\n    }\n\n    fn as_url(&self) -> &ArcRwSignal<Url> {\n        &self.url\n    }\n\n    fn current() -> Result<Url, Self::Error> {\n        let location = window().location();\n        Ok(Url {\n            origin: location.origin()?,\n            path: location.pathname()?,\n            search: location\n                .search()?\n                .strip_prefix('?')\n                .map(String::from)\n                .unwrap_or_default(),\n            search_params: search_params_from_web_url(\n                &UrlSearchParams::new_with_str(&location.search()?)?,\n            )?,\n            hash: location.hash()?,\n        })\n    }\n\n    fn parse(url: &str) -> Result<Url, Self::Error> {\n        let base = window().location().origin()?;\n        Self::parse_with_base(url, &base)\n    }\n\n    fn parse_with_base(url: &str, base: &str) -> Result<Url, Self::Error> {\n        let location = web_sys::Url::new_with_base(url, base)?;\n        Ok(Url {\n            origin: location.origin(),\n            path: location.pathname(),\n            search: location\n                .search()\n                .strip_prefix('?')\n                .map(String::from)\n                .unwrap_or_default(),\n            search_params: search_params_from_web_url(\n                &location.search_params(),\n            )?,\n            hash: location.hash(),\n        })\n    }\n\n    fn init(&self, base: Option<Cow<'static, str>>) {\n        let navigate = {\n            let url = self.url.clone();\n            let pending = Arc::clone(&self.pending_navigation);\n            let this = self.clone();\n            move |new_url: Url, loc| {\n                let same_path = {\n                    let curr = url.read_untracked();\n                    curr.origin() == new_url.origin()\n                        && curr.path() == new_url.path()\n                };\n\n                url.set(new_url.clone());\n                if same_path {\n                    this.complete_navigation(&loc);\n                }\n                let pending = Arc::clone(&pending);\n                let (tx, rx) = oneshot::channel::<()>();\n                if !same_path {\n                    *pending.lock().or_poisoned() = Some(tx);\n                }\n                let url = url.clone();\n                let this = this.clone();\n                async move {\n                    if !same_path {\n                        // if it has been canceled, ignore\n                        // otherwise, complete navigation -- i.e., set URL in address bar\n                        if rx.await.is_ok() {\n                            // only update the URL in the browser if this is still the current URL\n                            // if we've navigated to another page in the meantime, don't update the\n                            // browser URL\n                            let curr = url.read_untracked();\n                            if curr == new_url {\n                                this.complete_navigation(&loc);\n                            }\n                        }\n                    }\n                }\n            }\n        };\n\n        let handle_anchor_click =\n            handle_anchor_click(base, Self::parse_with_base, navigate);\n\n        let click_handle = window_event_listener(ev::click, move |ev| {\n            if let Err(e) = handle_anchor_click(ev) {\n                #[cfg(feature = \"tracing\")]\n                tracing::error!(\"{e:?}\");\n                #[cfg(not(feature = \"tracing\"))]\n                web_sys::console::error_1(&e);\n            }\n        });\n\n        // handle popstate event (forward/back navigation)\n        let popstate_cb = {\n            let url = self.url.clone();\n            let path_stack = self.path_stack.clone();\n            let is_back = self.is_back.clone();\n            move || match Self::current() {\n                Ok(new_url) => {\n                    let mut stack = path_stack.write_value();\n                    let is_navigating_back = stack.len() == 1\n                        || (stack.len() >= 2\n                            && stack.get(stack.len() - 2) == Some(&new_url));\n\n                    if is_navigating_back {\n                        stack.pop();\n                    }\n\n                    is_back.set(is_navigating_back);\n\n                    url.set(new_url);\n                }\n                Err(e) => {\n                    #[cfg(feature = \"tracing\")]\n                    tracing::error!(\"{e:?}\");\n                    #[cfg(not(feature = \"tracing\"))]\n                    web_sys::console::error_1(&e);\n                }\n            }\n        };\n\n        let popstate_handle =\n            window_event_listener(ev::popstate, move |_| popstate_cb());\n\n        on_cleanup(|| {\n            click_handle.remove();\n            popstate_handle.remove();\n        });\n    }\n\n    fn ready_to_complete(&self) {\n        if let Some(tx) = self.pending_navigation.lock().or_poisoned().take() {\n            _ = tx.send(());\n        }\n    }\n\n    fn complete_navigation(&self, loc: &LocationChange) {\n        let history = window().history().unwrap();\n\n        let current_path = self\n            .path_stack\n            .read_value()\n            .last()\n            .map(|url| url.to_full_path());\n        let add_to_stack = current_path.as_ref() != Some(&loc.value);\n\n        if loc.replace {\n            history\n                .replace_state_with_url(\n                    &loc.state.to_js_value(),\n                    \"\",\n                    Some(&loc.value),\n                )\n                .unwrap();\n        } else if add_to_stack {\n            // push the \"forward direction\" marker\n            let state = &loc.state.to_js_value();\n            history\n                .push_state_with_url(state, \"\", Some(&loc.value))\n                .unwrap();\n        }\n\n        // add this URL to the \"path stack\" for detecting back navigations, and\n        // unset \"navigating back\" state\n        if let Ok(url) = Self::current() {\n            if add_to_stack {\n                self.path_stack.write_value().push(url);\n            }\n            self.is_back.set(false);\n        }\n\n        // scroll to el\n        Self::scroll_to_el(loc.scroll);\n    }\n\n    fn redirect(loc: &str) {\n        let navigate = use_navigate();\n        let Some(url) = resolve_redirect_url(loc) else {\n            return; // resolve_redirect_url() already logs an error\n        };\n        let current_origin = location().origin().unwrap();\n        if url.origin() == current_origin {\n            let navigate = navigate.clone();\n            // delay by a tick here, so that the Action updates *before* the redirect\n            request_animation_frame(move || {\n                navigate(&url.href(), Default::default());\n            });\n            // Use set_href() if the conditions for client-side navigation were not satisfied\n        } else if let Err(e) = location().set_href(&url.href()) {\n            leptos::logging::error!(\"Failed to redirect: {e:#?}\");\n        }\n    }\n\n    fn is_back(&self) -> ReadSignal<bool> {\n        self.is_back.read_only().into()\n    }\n}\n\nfn search_params_from_web_url(\n    params: &web_sys::UrlSearchParams,\n) -> Result<ParamsMap, JsValue> {\n    try_iter(params)?\n        .into_iter()\n        .flatten()\n        .map(|pair| {\n            pair.and_then(|pair| {\n                let row = pair.dyn_into::<Array>()?;\n                Ok((\n                    String::from(row.get(0).dyn_into::<JsString>()?),\n                    String::from(row.get(1).dyn_into::<JsString>()?),\n                ))\n            })\n        })\n        .collect()\n}\n\n/// Resolves a redirect location to an (absolute) URL.\npub(crate) fn resolve_redirect_url(loc: &str) -> Option<web_sys::Url> {\n    let origin = match window().location().origin() {\n        Ok(origin) => origin,\n        Err(e) => {\n            leptos::logging::error!(\"Failed to get origin: {:#?}\", e);\n            return None;\n        }\n    };\n\n    // TODO: Use server function's URL as base instead.\n    let base = origin;\n\n    match web_sys::Url::new_with_base(loc, &base) {\n        Ok(url) => Some(url),\n        Err(e) => {\n            leptos::logging::error!(\n                \"Invalid redirect location: {}\",\n                e.as_string().unwrap_or_default(),\n            );\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "router/src/location/mod.rs",
    "content": "#![allow(missing_docs)]\n\nuse any_spawner::Executor;\nuse core::fmt::Debug;\nuse js_sys::Reflect;\nuse leptos::server::ServerActionError;\nuse reactive_graph::{\n    computed::Memo,\n    owner::provide_context,\n    signal::{ArcRwSignal, ReadSignal},\n    traits::With,\n};\nuse send_wrapper::SendWrapper;\nuse std::{borrow::Cow, future::Future};\nuse tachys::dom::window;\nuse wasm_bindgen::{JsCast, JsValue};\nuse web_sys::{HtmlAnchorElement, MouseEvent};\n\nmod history;\nmod server;\nuse crate::params::ParamsMap;\npub use history::*;\npub use server::*;\n\npub(crate) const BASE: &str = \"https://leptos.dev\";\n\n#[derive(Debug, Default, Clone, PartialEq, Eq)]\npub struct Url {\n    origin: String,\n    path: String,\n    search: String,\n    search_params: ParamsMap,\n    hash: String,\n}\n\nimpl Url {\n    pub fn origin(&self) -> &str {\n        &self.origin\n    }\n\n    pub fn origin_mut(&mut self) -> &mut String {\n        &mut self.origin\n    }\n\n    pub fn path(&self) -> &str {\n        &self.path\n    }\n\n    pub fn path_mut(&mut self) -> &mut str {\n        &mut self.path\n    }\n\n    pub fn search(&self) -> &str {\n        &self.search\n    }\n\n    pub fn search_mut(&mut self) -> &mut String {\n        &mut self.search\n    }\n\n    pub fn search_params(&self) -> &ParamsMap {\n        &self.search_params\n    }\n\n    pub fn search_params_mut(&mut self) -> &mut ParamsMap {\n        &mut self.search_params\n    }\n\n    pub fn hash(&self) -> &str {\n        #[cfg(all(feature = \"ssr\", any(debug_assertions, leptos_debuginfo)))]\n        {\n            #[cfg(feature = \"tracing\")]\n            tracing::warn!(\n                \"Reading hash on the server can lead to hydration errors.\"\n            );\n            #[cfg(not(feature = \"tracing\"))]\n            eprintln!(\n                \"Reading hash on the server can lead to hydration errors.\"\n            );\n        }\n        &self.hash\n    }\n\n    pub fn hash_mut(&mut self) -> &mut String {\n        #[cfg(all(feature = \"ssr\", any(debug_assertions, leptos_debuginfo)))]\n        {\n            #[cfg(feature = \"tracing\")]\n            tracing::warn!(\n                \"Reading hash on the server can lead to hydration errors.\"\n            );\n            #[cfg(not(feature = \"tracing\"))]\n            eprintln!(\n                \"Reading hash on the server can lead to hydration errors.\"\n            );\n        }\n        &mut self.hash\n    }\n\n    pub fn provide_server_action_error(&self) {\n        let search_params = self.search_params();\n        if let (Some(err), Some(path)) = (\n            search_params.get_str(\"__err\"),\n            search_params.get_str(\"__path\"),\n        ) {\n            provide_context(ServerActionError::new(path, err))\n        }\n    }\n\n    pub(crate) fn to_full_path(&self) -> String {\n        let mut path = self.path.to_string();\n        if !self.search.is_empty() {\n            path.push('?');\n            path.push_str(&self.search);\n        }\n        if !self.hash.is_empty() {\n            if !self.hash.starts_with('#') {\n                path.push('#');\n            }\n            path.push_str(&self.hash);\n        }\n        path\n    }\n\n    pub fn escape(s: &str) -> String {\n        #[cfg(not(feature = \"ssr\"))]\n        {\n            js_sys::encode_uri_component(s).as_string().unwrap()\n        }\n        #[cfg(feature = \"ssr\")]\n        {\n            percent_encoding::utf8_percent_encode(\n                s,\n                percent_encoding::NON_ALPHANUMERIC,\n            )\n            .to_string()\n        }\n    }\n\n    pub fn unescape(s: &str) -> String {\n        #[cfg(feature = \"ssr\")]\n        {\n            percent_encoding::percent_decode_str(s)\n                .decode_utf8()\n                .unwrap()\n                .to_string()\n        }\n\n        #[cfg(not(feature = \"ssr\"))]\n        {\n            match js_sys::decode_uri_component(s) {\n                Ok(v) => v.into(),\n                Err(_) => s.into(),\n            }\n        }\n    }\n\n    pub fn unescape_minimal(s: &str) -> String {\n        #[cfg(not(feature = \"ssr\"))]\n        {\n            match js_sys::decode_uri(s) {\n                Ok(v) => v.into(),\n                Err(_) => s.into(),\n            }\n        }\n\n        #[cfg(feature = \"ssr\")]\n        {\n            Self::unescape(s)\n        }\n    }\n}\n\n/// A reactive description of the current URL, containing equivalents to the local parts of\n/// the browser's [`Location`](https://developer.mozilla.org/en-US/docs/Web/API/Location).\n#[derive(Debug, Clone, PartialEq)]\npub struct Location {\n    /// The path of the URL, not containing the query string or hash fragment.\n    pub pathname: Memo<String>,\n    /// The raw query string.\n    pub search: Memo<String>,\n    /// The query string parsed into its key-value pairs.\n    pub query: Memo<ParamsMap>,\n    /// The hash fragment.\n    pub hash: Memo<String>,\n    /// The [`state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state) at the top of the history stack.\n    pub state: ReadSignal<State>,\n}\n\nimpl Location {\n    pub(crate) fn new(\n        url: impl Into<ReadSignal<Url>>,\n        state: impl Into<ReadSignal<State>>,\n    ) -> Self {\n        let url = url.into();\n        let state = state.into();\n        let pathname = Memo::new(move |_| url.with(|url| url.path.clone()));\n        let search = Memo::new(move |_| url.with(|url| url.search.clone()));\n        let hash = Memo::new(move |_| url.with(|url| url.hash().to_string()));\n        let query =\n            Memo::new(move |_| url.with(|url| url.search_params.clone()));\n        Location {\n            pathname,\n            search,\n            query,\n            hash,\n            state,\n        }\n    }\n}\n\n/// A description of a navigation.\n#[derive(Debug, Clone, PartialEq)]\npub struct LocationChange {\n    /// The new URL.\n    pub value: String,\n    /// If true, the new location will replace the current one in the history stack, i.e.,\n    /// clicking the \"back\" button will not return to the current location.\n    pub replace: bool,\n    /// If true, the router will scroll to the top of the page at the end of the navigation.\n    pub scroll: bool,\n    /// The [`state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state) that will be added during navigation.\n    pub state: State,\n}\n\nimpl Default for LocationChange {\n    fn default() -> Self {\n        Self {\n            value: Default::default(),\n            replace: true,\n            scroll: true,\n            state: Default::default(),\n        }\n    }\n}\n\npub trait LocationProvider: Clone + 'static {\n    type Error: Debug;\n\n    fn new() -> Result<Self, Self::Error>;\n\n    fn as_url(&self) -> &ArcRwSignal<Url>;\n\n    fn current() -> Result<Url, Self::Error>;\n\n    /// Sets up any global event listeners or other initialization needed.\n    fn init(&self, base: Option<Cow<'static, str>>);\n\n    /// Should be called after a navigation when all route components and data have been loaded and\n    /// the URL can be updated.\n    fn ready_to_complete(&self);\n\n    /// Update the browser's history to reflect a new location.\n    fn complete_navigation(&self, loc: &LocationChange);\n\n    fn parse(url: &str) -> Result<Url, Self::Error> {\n        Self::parse_with_base(url, BASE)\n    }\n\n    fn parse_with_base(url: &str, base: &str) -> Result<Url, Self::Error>;\n\n    fn redirect(loc: &str);\n\n    /// Whether we are currently in a \"back\" navigation.\n    fn is_back(&self) -> ReadSignal<bool>;\n}\n\n#[derive(Debug, Clone, Default)]\npub struct State(Option<SendWrapper<JsValue>>);\n\nimpl State {\n    pub fn new(state: Option<JsValue>) -> Self {\n        Self(state.map(SendWrapper::new))\n    }\n\n    pub fn to_js_value(&self) -> JsValue {\n        match &self.0 {\n            Some(v) => v.clone().take(),\n            None => JsValue::UNDEFINED,\n        }\n    }\n}\n\nimpl PartialEq for State {\n    fn eq(&self, other: &Self) -> bool {\n        self.0.as_ref().map(|n| n.as_ref())\n            == other.0.as_ref().map(|n| n.as_ref())\n    }\n}\n\nimpl<T> From<T> for State\nwhere\n    T: Into<JsValue>,\n{\n    fn from(value: T) -> Self {\n        State::new(Some(value.into()))\n    }\n}\n\npub(crate) fn handle_anchor_click<NavFn, NavFut>(\n    router_base: Option<Cow<'static, str>>,\n    parse_with_base: fn(&str, &str) -> Result<Url, JsValue>,\n    navigate: NavFn,\n) -> Box<dyn Fn(MouseEvent) -> Result<(), JsValue>>\nwhere\n    NavFn: Fn(Url, LocationChange) -> NavFut + 'static,\n    NavFut: Future<Output = ()> + 'static,\n{\n    let router_base = router_base.unwrap_or_default();\n\n    Box::new(move |ev: MouseEvent| {\n        let origin = window().location().origin()?;\n        if ev.default_prevented()\n            || ev.button() != 0\n            || ev.meta_key()\n            || ev.alt_key()\n            || ev.ctrl_key()\n            || ev.shift_key()\n        {\n            return Ok(());\n        }\n\n        let composed_path = ev.composed_path();\n        let mut a: Option<HtmlAnchorElement> = None;\n        for i in 0..composed_path.length() {\n            if let Ok(el) = composed_path.get(i).dyn_into::<HtmlAnchorElement>()\n            {\n                a = Some(el);\n            }\n        }\n        if let Some(a) = a {\n            let href = a.href();\n            let target = a.target();\n\n            // let browser handle this event if link has target,\n            // or if it doesn't have href or state\n            // TODO \"state\" is set as a prop, not an attribute\n            if !target.is_empty()\n                || (href.is_empty() && !a.has_attribute(\"state\"))\n            {\n                return Ok(());\n            }\n\n            let rel = a.get_attribute(\"rel\").unwrap_or_default();\n            let mut rel = rel.split([' ', '\\t']);\n\n            // let browser handle event if it has rel=external or download\n            if a.has_attribute(\"download\") || rel.any(|p| p == \"external\") {\n                return Ok(());\n            }\n\n            let url = parse_with_base(href.as_str(), &origin).unwrap();\n            let path_name = Url::unescape_minimal(&url.path);\n\n            // let browser handle this event if it leaves our domain\n            // or our base path\n            if url.origin != origin\n                || (!router_base.is_empty()\n                    && !path_name.is_empty()\n                    // NOTE: the two `to_lowercase()` calls here added a total of about 14kb to\n                    // release binary size, for limited gain\n                    && !path_name.starts_with(&*router_base))\n            {\n                return Ok(());\n            }\n\n            // we've passed all the checks to navigate on the client side, so we prevent the\n            // default behavior of the click\n            ev.prevent_default();\n            let to = path_name\n                + if url.search.is_empty() { \"\" } else { \"?\" }\n                + &url.search\n                + &url.hash;\n            let state = Reflect::get(&a, &JsValue::from_str(\"state\"))\n                .ok()\n                .and_then(|value| {\n                    if value == JsValue::UNDEFINED {\n                        None\n                    } else {\n                        Some(value)\n                    }\n                });\n\n            let replace = Reflect::get(&a, &JsValue::from_str(\"replace\"))\n                .ok()\n                .and_then(|value| value.as_bool())\n                .unwrap_or(false);\n\n            let change = LocationChange {\n                value: to,\n                replace,\n                scroll: !a.has_attribute(\"noscroll\")\n                    && !a.has_attribute(\"data-noscroll\"),\n                state: State::new(state),\n            };\n\n            Executor::spawn_local(navigate(url, change));\n        }\n\n        Ok(())\n    })\n}\n"
  },
  {
    "path": "router/src/location/server.rs",
    "content": "use super::{Url, BASE};\nuse crate::params::ParamsMap;\nuse std::sync::Arc;\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct RequestUrl(Arc<str>);\n\nimpl RequestUrl {\n    /// Creates a server-side request URL from a path.\n    pub fn new(path: &str) -> Self {\n        Self(path.into())\n    }\n}\n\nimpl AsRef<str> for RequestUrl {\n    fn as_ref(&self) -> &str {\n        &self.0\n    }\n}\n\nimpl Default for RequestUrl {\n    fn default() -> Self {\n        Self::new(\"/\")\n    }\n}\n\nimpl RequestUrl {\n    pub fn parse(&self) -> Result<Url, url::ParseError> {\n        self.parse_with_base(BASE)\n    }\n\n    pub fn parse_with_base(&self, base: &str) -> Result<Url, url::ParseError> {\n        let base = url::Url::parse(base)?;\n        let url = url::Url::options().base_url(Some(&base)).parse(&self.0)?;\n\n        let search_params = url\n            .query_pairs()\n            .map(|(k, v)| (k.to_string(), v.to_string()))\n            .collect::<ParamsMap>();\n\n        Ok(Url {\n            origin: url.origin().unicode_serialization(),\n            path: url.path().to_string(),\n            search: url.query().unwrap_or_default().to_string(),\n            search_params,\n            hash: Default::default(),\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::RequestUrl;\n\n    #[test]\n    pub fn should_parse_url_without_origin() {\n        let url = RequestUrl::new(\"/foo/bar\").parse().unwrap();\n        assert_eq!(url.path(), \"/foo/bar\");\n    }\n\n    #[test]\n    pub fn should_not_parse_url_without_slash() {\n        let url = RequestUrl::new(\"foo/bar\").parse().unwrap();\n        assert_eq!(url.path(), \"/foo/bar\");\n    }\n\n    #[test]\n    pub fn should_parse_with_base() {\n        let url = RequestUrl::new(\"https://www.example.com/foo/bar\")\n            .parse()\n            .unwrap();\n        assert_eq!(url.origin(), \"https://www.example.com\");\n        assert_eq!(url.path(), \"/foo/bar\");\n    }\n}\n"
  },
  {
    "path": "router/src/matching/any_choose_view.rs",
    "content": "use super::ChooseView;\nuse futures::FutureExt;\nuse std::{future::Future, pin::Pin};\nuse tachys::{erased::Erased, view::any_view::AnyView};\n\n/// A type-erased [`ChooseView`].\npub struct AnyChooseView {\n    value: Erased,\n    clone: fn(&Erased) -> AnyChooseView,\n    #[allow(clippy::type_complexity)]\n    choose: fn(Erased) -> Pin<Box<dyn Future<Output = AnyView>>>,\n    preload: for<'a> fn(&'a Erased) -> Pin<Box<dyn Future<Output = ()> + 'a>>,\n}\n\nimpl Clone for AnyChooseView {\n    fn clone(&self) -> Self {\n        (self.clone)(&self.value)\n    }\n}\n\nimpl AnyChooseView {\n    pub(crate) fn new<T: ChooseView>(value: T) -> Self {\n        fn clone<T: ChooseView>(value: &Erased) -> AnyChooseView {\n            AnyChooseView::new(value.get_ref::<T>().clone())\n        }\n\n        fn choose<T: ChooseView>(\n            value: Erased,\n        ) -> Pin<Box<dyn Future<Output = AnyView>>> {\n            value.into_inner::<T>().choose().boxed_local()\n        }\n\n        fn preload<'a, T: ChooseView>(\n            value: &'a Erased,\n        ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {\n            value.get_ref::<T>().preload().boxed_local()\n        }\n\n        Self {\n            value: Erased::new(value),\n            clone: clone::<T>,\n            choose: choose::<T>,\n            preload: preload::<T>,\n        }\n    }\n}\n\nimpl ChooseView for AnyChooseView {\n    async fn choose(self) -> AnyView {\n        (self.choose)(self.value).await\n    }\n\n    async fn preload(&self) {\n        (self.preload)(&self.value).await;\n    }\n}\n"
  },
  {
    "path": "router/src/matching/choose_view.rs",
    "content": "use either_of::*;\nuse leptos::prelude::{ArcStoredValue, WriteValue};\nuse std::{future::Future, marker::PhantomData};\nuse tachys::view::any_view::{AnyView, IntoAny};\n\npub trait ChooseView\nwhere\n    Self: Send + Clone + 'static,\n{\n    fn choose(self) -> impl Future<Output = AnyView>;\n\n    fn preload(&self) -> impl Future<Output = ()>;\n}\n\nimpl<F, View> ChooseView for F\nwhere\n    F: Fn() -> View + Send + Clone + 'static,\n    View: IntoAny,\n{\n    async fn choose(self) -> AnyView {\n        self().into_any()\n    }\n\n    async fn preload(&self) {}\n}\n\nimpl<T> ChooseView for Lazy<T>\nwhere\n    T: Send + Sync + LazyRoute,\n{\n    async fn choose(self) -> AnyView {\n        let data = self.data.write_value().take().unwrap_or_else(T::data);\n        T::view(data).await\n    }\n\n    async fn preload(&self) {\n        *self.data.write_value() = Some(T::data());\n        T::preload().await;\n    }\n}\n\npub trait LazyRoute: Send + 'static {\n    fn data() -> Self;\n\n    fn view(this: Self) -> impl Future<Output = AnyView>;\n\n    fn preload() -> impl Future<Output = ()> {\n        async {}\n    }\n}\n\n#[derive(Debug)]\npub struct Lazy<T> {\n    ty: PhantomData<T>,\n    data: ArcStoredValue<Option<T>>,\n}\n\nimpl<T> Clone for Lazy<T> {\n    fn clone(&self) -> Self {\n        Self {\n            ty: self.ty,\n            data: self.data.clone(),\n        }\n    }\n}\n\nimpl<T> Lazy<T> {\n    pub fn new() -> Self {\n        Self::default()\n    }\n}\n\nimpl<T> Default for Lazy<T> {\n    fn default() -> Self {\n        Self {\n            ty: Default::default(),\n            data: ArcStoredValue::new(None),\n        }\n    }\n}\n\nimpl ChooseView for () {\n    async fn choose(self) -> AnyView {\n        ().into_any()\n    }\n\n    async fn preload(&self) {}\n}\n\nimpl<A, B> ChooseView for Either<A, B>\nwhere\n    A: ChooseView,\n    B: ChooseView,\n{\n    async fn choose(self) -> AnyView {\n        match self {\n            Either::Left(f) => f.choose().await.into_any(),\n            Either::Right(f) => f.choose().await.into_any(),\n        }\n    }\n\n    async fn preload(&self) {\n        match self {\n            Either::Left(f) => f.preload().await,\n            Either::Right(f) => f.preload().await,\n        }\n    }\n}\n\nmacro_rules! tuples {\n    ($either:ident => $($ty:ident),*) => {\n        impl<$($ty,)*> ChooseView for $either<$($ty,)*>\n        where\n            $($ty: ChooseView,)*\n        {\n            async fn choose(self) -> AnyView {\n                match self {\n                    $(\n                        $either::$ty(f) => f.choose().await.into_any(),\n                    )*\n                }\n            }\n\n            async fn preload(&self) {\n                match self {\n                    $($either::$ty(f) => f.preload().await,)*\n                }\n            }\n        }\n    };\n}\n\ntuples!(EitherOf3 => A, B, C);\ntuples!(EitherOf4 => A, B, C, D);\ntuples!(EitherOf5 => A, B, C, D, E);\ntuples!(EitherOf6 => A, B, C, D, E, F);\ntuples!(EitherOf7 => A, B, C, D, E, F, G);\ntuples!(EitherOf8 => A, B, C, D, E, F, G, H);\ntuples!(EitherOf9 => A, B, C, D, E, F, G, H, I);\ntuples!(EitherOf10 => A, B, C, D, E, F, G, H, I, J);\ntuples!(EitherOf11 => A, B, C, D, E, F, G, H, I, J, K);\ntuples!(EitherOf12 => A, B, C, D, E, F, G, H, I, J, K, L);\ntuples!(EitherOf13 => A, B, C, D, E, F, G, H, I, J, K, L, M);\ntuples!(EitherOf14 => A, B, C, D, E, F, G, H, I, J, K, L, M, N);\ntuples!(EitherOf15 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);\ntuples!(EitherOf16 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);\n\n/// A version of [`IntoMaybeErased`] for the [`ChooseView`] trait.\npub trait IntoChooseViewMaybeErased {\n    /// The type of the erased view.\n    type Output: IntoChooseViewMaybeErased;\n\n    /// Erase the type of the view.\n    fn into_maybe_erased(self) -> Self::Output;\n}\n\nimpl<T> IntoChooseViewMaybeErased for T\nwhere\n    T: ChooseView + Send + Clone + 'static,\n{\n    #[cfg(erase_components)]\n    type Output = crate::matching::any_choose_view::AnyChooseView;\n\n    #[cfg(not(erase_components))]\n    type Output = Self;\n\n    fn into_maybe_erased(self) -> Self::Output {\n        #[cfg(erase_components)]\n        {\n            crate::matching::any_choose_view::AnyChooseView::new(self)\n        }\n        #[cfg(not(erase_components))]\n        {\n            self\n        }\n    }\n}\n"
  },
  {
    "path": "router/src/matching/horizontal/mod.rs",
    "content": "use super::{PartialPathMatch, PathSegment};\nuse std::sync::Arc;\nmod param_segments;\nmod static_segment;\nmod tuples;\npub use param_segments::*;\npub use static_segment::*;\n\n/// Defines a route which may or may not be matched by any given URL,\n/// or URL segment.\n///\n/// This is a \"horizontal\" matching: i.e., it treats a tuple of route segments\n/// as subsequent segments of the URL and tries to match them all.\npub trait PossibleRouteMatch {\n    fn optional(&self) -> bool;\n\n    /// Checks if this segment matches beginning of the path\n    ///\n    ///\n    /// # Arguments\n    ///\n    /// * path - unmatched reminder of the path.\n    ///\n    /// # Returns\n    ///\n    /// If segment doesn't match a path then returns `None`. In case of a match returns the\n    /// information about which part of the path was matched.\n    ///\n    /// 1. Paths which are empty `\"\"` or just `\"/\"` should match.\n    /// 2. If you match just a path `\"/\"`, you should preserve the starting slash\n    ///    in the [remaining](PartialPathMatch::remaining) part, so other segments which will be\n    ///    tested can detect wherever they are matching from the beginning of the given path segment.\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>>;\n\n    fn generate_path(&self, path: &mut Vec<PathSegment>);\n}\n\nimpl PossibleRouteMatch for Box<dyn PossibleRouteMatch + Send + Sync> {\n    fn optional(&self) -> bool {\n        (**self).optional()\n    }\n\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n        (**self).test(path)\n    }\n\n    fn generate_path(&self, path: &mut Vec<PathSegment>) {\n        (**self).generate_path(path);\n    }\n}\n\nimpl PossibleRouteMatch for Arc<dyn PossibleRouteMatch + Send + Sync> {\n    fn optional(&self) -> bool {\n        (**self).optional()\n    }\n\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n        (**self).test(path)\n    }\n\n    fn generate_path(&self, path: &mut Vec<PathSegment>) {\n        (**self).generate_path(path);\n    }\n}\n"
  },
  {
    "path": "router/src/matching/horizontal/param_segments.rs",
    "content": "use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};\nuse core::iter;\nuse std::borrow::Cow;\n\n/// A segment that captures a value from the url and maps it to a key.\n///\n/// # Examples\n/// ```rust\n/// # (|| -> Option<()> { // Option does not impl Terminate, so no main\n/// use leptos::prelude::*;\n/// use leptos_router::{path, ParamSegment, PossibleRouteMatch};\n///\n/// let path = &\"/hello\";\n///\n/// // Manual definition\n/// let manual = (ParamSegment(\"message\"),);\n/// let params = manual.test(path)?.params();\n/// let (key, value) = params.last()?;\n///\n/// assert_eq!(key, \"message\");\n/// assert_eq!(value, \"hello\");\n///\n/// // Macro definition\n/// let using_macro = path!(\"/:message\");\n/// let params = using_macro.test(path)?.params();\n/// let (key, value) = params.last()?;\n///\n/// assert_eq!(key, \"message\");\n/// assert_eq!(value, \"hello\");\n///\n/// # Some(())\n/// # })().unwrap();\n/// ```\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct ParamSegment(pub &'static str);\n\nimpl PossibleRouteMatch for ParamSegment {\n    fn optional(&self) -> bool {\n        false\n    }\n\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n        let mut matched_len = 0;\n        let mut param_offset = 0;\n        let mut param_len = 0;\n        let mut test = path.chars();\n\n        // match an initial /\n        if let Some('/') = test.next() {\n            matched_len += 1;\n            param_offset = 1;\n        }\n        for char in test {\n            // when we get a closing /, stop matching\n            if char == '/' {\n                break;\n            }\n            // otherwise, push into the matched param\n            else {\n                matched_len += char.len_utf8();\n                param_len += char.len_utf8();\n            }\n        }\n\n        if matched_len == 0 || (matched_len == 1 && path.starts_with('/')) {\n            return None;\n        }\n\n        let (matched, remaining) = path.split_at(matched_len);\n        let param_value = vec![(\n            Cow::Borrowed(self.0),\n            path[param_offset..param_len + param_offset].to_string(),\n        )];\n        Some(PartialPathMatch::new(remaining, param_value, matched))\n    }\n\n    fn generate_path(&self, path: &mut Vec<PathSegment>) {\n        path.push(PathSegment::Param(self.0.into()));\n    }\n}\n\n/// A segment that captures all remaining values from the url and maps it to a key.\n///\n/// A [`WildcardSegment`] __must__ be the last segment of your path definition.\n///\n/// ```rust\n/// # (|| -> Option<()> { // Option does not impl Terminate, so no main\n/// use leptos::prelude::*;\n/// use leptos_router::{\n///     path, ParamSegment, PossibleRouteMatch, StaticSegment, WildcardSegment,\n/// };\n///\n/// let path = &\"/echo/send/sync/and/static\";\n///\n/// // Manual definition\n/// let manual = (StaticSegment(\"echo\"), WildcardSegment(\"kitchen_sink\"));\n/// let params = manual.test(path)?.params();\n/// let (key, value) = params.last()?;\n///\n/// assert_eq!(key, \"kitchen_sink\");\n/// assert_eq!(value, \"send/sync/and/static\");\n///\n/// // Macro definition\n/// let using_macro = path!(\"/echo/*else\");\n/// let params = using_macro.test(path)?.params();\n/// let (key, value) = params.last()?;\n///\n/// assert_eq!(key, \"else\");\n/// assert_eq!(value, \"send/sync/and/static\");\n///\n/// // This fails to compile because the macro will catch the bad ordering\n/// // let bad = path!(\"/echo/*foo/bar/:baz\");\n///\n/// // This compiles but may not work as you expect at runtime.\n/// (\n///     StaticSegment(\"echo\"),\n///     WildcardSegment(\"foo\"),\n///     ParamSegment(\"baz\"),\n/// );\n///\n/// # Some(())\n/// # })().unwrap();\n/// ```\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct WildcardSegment(pub &'static str);\n\nimpl PossibleRouteMatch for WildcardSegment {\n    fn optional(&self) -> bool {\n        false\n    }\n\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n        let mut matched_len = 0;\n        let mut param_offset = 0;\n        let mut param_len = 0;\n        let mut test = path.chars();\n\n        // match an initial /\n        if let Some('/') = test.next() {\n            matched_len += 1;\n            param_offset += 1;\n        }\n        for char in test {\n            matched_len += char.len_utf8();\n            param_len += char.len_utf8();\n        }\n\n        let (matched, remaining) = path.split_at(matched_len);\n        let param_value = iter::once((\n            Cow::Borrowed(self.0),\n            path[param_offset..param_len + param_offset].to_string(),\n        ));\n        Some(PartialPathMatch::new(\n            remaining,\n            param_value.into_iter().collect(),\n            matched,\n        ))\n    }\n\n    fn generate_path(&self, path: &mut Vec<PathSegment>) {\n        path.push(PathSegment::Splat(self.0.into()));\n    }\n}\n\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct OptionalParamSegment(pub &'static str);\n\nimpl PossibleRouteMatch for OptionalParamSegment {\n    fn optional(&self) -> bool {\n        true\n    }\n\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n        let mut matched_len = 0;\n        let mut param_offset = 0;\n        let mut param_len = 0;\n        let mut test = path.chars();\n\n        // match an initial /\n        if let Some('/') = test.next() {\n            matched_len += 1;\n            param_offset = 1;\n        }\n        for char in test {\n            // when we get a closing /, stop matching\n            if char == '/' {\n                break;\n            }\n            // otherwise, push into the matched param\n            else {\n                matched_len += char.len_utf8();\n                param_len += char.len_utf8();\n            }\n        }\n\n        let matched_len = if matched_len == 1 && path.starts_with('/') {\n            0\n        } else {\n            matched_len\n        };\n        let (matched, remaining) = path.split_at(matched_len);\n        let param_value = (matched_len > 0)\n            .then(|| {\n                (\n                    Cow::Borrowed(self.0),\n                    path[param_offset..param_len + param_offset].to_string(),\n                )\n            })\n            .into_iter()\n            .collect();\n        Some(PartialPathMatch::new(remaining, param_value, matched))\n    }\n\n    fn generate_path(&self, path: &mut Vec<PathSegment>) {\n        path.push(PathSegment::OptionalParam(self.0.into()));\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::PossibleRouteMatch;\n    use crate::{\n        OptionalParamSegment, ParamSegment, StaticSegment, WildcardSegment,\n    };\n\n    #[test]\n    fn single_param_match() {\n        let path = \"/foo\";\n        let def = ParamSegment(\"a\");\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"a\".into(), \"foo\".into()));\n    }\n\n    #[test]\n    fn single_param_match_with_trailing_slash() {\n        let path = \"/foo/\";\n        let def = ParamSegment(\"a\");\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo\");\n        assert_eq!(matched.remaining(), \"/\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"a\".into(), \"foo\".into()));\n    }\n\n    #[test]\n    fn tuple_of_param_matches() {\n        let path = \"/foo/bar\";\n        let def = (ParamSegment(\"a\"), ParamSegment(\"b\"));\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"a\".into(), \"foo\".into()));\n        assert_eq!(params[1], (\"b\".into(), \"bar\".into()));\n    }\n\n    #[test]\n    fn splat_should_match_all() {\n        let path = \"/foo/bar/////\";\n        let def = (\n            StaticSegment(\"foo\"),\n            StaticSegment(\"bar\"),\n            WildcardSegment(\"rest\"),\n        );\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar/////\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"rest\".into(), \"////\".into()));\n    }\n\n    #[test]\n    fn optional_param_can_match() {\n        let path = \"/foo\";\n        let def = OptionalParamSegment(\"a\");\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"a\".into(), \"foo\".into()));\n    }\n\n    #[test]\n    fn optional_param_can_not_match() {\n        let path = \"/\";\n        let def = OptionalParamSegment(\"a\");\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"\");\n        assert_eq!(matched.remaining(), \"/\");\n        let params = matched.params();\n        assert_eq!(params.first(), None);\n    }\n\n    #[test]\n    fn optional_params_match_first() {\n        let path = \"/foo\";\n        let def = (OptionalParamSegment(\"a\"), OptionalParamSegment(\"b\"));\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"a\".into(), \"foo\".into()));\n    }\n\n    #[test]\n    fn optional_params_can_match_both() {\n        let path = \"/foo/bar\";\n        let def = (OptionalParamSegment(\"a\"), OptionalParamSegment(\"b\"));\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"a\".into(), \"foo\".into()));\n        assert_eq!(params[1], (\"b\".into(), \"bar\".into()));\n    }\n\n    #[test]\n    fn matching_after_optional_param() {\n        let path = \"/bar\";\n        let def = (OptionalParamSegment(\"a\"), StaticSegment(\"bar\"));\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn static_before_param() {\n        let path = \"/foo/bar\";\n        let def = (StaticSegment(\"foo\"), ParamSegment(\"b\"));\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"b\".into(), \"bar\".into()));\n    }\n\n    #[test]\n    fn static_before_optional_param() {\n        let path = \"/foo/bar\";\n        let def = (StaticSegment(\"foo\"), OptionalParamSegment(\"b\"));\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"b\".into(), \"bar\".into()));\n    }\n\n    #[test]\n    fn multiple_optional_params_match_first() {\n        let path = \"/foo/bar\";\n        let def = (\n            OptionalParamSegment(\"a\"),\n            OptionalParamSegment(\"b\"),\n            StaticSegment(\"bar\"),\n        );\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"a\".into(), \"foo\".into()));\n    }\n\n    #[test]\n    fn multiple_optionals_can_match_both() {\n        let path = \"/foo/qux/bar\";\n        let def = (\n            OptionalParamSegment(\"a\"),\n            OptionalParamSegment(\"b\"),\n            StaticSegment(\"bar\"),\n        );\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/qux/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert_eq!(params[0], (\"a\".into(), \"foo\".into()));\n        assert_eq!(params[1], (\"b\".into(), \"qux\".into()));\n    }\n}\n"
  },
  {
    "path": "router/src/matching/horizontal/static_segment.rs",
    "content": "use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};\nuse std::fmt::Debug;\n\nimpl PossibleRouteMatch for () {\n    fn optional(&self) -> bool {\n        false\n    }\n\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n        Some(PartialPathMatch::new(path, vec![], \"\"))\n    }\n\n    fn generate_path(&self, _path: &mut Vec<PathSegment>) {}\n}\n\npub trait AsPath {\n    fn as_path(&self) -> &'static str;\n}\n\nimpl AsPath for &'static str {\n    fn as_path(&self) -> &'static str {\n        self\n    }\n}\n\n/// A segment that is expected to be static. Not requiring mapping into params.\n///\n/// Should work exactly as you would expect.\n///\n/// # Examples\n/// ```rust\n/// # (|| -> Option<()> { // Option does not impl Terminate, so no main\n/// use leptos::prelude::*;\n/// use leptos_router::{path, PossibleRouteMatch, StaticSegment};\n///\n/// let path = &\"/users\";\n///\n/// // Manual definition\n/// let manual = (StaticSegment(\"users\"),);\n/// let matched = manual.test(path)?;\n/// assert_eq!(matched.matched(), \"/users\");\n///\n/// // Params are empty as we had no `ParamSegement`s or `WildcardSegment`s\n/// // If you did have additional dynamic segments, this would not be empty.\n/// assert_eq!(matched.params().len(), 0);\n///\n/// // Macro definition\n/// let using_macro = path!(\"/users\");\n/// let matched = manual.test(path)?;\n/// assert_eq!(matched.matched(), \"/users\");\n///\n/// assert_eq!(matched.params().len(), 0);\n///\n/// # Some(())\n/// # })().unwrap();\n/// ```\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct StaticSegment<T: AsPath>(pub T);\n\nimpl<T: AsPath> PossibleRouteMatch for StaticSegment<T> {\n    fn optional(&self) -> bool {\n        false\n    }\n\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n        let mut matched_len = 0;\n        let mut test = path.chars().peekable();\n        let mut this = self.0.as_path().chars();\n        let mut has_matched =\n            self.0.as_path().is_empty() || self.0.as_path() == \"/\";\n\n        // match an initial /\n        if let Some('/') = test.peek() {\n            test.next();\n\n            if !self.0.as_path().is_empty() {\n                matched_len += 1;\n            }\n            if self.0.as_path().starts_with('/') || self.0.as_path().is_empty()\n            {\n                this.next();\n            }\n        } else if !path.is_empty() {\n            // Path must start with `/` otherwise we are not certain about being at the beginning of the segment in the path\n            return None;\n        }\n\n        for char in test {\n            let n = this.next();\n            // when we get a closing /, stop matching\n            if char == '/' {\n                if n.is_some() {\n                    return None;\n                }\n                break;\n            } else if n.is_none() {\n                break;\n            }\n            // if the next character in the path matches the\n            // next character in the segment, add it to the match\n            else if Some(char) == n {\n                has_matched = true;\n                matched_len += char.len_utf8();\n            }\n            // otherwise, this route doesn't match and we should\n            // return None\n            else {\n                return None;\n            }\n        }\n\n        // if we still have remaining, unmatched characters in this segment, it was not a match\n        if this.next().is_some() {\n            return None;\n        }\n\n        // build the match object\n        let (matched, remaining) = if matched_len == 1 && path.starts_with('/')\n        {\n            // If only thing that matched is `/` we can't eat it, otherwise next invocation of the\n            // test function will not be able to tell that we are matching from the beginning of the path segment\n            (\"/\", path)\n        } else {\n            // the remaining is built from the path in, with the slice moved\n            // by the length of this match\n            path.split_at(matched_len)\n        };\n        has_matched.then(|| PartialPathMatch::new(remaining, vec![], matched))\n    }\n\n    fn generate_path(&self, path: &mut Vec<PathSegment>) {\n        path.push(PathSegment::Static(self.0.as_path().into()))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{PossibleRouteMatch, StaticSegment};\n    use crate::AsPath;\n\n    #[derive(Debug, Clone)]\n    enum Paths {\n        Foo,\n        Bar,\n    }\n\n    impl AsPath for Paths {\n        fn as_path(&self) -> &'static str {\n            match self {\n                Foo => \"foo\",\n                Bar => \"bar\",\n            }\n        }\n    }\n\n    use Paths::*;\n\n    #[test]\n    fn single_static_match() {\n        let path = \"/foo\";\n        let def = StaticSegment(\"foo\");\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn single_static_match_on_enum() {\n        let path = \"/foo\";\n        let def = StaticSegment(Foo);\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn single_static_mismatch() {\n        let path = \"/foo\";\n        let def = StaticSegment(\"bar\");\n        assert!(def.test(path).is_none());\n    }\n\n    #[test]\n    fn single_static_mismatch_on_enum() {\n        let path = \"/foo\";\n        let def = StaticSegment(Bar);\n        assert!(def.test(path).is_none());\n    }\n\n    #[test]\n    fn single_static_match_with_trailing_slash() {\n        let path = \"/foo/\";\n        let def = StaticSegment(\"foo\");\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo\");\n        assert_eq!(matched.remaining(), \"/\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn single_static_match_with_trailing_slash_on_enum() {\n        let path = \"/foo/\";\n        let def = StaticSegment(Foo);\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo\");\n        assert_eq!(matched.remaining(), \"/\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn tuple_of_static_matches() {\n        let path = \"/foo/bar\";\n        let def = (StaticSegment(\"foo\"), StaticSegment(\"bar\"));\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn tuple_of_static_matches_on_enum() {\n        let path = \"/foo/bar\";\n        let def = (StaticSegment(Foo), StaticSegment(Bar));\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn allow_empty_match() {\n        let path = \"\";\n        let def = StaticSegment(\"\");\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn tuple_static_mismatch() {\n        let path = \"/foo/baz\";\n        let def = (StaticSegment(\"foo\"), StaticSegment(\"bar\"));\n        assert!(def.test(path).is_none());\n    }\n\n    #[test]\n    fn tuple_static_mismatch_on_enum() {\n        let path = \"/foo/baz\";\n        let def = (StaticSegment(Foo), StaticSegment(Bar));\n        assert!(def.test(path).is_none());\n    }\n\n    #[test]\n    fn dont_match_smooshed_segments() {\n        let path = \"/foobar\";\n        let def = (StaticSegment(Foo), StaticSegment(Bar));\n        assert!(def.test(path).is_none());\n    }\n\n    #[test]\n    fn arbitrary_nesting_of_tuples_has_no_effect_on_matching() {\n        let path = \"/foo/bar\";\n        let def = (\n            (),\n            (StaticSegment(\"foo\")),\n            (),\n            ((), ()),\n            StaticSegment(\"bar\"),\n            (),\n        );\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn arbitrary_nesting_of_tuples_has_no_effect_on_matching_on_enum() {\n        let path = \"/foo/bar\";\n        let def = (\n            (),\n            (StaticSegment(Foo)),\n            (),\n            ((), ()),\n            StaticSegment(Bar),\n            (),\n        );\n        let matched = def.test(path).expect(\"couldn't match route\");\n        assert_eq!(matched.matched(), \"/foo/bar\");\n        assert_eq!(matched.remaining(), \"\");\n        let params = matched.params();\n        assert!(params.is_empty());\n    }\n\n    #[test]\n    fn only_match_full_static_paths() {\n        let def = (StaticSegment(\"tests\"), StaticSegment(\"abc\"));\n        assert!(def.test(\"/tes/abc\").is_none());\n        assert!(def.test(\"/test/abc\").is_none());\n        assert!(def.test(\"/tes/abc/\").is_none());\n        assert!(def.test(\"/test/abc/\").is_none());\n        assert!(def.test(\"/tests/ab\").is_none());\n        assert!(def.test(\"/tests/ab/\").is_none());\n    }\n}\n"
  },
  {
    "path": "router/src/matching/horizontal/tuples.rs",
    "content": "use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};\n\nmacro_rules! tuples {\n    ($first:ident => $($ty:ident),*) => {\n        impl<$first, $($ty),*> PossibleRouteMatch for ($first, $($ty,)*)\n        where\n            $first: PossibleRouteMatch,\n\t\t\t$($ty: PossibleRouteMatch),*,\n        {\n            fn optional(&self) -> bool {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = &self;\n                [$first.optional(), $($ty.optional()),*].into_iter().any(|n| n)\n            }\n\n            fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = &self;\n\n                // on the first run, include all optionals\n                let mut include_optionals = {\n                    [$first.optional(), $($ty.optional()),*].into_iter().filter(|n| *n).count()\n                };\n\n                loop {\n                    let mut nth_field = 0;\n                    let mut matched_len = 0;\n                    let mut r = path;\n\n                    let mut p = Vec::new();\n                    let mut m = String::new();\n\n                    if $first.optional() {\n                        nth_field += 1;\n                    }\n                    if !$first.optional() || nth_field <= include_optionals {\n                        match $first.test(r) {\n                            None => {\n                                return None;\n                            },\n                            Some(PartialPathMatch { remaining, matched, params }) => {\n                                p.extend(params.into_iter());\n                                m.push_str(matched);\n                                r = remaining;\n                            },\n                        }\n                    }\n\n                    matched_len += m.len();\n                    $(\n                        if $ty.optional() {\n                            nth_field += 1;\n                        }\n                        if !$ty.optional() || nth_field <= include_optionals {\n                            let PartialPathMatch {\n                                remaining,\n                                matched,\n                                params\n                            } = match $ty.test(r) {\n                                None => if $ty.optional() {\n                                    return None;\n                                } else {\n                                    if include_optionals == 0 {\n                                        return None;\n                                    }\n                                    include_optionals -= 1;\n                                    continue;\n                                },\n                                Some(v) => v,\n                            };\n                            r = remaining;\n                            matched_len += matched.len();\n                            p.extend(params);\n                        }\n                    )*\n                    return Some(PartialPathMatch {\n                        remaining: r,\n                        matched: &path[0..matched_len],\n                        params: p\n                    });\n                }\n            }\n\n            fn generate_path(&self, path: &mut Vec<PathSegment>) {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = &self;\n                $first.generate_path(path);\n                $(\n                    $ty.generate_path(path);\n                )*\n            }\n        }\n\t};\n}\n\nimpl<A> PossibleRouteMatch for (A,)\nwhere\n    Self: core::fmt::Debug,\n    A: PossibleRouteMatch,\n{\n    fn optional(&self) -> bool {\n        self.0.optional()\n    }\n\n    fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>> {\n        let remaining = path;\n        let PartialPathMatch {\n            remaining,\n            matched,\n            params,\n        } = self.0.test(remaining)?;\n        Some(PartialPathMatch {\n            remaining,\n            matched: &path[0..matched.len()],\n            params,\n        })\n    }\n\n    fn generate_path(&self, path: &mut Vec<PathSegment>) {\n        self.0.generate_path(path);\n    }\n}\n\ntuples!(A => B);\ntuples!(A => B, C);\ntuples!(A => B, C, D);\ntuples!(A => B, C, D, E);\ntuples!(A => B, C, D, E, F);\ntuples!(A => B, C, D, E, F, G);\ntuples!(A => B, C, D, E, F, G, H);\ntuples!(A => B, C, D, E, F, G, H, I);\ntuples!(A => B, C, D, E, F, G, H, I, J);\ntuples!(A => B, C, D, E, F, G, H, I, J, K);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);\ntuples!(A => B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);\n/*tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y\n);\ntuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,\n    Z\n);*/\n"
  },
  {
    "path": "router/src/matching/mod.rs",
    "content": "#![allow(missing_docs)]\n\nmod any_choose_view;\nmod choose_view;\nmod path_segment;\npub(crate) mod resolve_path;\npub use choose_view::*;\npub use path_segment::*;\nmod horizontal;\nmod nested;\nmod vertical;\nuse crate::{static_routes::RegenerationFn, Method, SsrMode};\npub use horizontal::*;\npub use nested::*;\nuse std::{borrow::Cow, collections::HashSet, sync::atomic::Ordering};\npub use vertical::*;\n\n#[derive(Debug)]\npub struct RouteDefs<Children> {\n    base: Option<Cow<'static, str>>,\n    children: Children,\n}\n\nimpl<Children> Clone for RouteDefs<Children>\nwhere\n    Children: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            base: self.base.clone(),\n            children: self.children.clone(),\n        }\n    }\n}\n\nimpl<Children> RouteDefs<Children> {\n    pub fn new(children: Children) -> Self {\n        Self {\n            base: None,\n            children,\n        }\n    }\n\n    pub fn new_with_base(\n        children: Children,\n        base: impl Into<Cow<'static, str>>,\n    ) -> Self {\n        Self {\n            base: Some(base.into()),\n            children,\n        }\n    }\n}\n\nimpl<Children> RouteDefs<Children>\nwhere\n    Children: MatchNestedRoutes,\n{\n    pub fn match_route(&self, path: &str) -> Option<Children::Match> {\n        let path = match &self.base {\n            None => path,\n            Some(base) => {\n                let (base, path) = if base.starts_with('/') {\n                    (base.trim_start_matches('/'), path.trim_start_matches('/'))\n                } else {\n                    (base.as_ref(), path)\n                };\n                path.strip_prefix(base)?\n            }\n        };\n\n        let (matched, remaining) = self.children.match_nested(path);\n        let matched = matched?;\n\n        if !(remaining.is_empty() || remaining == \"/\") {\n            None\n        } else {\n            Some(matched.1)\n        }\n    }\n\n    pub fn generate_routes(\n        &self,\n    ) -> (\n        Option<&str>,\n        impl IntoIterator<Item = GeneratedRouteData> + '_,\n    ) {\n        (self.base.as_deref(), self.children.generate_routes())\n    }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\npub struct RouteMatchId(pub(crate) u16);\n\nimpl RouteMatchId {\n    /// Creates a new match ID based on the current route ID used in nested route generation.\n    ///\n    /// In general, you do not need this; it should only be used for custom route matching behavior\n    /// in a library that creates its own route types.\n    pub fn new_from_route_id() -> RouteMatchId {\n        RouteMatchId(ROUTE_ID.fetch_add(1, Ordering::Relaxed))\n    }\n}\n\npub trait MatchInterface {\n    type Child: MatchInterface + MatchParams + 'static;\n\n    fn as_id(&self) -> RouteMatchId;\n\n    fn as_matched(&self) -> &str;\n\n    fn into_view_and_child(self) -> (impl ChooseView, Option<Self::Child>);\n}\n\npub trait MatchParams {\n    fn to_params(&self) -> Vec<(Cow<'static, str>, String)>;\n}\n\npub trait MatchNestedRoutes {\n    type Data;\n    type Match: MatchInterface + MatchParams;\n\n    /// Matches nested routes\n    ///\n    /// # Arguments\n    ///\n    /// * path - A path which is being navigated to\n    ///\n    /// # Returns\n    ///\n    /// Tuple where\n    ///\n    /// * 0 - If match has been found `Some` containing tuple where\n    ///     * 0 - [RouteMatchId] identifying the matching route\n    ///     * 1 - [Self::Match] matching route\n    /// * 1 - Remaining path\n    fn match_nested<'a>(\n        &'a self,\n        path: &'a str,\n    ) -> (Option<(RouteMatchId, Self::Match)>, &'a str);\n\n    fn generate_routes(\n        &self,\n    ) -> impl IntoIterator<Item = GeneratedRouteData> + '_;\n\n    fn optional(&self) -> bool;\n}\n\n#[derive(Default, Debug, PartialEq)]\npub struct GeneratedRouteData {\n    pub segments: Vec<PathSegment>,\n    pub ssr_mode: SsrMode,\n    pub methods: HashSet<Method>,\n    pub regenerate: Vec<RegenerationFn>,\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{NestedRoute, ParamSegment, RouteDefs};\n    use crate::{\n        matching::MatchParams, MatchInterface, PathSegment, StaticSegment,\n        WildcardSegment,\n    };\n    use either_of::{Either, EitherOf4};\n\n    #[test]\n    pub fn matches_single_root_route() {\n        let routes =\n            RouteDefs::<_>::new(NestedRoute::new(StaticSegment(\"/\"), || ()));\n        let matched = routes.match_route(\"/\");\n        assert!(matched.is_some());\n        // this case seems like it should match, but implementing it interferes with\n        // handling trailing slash requirements accurately -- paths for the root are \"/\",\n        // not \"\", in any case\n        let matched = routes.match_route(\"\");\n        assert!(matched.is_none());\n        let (base, paths) = routes.generate_routes();\n        assert_eq!(base, None);\n        let paths = paths.into_iter().map(|g| g.segments).collect::<Vec<_>>();\n        assert_eq!(paths, vec![vec![PathSegment::Static(\"/\".into())]]);\n    }\n\n    #[test]\n    pub fn matches_nested_route() {\n        let routes: RouteDefs<_> = RouteDefs::new(\n            NestedRoute::new(StaticSegment(\"\"), || \"Home\").child(\n                NestedRoute::new(\n                    (StaticSegment(\"author\"), StaticSegment(\"contact\")),\n                    || \"Contact Me\",\n                ),\n            ),\n        );\n\n        // route generation\n        let (base, paths) = routes.generate_routes();\n        assert_eq!(base, None);\n        let paths = paths.into_iter().map(|g| g.segments).collect::<Vec<_>>();\n        assert_eq!(\n            paths,\n            vec![vec![\n                PathSegment::Static(\"\".into()),\n                PathSegment::Static(\"author\".into()),\n                PathSegment::Static(\"contact\".into())\n            ]]\n        );\n\n        let matched = routes.match_route(\"/author/contact\").unwrap();\n        assert_eq!(MatchInterface::as_matched(&matched), \"\");\n        let (_, child) = MatchInterface::into_view_and_child(matched);\n        assert_eq!(\n            MatchInterface::as_matched(&child.unwrap()),\n            \"/author/contact\"\n        );\n    }\n\n    #[test]\n    pub fn does_not_match_route_unless_full_param_matches() {\n        let routes = RouteDefs::<_>::new((\n            NestedRoute::new(StaticSegment(\"/property-api\"), || ()),\n            NestedRoute::new(StaticSegment(\"/property\"), || ()),\n        ));\n        let matched = routes.match_route(\"/property\").unwrap();\n        assert!(matches!(matched, Either::Right(_)));\n    }\n\n    #[test]\n    pub fn does_not_match_incomplete_route() {\n        let routes: RouteDefs<_> = RouteDefs::new(\n            NestedRoute::new(StaticSegment(\"\"), || \"Home\").child(\n                NestedRoute::new(\n                    (StaticSegment(\"author\"), StaticSegment(\"contact\")),\n                    || \"Contact Me\",\n                ),\n            ),\n        );\n        let matched = routes.match_route(\"/\");\n        assert!(matched.is_none());\n    }\n\n    #[test]\n    pub fn chooses_between_nested_routes() {\n        let routes: RouteDefs<_> = RouteDefs::new((\n            NestedRoute::new(StaticSegment(\"/\"), || ()).child((\n                NestedRoute::new(StaticSegment(\"\"), || ()),\n                NestedRoute::new(StaticSegment(\"about\"), || ()),\n            )),\n            NestedRoute::new(StaticSegment(\"/blog\"), || ()).child((\n                NestedRoute::new(StaticSegment(\"\"), || ()),\n                NestedRoute::new(\n                    (StaticSegment(\"post\"), ParamSegment(\"id\")),\n                    || (),\n                ),\n            )),\n        ));\n\n        // generates routes correctly\n        let (base, paths) = routes.generate_routes();\n        assert_eq!(base, None);\n        let paths = paths.into_iter().map(|g| g.segments).collect::<Vec<_>>();\n        assert_eq!(\n            paths,\n            vec![\n                vec![\n                    PathSegment::Static(\"/\".into()),\n                    PathSegment::Static(\"\".into()),\n                ],\n                vec![\n                    PathSegment::Static(\"/\".into()),\n                    PathSegment::Static(\"about\".into())\n                ],\n                vec![\n                    PathSegment::Static(\"/blog\".into()),\n                    PathSegment::Static(\"\".into()),\n                ],\n                vec![\n                    PathSegment::Static(\"/blog\".into()),\n                    PathSegment::Static(\"post\".into()),\n                    PathSegment::Param(\"id\".into())\n                ]\n            ]\n        );\n\n        let matched = routes.match_route(\"/about\").unwrap();\n        let params = matched.to_params();\n        assert!(params.is_empty());\n        let matched = routes.match_route(\"/blog\").unwrap();\n        let params = matched.to_params();\n        assert!(params.is_empty());\n        let matched = routes.match_route(\"/blog/post/42\").unwrap();\n        let params = matched.to_params();\n        assert_eq!(params, vec![(\"id\".into(), \"42\".into())]);\n    }\n\n    #[test]\n    pub fn arbitrary_nested_routes() {\n        let routes: RouteDefs<_> = RouteDefs::new_with_base(\n            (\n                NestedRoute::new(StaticSegment(\"/\"), || ()).child((\n                    NestedRoute::new(StaticSegment(\"/\"), || ()),\n                    NestedRoute::new(StaticSegment(\"about\"), || ()),\n                )),\n                NestedRoute::new(StaticSegment(\"/blog\"), || ()).child((\n                    NestedRoute::new(StaticSegment(\"\"), || ()),\n                    NestedRoute::new(StaticSegment(\"category\"), || ()),\n                    NestedRoute::new(\n                        (StaticSegment(\"post\"), ParamSegment(\"id\")),\n                        || (),\n                    ),\n                )),\n                NestedRoute::new(\n                    (StaticSegment(\"/contact\"), WildcardSegment(\"any\")),\n                    || (),\n                ),\n            ),\n            \"/portfolio\",\n        );\n\n        // generates routes correctly\n        let (base, _paths) = routes.generate_routes();\n        assert_eq!(base, Some(\"/portfolio\"));\n\n        let matched = routes.match_route(\"/about\");\n        assert!(matched.is_none());\n\n        let matched = routes.match_route(\"/portfolio/about\").unwrap();\n        let params = matched.to_params();\n        assert!(params.is_empty());\n\n        let matched = routes.match_route(\"/portfolio/blog/post/42\").unwrap();\n        let params = matched.to_params();\n        assert_eq!(params, vec![(\"id\".into(), \"42\".into())]);\n\n        let matched = routes.match_route(\"/portfolio/contact\").unwrap();\n        let params = matched.to_params();\n        assert_eq!(params, vec![(\"any\".into(), \"\".into())]);\n\n        let matched = routes.match_route(\"/portfolio/contact/foobar\").unwrap();\n        let params = matched.to_params();\n        assert_eq!(params, vec![(\"any\".into(), \"foobar\".into())]);\n    }\n\n    #[test]\n    pub fn dont_match_smooshed_static_segments() {\n        let routes = RouteDefs::<_>::new((\n            NestedRoute::new(StaticSegment(\"\"), || ()),\n            NestedRoute::new(StaticSegment(\"users\"), || ()),\n            NestedRoute::new(\n                (StaticSegment(\"users\"), StaticSegment(\"id\")),\n                || (),\n            ),\n            NestedRoute::new(WildcardSegment(\"any\"), || ()),\n        ));\n\n        let matched = routes.match_route(\"/users\");\n        assert!(matches!(matched, Some(EitherOf4::B(..))));\n\n        let matched = routes.match_route(\"/users/id\");\n        assert!(matches!(matched, Some(EitherOf4::C(..))));\n\n        let matched = routes.match_route(\"/usersid\");\n        assert!(matches!(matched, Some(EitherOf4::D(..))));\n    }\n}\n\n/// Successful result of [testing](PossibleRouteMatch::test) a single segment in the route path\n#[derive(Debug)]\npub struct PartialPathMatch<'a> {\n    /// unmatched yet part of the path\n    pub(crate) remaining: &'a str,\n    /// value of parameters encoded inside of the path\n    pub(crate) params: Vec<(Cow<'static, str>, String)>,\n    /// part of the original path that was matched by segment\n    pub(crate) matched: &'a str,\n}\n\nimpl<'a> PartialPathMatch<'a> {\n    pub fn new(\n        remaining: &'a str,\n        params: Vec<(Cow<'static, str>, String)>,\n        matched: &'a str,\n    ) -> Self {\n        Self {\n            remaining,\n            params,\n            matched,\n        }\n    }\n\n    pub fn is_complete(&self) -> bool {\n        self.remaining.is_empty() || self.remaining == \"/\"\n    }\n\n    pub fn remaining(&self) -> &'a str {\n        self.remaining\n    }\n\n    pub fn params(self) -> Vec<(Cow<'static, str>, String)> {\n        self.params\n    }\n\n    pub fn matched(&self) -> &'a str {\n        self.matched\n    }\n}\n"
  },
  {
    "path": "router/src/matching/nested/any_nested_match.rs",
    "content": "#![allow(clippy::type_complexity)]\nuse crate::{\n    matching::any_choose_view::AnyChooseView, ChooseView, MatchInterface,\n    MatchParams, RouteMatchId,\n};\nuse std::{borrow::Cow, fmt::Debug};\nuse tachys::erased::ErasedLocal;\n\n/// A type-erased container for any [`MatchParams'] + [`MatchInterface`].\npub struct AnyNestedMatch {\n    value: ErasedLocal,\n    to_params: fn(&ErasedLocal) -> Vec<(Cow<'static, str>, String)>,\n    as_id: fn(&ErasedLocal) -> RouteMatchId,\n    as_matched: for<'a> fn(&'a ErasedLocal) -> &'a str,\n    into_view_and_child:\n        fn(ErasedLocal) -> (AnyChooseView, Option<AnyNestedMatch>),\n}\n\nimpl Debug for AnyNestedMatch {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AnyNestedMatch\").finish_non_exhaustive()\n    }\n}\n\n/// Converts anything implementing [`MatchParams'] + [`MatchInterface`] into an erased type.\npub trait IntoAnyNestedMatch {\n    /// Wraps the nested route.\n    fn into_any_nested_match(self) -> AnyNestedMatch;\n}\n\nimpl<T> IntoAnyNestedMatch for T\nwhere\n    T: MatchParams + MatchInterface + 'static,\n{\n    fn into_any_nested_match(self) -> AnyNestedMatch {\n        let value = ErasedLocal::new(self);\n\n        fn to_params<T: MatchParams + 'static>(\n            value: &ErasedLocal,\n        ) -> Vec<(Cow<'static, str>, String)> {\n            let value = value.get_ref::<T>();\n            value.to_params()\n        }\n\n        fn as_id<T: MatchInterface + 'static>(\n            value: &ErasedLocal,\n        ) -> RouteMatchId {\n            let value = value.get_ref::<T>();\n            value.as_id()\n        }\n\n        fn as_matched<T: MatchInterface + 'static>(\n            value: &ErasedLocal,\n        ) -> &str {\n            let value = value.get_ref::<T>();\n            value.as_matched()\n        }\n\n        fn into_view_and_child<T: MatchInterface + 'static>(\n            value: ErasedLocal,\n        ) -> (AnyChooseView, Option<AnyNestedMatch>) {\n            let value = value.into_inner::<T>();\n            let (view, child) = value.into_view_and_child();\n            (\n                AnyChooseView::new(view),\n                child.map(|child| child.into_any_nested_match()),\n            )\n        }\n\n        AnyNestedMatch {\n            value,\n            to_params: to_params::<T>,\n            as_id: as_id::<T>,\n            as_matched: as_matched::<T>,\n            into_view_and_child: into_view_and_child::<T>,\n        }\n    }\n}\n\nimpl MatchParams for AnyNestedMatch {\n    fn to_params(&self) -> Vec<(Cow<'static, str>, String)> {\n        (self.to_params)(&self.value)\n    }\n}\n\nimpl MatchInterface for AnyNestedMatch {\n    type Child = AnyNestedMatch;\n\n    fn as_id(&self) -> RouteMatchId {\n        (self.as_id)(&self.value)\n    }\n\n    fn as_matched(&self) -> &str {\n        (self.as_matched)(&self.value)\n    }\n\n    fn into_view_and_child(self) -> (impl ChooseView, Option<Self::Child>) {\n        (self.into_view_and_child)(self.value)\n    }\n}\n"
  },
  {
    "path": "router/src/matching/nested/any_nested_route.rs",
    "content": "#![allow(clippy::type_complexity)]\nuse crate::{\n    matching::nested::any_nested_match::{AnyNestedMatch, IntoAnyNestedMatch},\n    GeneratedRouteData, MatchNestedRoutes, RouteMatchId,\n};\nuse std::fmt::Debug;\nuse tachys::{erased::Erased, prelude::IntoMaybeErased};\n\n/// A type-erased container for any [`MatchNestedRoutes`].\npub struct AnyNestedRoute {\n    value: Erased,\n    clone: fn(&Erased) -> AnyNestedRoute,\n    match_nested:\n        for<'a> fn(\n            &'a Erased,\n            &'a str,\n        )\n            -> (Option<(RouteMatchId, AnyNestedMatch)>, &'a str),\n    generate_routes: fn(&Erased) -> Vec<GeneratedRouteData>,\n    optional: fn(&Erased) -> bool,\n}\n\nimpl Clone for AnyNestedRoute {\n    fn clone(&self) -> Self {\n        (self.clone)(&self.value)\n    }\n}\n\nimpl Debug for AnyNestedRoute {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AnyNestedRoute\").finish_non_exhaustive()\n    }\n}\n\nimpl IntoMaybeErased for AnyNestedRoute {\n    type Output = Self;\n\n    fn into_maybe_erased(self) -> Self::Output {\n        self\n    }\n}\n\n/// Converts anything implementing [`MatchNestedRoutes`] into [`AnyNestedRoute`].\npub trait IntoAnyNestedRoute {\n    /// Wraps the nested route.\n    fn into_any_nested_route(self) -> AnyNestedRoute;\n}\n\nimpl<T> IntoAnyNestedRoute for T\nwhere\n    T: MatchNestedRoutes + Send + Clone + 'static,\n{\n    fn into_any_nested_route(self) -> AnyNestedRoute {\n        fn clone<T: MatchNestedRoutes + Send + Clone + 'static>(\n            value: &Erased,\n        ) -> AnyNestedRoute {\n            value.get_ref::<T>().clone().into_any_nested_route()\n        }\n\n        fn match_nested<'a, T: MatchNestedRoutes + Send + Clone + 'static>(\n            value: &'a Erased,\n            path: &'a str,\n        ) -> (Option<(RouteMatchId, AnyNestedMatch)>, &'a str) {\n            let (maybe_match, path) = value.get_ref::<T>().match_nested(path);\n            (\n                maybe_match\n                    .map(|(id, matched)| (id, matched.into_any_nested_match())),\n                path,\n            )\n        }\n\n        fn generate_routes<T: MatchNestedRoutes + Send + Clone + 'static>(\n            value: &Erased,\n        ) -> Vec<GeneratedRouteData> {\n            value.get_ref::<T>().generate_routes().into_iter().collect()\n        }\n\n        fn optional<T: MatchNestedRoutes + Send + Clone + 'static>(\n            value: &Erased,\n        ) -> bool {\n            value.get_ref::<T>().optional()\n        }\n\n        AnyNestedRoute {\n            value: Erased::new(self),\n            clone: clone::<T>,\n            match_nested: match_nested::<T>,\n            generate_routes: generate_routes::<T>,\n            optional: optional::<T>,\n        }\n    }\n}\n\nimpl MatchNestedRoutes for AnyNestedRoute {\n    type Data = AnyNestedMatch;\n    type Match = AnyNestedMatch;\n\n    fn match_nested<'a>(\n        &'a self,\n        path: &'a str,\n    ) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {\n        (self.match_nested)(&self.value, path)\n    }\n\n    fn generate_routes(&self) -> impl IntoIterator<Item = GeneratedRouteData> {\n        (self.generate_routes)(&self.value)\n    }\n\n    fn optional(&self) -> bool {\n        (self.optional)(&self.value)\n    }\n}\n"
  },
  {
    "path": "router/src/matching/nested/mod.rs",
    "content": "use super::{\n    IntoChooseViewMaybeErased, MatchInterface, MatchNestedRoutes, PathSegment,\n    PossibleRouteMatch, RouteMatchId,\n};\nuse crate::{ChooseView, GeneratedRouteData, MatchParams, Method, SsrMode};\nuse core::{fmt, iter};\nuse either_of::Either;\nuse std::{\n    borrow::Cow,\n    collections::HashSet,\n    sync::atomic::{AtomicU16, Ordering},\n};\nuse tachys::prelude::IntoMaybeErased;\n\npub mod any_nested_match;\npub mod any_nested_route;\nmod tuples;\n\npub(crate) static ROUTE_ID: AtomicU16 = AtomicU16::new(1);\n\n#[derive(Debug, PartialEq, Eq)]\npub struct NestedRoute<Segments, Children, Data, View> {\n    id: u16,\n    segments: Segments,\n    children: Option<Children>,\n    data: Data,\n    view: View,\n    methods: HashSet<Method>,\n    ssr_mode: SsrMode,\n}\n\nimpl<Segments, Children, Data, View> IntoMaybeErased\n    for NestedRoute<Segments, Children, Data, View>\nwhere\n    Self: MatchNestedRoutes + Send + Clone + 'static,\n{\n    #[cfg(erase_components)]\n    type Output = any_nested_route::AnyNestedRoute;\n\n    #[cfg(not(erase_components))]\n    type Output = Self;\n\n    fn into_maybe_erased(self) -> Self::Output {\n        #[cfg(erase_components)]\n        {\n            use any_nested_route::IntoAnyNestedRoute;\n\n            self.into_any_nested_route()\n        }\n        #[cfg(not(erase_components))]\n        {\n            self\n        }\n    }\n}\n\nimpl<Segments, Children, Data, View> Clone\n    for NestedRoute<Segments, Children, Data, View>\nwhere\n    Segments: Clone,\n    Children: Clone,\n    Data: Clone,\n    View: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            id: self.id,\n            segments: self.segments.clone(),\n            children: self.children.clone(),\n            data: self.data.clone(),\n            view: self.view.clone(),\n            methods: self.methods.clone(),\n            ssr_mode: self.ssr_mode.clone(),\n        }\n    }\n}\n\nimpl<Segments, View> NestedRoute<Segments, (), (), View> {\n    pub fn new(\n        path: Segments,\n        view: View,\n    ) -> NestedRoute<\n        Segments,\n        (),\n        (),\n        <View as IntoChooseViewMaybeErased>::Output,\n    >\n    where\n        View: ChooseView,\n    {\n        NestedRoute {\n            id: ROUTE_ID.fetch_add(1, Ordering::Relaxed),\n            segments: path,\n            children: None,\n            data: (),\n            view: view.into_maybe_erased(),\n            methods: [Method::Get].into(),\n            ssr_mode: Default::default(),\n        }\n    }\n}\n\nimpl<Segments, Data, View> NestedRoute<Segments, (), Data, View> {\n    pub fn child<Children>(\n        self,\n        child: Children,\n    ) -> NestedRoute<Segments, Children, Data, View> {\n        let Self {\n            id,\n            segments,\n            data,\n            view,\n            ssr_mode,\n            methods,\n            ..\n        } = self;\n        NestedRoute {\n            id,\n            segments,\n            children: Some(child),\n            data,\n            view,\n            ssr_mode,\n            methods,\n        }\n    }\n\n    pub fn ssr_mode(mut self, ssr_mode: SsrMode) -> Self {\n        self.ssr_mode = ssr_mode;\n        self\n    }\n}\n\n#[derive(PartialEq, Eq)]\npub struct NestedMatch<Child, View> {\n    id: RouteMatchId,\n    /// The portion of the full path matched only by this nested route.\n    matched: String,\n    /// The map of params matched only by this nested route.\n    params: Vec<(Cow<'static, str>, String)>,\n    /// The nested route.\n    child: Option<Child>,\n    view_fn: View,\n}\n\nimpl<Child, View> fmt::Debug for NestedMatch<Child, View>\nwhere\n    Child: fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"NestedMatch\")\n            .field(\"matched\", &self.matched)\n            .field(\"params\", &self.params)\n            .field(\"child\", &self.child)\n            .finish()\n    }\n}\n\nimpl<Child, View> MatchParams for NestedMatch<Child, View> {\n    #[inline(always)]\n    fn to_params(&self) -> Vec<(Cow<'static, str>, String)> {\n        self.params.clone()\n    }\n}\n\nimpl<Child, View> MatchInterface for NestedMatch<Child, View>\nwhere\n    Child: MatchInterface + MatchParams + 'static,\n    View: ChooseView,\n{\n    type Child = Child;\n\n    fn as_id(&self) -> RouteMatchId {\n        self.id\n    }\n\n    fn as_matched(&self) -> &str {\n        &self.matched\n    }\n\n    fn into_view_and_child(self) -> (impl ChooseView, Option<Self::Child>) {\n        (self.view_fn, self.child)\n    }\n}\n\nimpl<Segments, Children, Data, View> MatchNestedRoutes\n    for NestedRoute<Segments, Children, Data, View>\nwhere\n    Self: 'static,\n    Segments: PossibleRouteMatch,\n    Children: MatchNestedRoutes,\n    View: ChooseView,\n{\n    type Data = Data;\n    type Match = NestedMatch<Children::Match, View>;\n\n    fn optional(&self) -> bool {\n        self.segments.optional()\n            && self.children.as_ref().map(|n| n.optional()).unwrap_or(true)\n    }\n\n    fn match_nested<'a>(\n        &'a self,\n        path: &'a str,\n    ) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {\n        // if this was optional (for example, this whole nested route definition consisted of an optional param),\n        // then we'll need to retest the inner value against the starting path, if this one succeeds and the inner one fails\n        let this_was_optional = self.segments.optional();\n\n        self.segments\n            .test(path)\n            .and_then({\n                type Params = Vec<(Cow<'static, str>, String)>;\n\n                // codegen optimisation:\n                fn inner<'a, Children>(\n                    this_was_optional: bool,\n                    path: &'a str,\n                    remaining: &'a str,\n                    segments: &dyn PossibleRouteMatch,\n                    children: &'a Option<Children>,\n                    mut params: Params,\n                ) -> Option<(Option<Children::Match>, &'a str, Params)>\n                where\n                    Children: MatchNestedRoutes,\n                {\n                    let mut was_optional_fallback = false;\n\n                    let (child, remaining) = match children {\n                        None => (None, remaining),\n                        Some(children) => {\n                            let (inner, remaining) =\n                                children.match_nested(remaining);\n\n                            if let Some((_, child)) = inner {\n                                (Some(child), remaining)\n                            } else if this_was_optional {\n                                // if the parent route was optional, re-match children against full path\n                                was_optional_fallback = true;\n                                let (inner, remaining) =\n                                    children.match_nested(path);\n                                inner.map(|(_, child)| {\n                                    (Some(child), remaining)\n                                })?\n                            } else {\n                                return None;\n                            }\n                        }\n                    };\n\n                    if remaining.is_empty() || remaining == \"/\" {\n                        // if this was an optional route, re-parse its params\n                        if was_optional_fallback {\n                            // new params are based on the path it matched (up to the point where the matched child begins)\n                            // e.g., if we have /:foo?/bar, for /bar we should *not* have { \"foo\": \"bar\" }\n                            // so, we re-parse based on \"\" to yield { \"foo\": \"\" }\n                            let matched = child\n                                .as_ref()\n                                .map_or(\"\", Children::Match::as_matched);\n                            let rematch = path.trim_end_matches(&format!(\n                                \"{matched}{remaining}\"\n                            ));\n                            let new_partial = segments.test(rematch).unwrap();\n                            params = new_partial.params;\n                        }\n\n                        params.extend(\n                            child\n                                .as_ref()\n                                .map_or(Vec::new(), Children::Match::to_params),\n                        );\n                        Some((child, remaining, params))\n                    } else {\n                        None\n                    }\n                }\n\n                |partial_match| {\n                    let (child, remaining, params) = inner(\n                        this_was_optional,\n                        path,\n                        partial_match.remaining,\n                        &self.segments,\n                        &self.children,\n                        partial_match.params,\n                    )?;\n                    let id = RouteMatchId(self.id);\n\n                    Some((\n                        Some((\n                            id,\n                            NestedMatch {\n                                id,\n                                matched: partial_match.matched.to_string(),\n                                params,\n                                child,\n                                view_fn: self.view.clone(),\n                            },\n                        )),\n                        remaining,\n                    ))\n                }\n            })\n            .unwrap_or((None, path))\n    }\n\n    fn generate_routes(\n        &self,\n    ) -> impl IntoIterator<Item = GeneratedRouteData> + '_ {\n        let mut segment_routes = Vec::new();\n        self.segments.generate_path(&mut segment_routes);\n        let children = self.children.as_ref();\n        let ssr_mode = self.ssr_mode.clone();\n        let methods = self.methods.clone();\n        let regenerate = match &ssr_mode {\n            SsrMode::Static(data) => match data.regenerate.as_ref() {\n                None => vec![],\n                Some(regenerate) => vec![regenerate.clone()],\n            },\n            _ => vec![],\n        };\n\n        match children {\n            None => Either::Left(iter::once(GeneratedRouteData {\n                segments: segment_routes,\n                ssr_mode,\n                methods,\n                regenerate,\n            })),\n            Some(children) => {\n                Either::Right(children.generate_routes().into_iter().map(\n                    move |child| {\n                        // extend this route's segments with child segments\n                        let segments = segment_routes\n                            .clone()\n                            .into_iter()\n                            .chain(child.segments)\n                            .collect();\n\n                        let mut methods = methods.clone();\n                        methods.extend(child.methods);\n\n                        let mut regenerate = regenerate.clone();\n                        regenerate.extend(child.regenerate);\n\n                        if child.ssr_mode > ssr_mode {\n                            GeneratedRouteData {\n                                segments,\n                                ssr_mode: child.ssr_mode,\n                                methods,\n                                regenerate,\n                            }\n                        } else {\n                            GeneratedRouteData {\n                                segments,\n                                ssr_mode: ssr_mode.clone(),\n                                methods,\n                                regenerate,\n                            }\n                        }\n                    },\n                ))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "router/src/matching/nested/tuples.rs",
    "content": "use super::{MatchInterface, MatchNestedRoutes, PathSegment, RouteMatchId};\nuse crate::{ChooseView, GeneratedRouteData, MatchParams};\nuse core::iter;\nuse either_of::*;\nuse std::borrow::Cow;\nuse tachys::view::iterators::StaticVec;\n\nimpl MatchParams for () {\n    fn to_params(&self) -> Vec<(Cow<'static, str>, String)> {\n        Vec::new()\n    }\n}\n\nimpl MatchInterface for () {\n    type Child = ();\n\n    fn as_id(&self) -> RouteMatchId {\n        RouteMatchId(0)\n    }\n\n    fn as_matched(&self) -> &str {\n        \"\"\n    }\n\n    fn into_view_and_child(self) -> (impl ChooseView, Option<Self::Child>) {\n        ((), None)\n    }\n}\n\nimpl MatchNestedRoutes for () {\n    type Data = ();\n    type Match = ();\n\n    fn optional(&self) -> bool {\n        false\n    }\n\n    fn match_nested<'a>(\n        &self,\n        path: &'a str,\n    ) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {\n        (Some((RouteMatchId(0), ())), path)\n    }\n\n    fn generate_routes(\n        &self,\n    ) -> impl IntoIterator<Item = GeneratedRouteData> + '_ {\n        iter::once(GeneratedRouteData {\n            segments: vec![PathSegment::Unit],\n            ..Default::default()\n        })\n    }\n}\n\nimpl<A> MatchParams for (A,)\nwhere\n    A: MatchParams,\n{\n    fn to_params(&self) -> Vec<(Cow<'static, str>, String)> {\n        self.0.to_params()\n    }\n}\n\nimpl<A> MatchInterface for (A,)\nwhere\n    A: MatchInterface + 'static,\n{\n    type Child = A::Child;\n\n    fn as_id(&self) -> RouteMatchId {\n        self.0.as_id()\n    }\n\n    fn as_matched(&self) -> &str {\n        self.0.as_matched()\n    }\n\n    fn into_view_and_child(self) -> (impl ChooseView, Option<Self::Child>) {\n        self.0.into_view_and_child()\n    }\n}\n\nimpl<A> MatchNestedRoutes for (A,)\nwhere\n    A: MatchNestedRoutes + 'static,\n{\n    type Data = A::Data;\n    type Match = A::Match;\n\n    fn match_nested<'a>(\n        &'a self,\n        path: &'a str,\n    ) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {\n        self.0.match_nested(path)\n    }\n\n    fn generate_routes(\n        &self,\n    ) -> impl IntoIterator<Item = GeneratedRouteData> + '_ {\n        self.0.generate_routes()\n    }\n\n    fn optional(&self) -> bool {\n        self.0.optional()\n    }\n}\n\nimpl<A, B> MatchParams for Either<A, B>\nwhere\n    A: MatchParams,\n    B: MatchParams,\n{\n    fn to_params(&self) -> Vec<(Cow<'static, str>, String)> {\n        match self {\n            Either::Left(i) => i.to_params(),\n            Either::Right(i) => i.to_params(),\n        }\n    }\n}\n\nimpl<A, B> MatchInterface for Either<A, B>\nwhere\n    A: MatchInterface,\n    B: MatchInterface,\n{\n    type Child = Either<A::Child, B::Child>;\n\n    fn as_id(&self) -> RouteMatchId {\n        match self {\n            Either::Left(i) => i.as_id(),\n            Either::Right(i) => i.as_id(),\n        }\n    }\n\n    fn as_matched(&self) -> &str {\n        match self {\n            Either::Left(i) => i.as_matched(),\n            Either::Right(i) => i.as_matched(),\n        }\n    }\n\n    fn into_view_and_child(self) -> (impl ChooseView, Option<Self::Child>) {\n        match self {\n            Either::Left(i) => {\n                let (view, child) = i.into_view_and_child();\n                (Either::Left(view), child.map(Either::Left))\n            }\n            Either::Right(i) => {\n                let (view, child) = i.into_view_and_child();\n                (Either::Right(view), child.map(Either::Right))\n            }\n        }\n    }\n}\n\nimpl<A, B> MatchNestedRoutes for (A, B)\nwhere\n    A: MatchNestedRoutes,\n    B: MatchNestedRoutes,\n{\n    type Data = (A::Data, B::Data);\n    type Match = Either<A::Match, B::Match>;\n\n    fn match_nested<'a>(\n        &'a self,\n        path: &'a str,\n    ) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {\n        #[allow(non_snake_case)]\n        let (A, B) = &self;\n        if let (Some((id, matched)), remaining) = A.match_nested(path) {\n            return (Some((id, Either::Left(matched))), remaining);\n        }\n        if let (Some((id, matched)), remaining) = B.match_nested(path) {\n            return (Some((id, Either::Right(matched))), remaining);\n        }\n        (None, path)\n    }\n\n    fn generate_routes(\n        &self,\n    ) -> impl IntoIterator<Item = GeneratedRouteData> + '_ {\n        #![allow(non_snake_case)]\n\n        let (A, B) = &self;\n\n        let A = A.generate_routes().into_iter();\n        let B = B.generate_routes().into_iter();\n\n        A.chain(B)\n    }\n\n    fn optional(&self) -> bool {\n        self.0.optional() && self.1.optional()\n    }\n}\n\nimpl<T> MatchNestedRoutes for StaticVec<T>\nwhere\n    T: MatchNestedRoutes,\n{\n    type Data = Vec<T::Data>;\n    type Match = T::Match;\n\n    fn match_nested<'a>(\n        &'a self,\n        path: &'a str,\n    ) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {\n        for item in self.iter() {\n            if let (Some((id, matched)), remaining) = item.match_nested(path) {\n                return (Some((id, matched)), remaining);\n            }\n        }\n        (None, path)\n    }\n\n    fn generate_routes(\n        &self,\n    ) -> impl IntoIterator<Item = GeneratedRouteData> + '_ {\n        self.iter().flat_map(T::generate_routes)\n    }\n\n    fn optional(&self) -> bool {\n        self.iter().all(|n| n.optional())\n    }\n}\n\nmacro_rules! chain_generated {\n    ($first:expr, $second:expr, ) => {\n        $first.chain($second)\n    };\n    ($first:expr, $second:ident, $($rest:ident,)+) => {\n        chain_generated!(\n            $first.chain($second),\n            $($rest,)+\n        )\n    }\n}\n\nmacro_rules! tuples {\n    ($either:ident => $($ty:ident = $count:expr),*) => {\n        impl<'a, $($ty,)*> MatchParams for $either <$($ty,)*>\n        where\n\t\t\t$($ty: MatchParams),*,\n        {\n            fn to_params(&self) -> Vec<(Cow<'static, str>, String)> {\n                match self {\n                    $($either::$ty(i) => i.to_params(),)*\n                }\n            }\n        }\n\n        impl<$($ty,)*> MatchInterface for $either <$($ty,)*>\n        where\n            $($ty: MatchInterface + 'static),*,\n        {\n            type Child = $either<$($ty::Child,)*>;\n\n            fn as_id(&self) -> RouteMatchId {\n                match self {\n                    $($either::$ty(i) => i.as_id(),)*\n                }\n            }\n\n            fn as_matched(&self) -> &str {\n                match self {\n                    $($either::$ty(i) => i.as_matched(),)*\n                }\n            }\n\n            fn into_view_and_child(\n                self,\n            ) -> (\n                impl ChooseView,\n                Option<Self::Child>,\n            ) {\n                match self {\n                    $($either::$ty(i) => {\n                        let (view, child) = i.into_view_and_child();\n                        ($either::$ty(view), child.map($either::$ty))\n                    })*\n                }\n            }\n        }\n\n        impl<$($ty),*> MatchNestedRoutes for ($($ty,)*)\n        where\n\t\t\t$($ty: MatchNestedRoutes + 'static),*,\n        {\n            type Data = ($($ty::Data,)*);\n            type Match = $either<$($ty::Match,)*>;\n\n            fn optional(&self) -> bool {\n                #[allow(non_snake_case)]\n                let ($($ty,)*) = &self;\n                $($ty.optional() &&)*\n                true\n            }\n\n            fn match_nested<'a>(&'a self, path: &'a str) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {\n                #[allow(non_snake_case)]\n\n                let ($($ty,)*) = &self;\n                $(if let (Some((_, matched)), remaining) = $ty.match_nested(path) {\n                    return (Some((RouteMatchId($count), $either::$ty(matched))), remaining);\n                })*\n                (None, path)\n            }\n\n            fn generate_routes(\n                &self,\n            ) -> impl IntoIterator<Item = GeneratedRouteData> + '_ {\n                #![allow(non_snake_case)]\n\n                let ($($ty,)*) = &self;\n                $(let $ty = $ty.generate_routes().into_iter();)*\n                chain_generated!($($ty,)*)\n            }\n        }\n    }\n}\n\ntuples!(EitherOf3 => A = 0, B = 1, C = 2);\ntuples!(EitherOf4 => A = 0, B = 1, C = 2, D = 3);\ntuples!(EitherOf5 => A = 0, B = 1, C = 2, D = 3, E = 4);\ntuples!(EitherOf6 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5);\ntuples!(EitherOf7 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6);\ntuples!(EitherOf8 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7);\ntuples!(EitherOf9 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8);\ntuples!(EitherOf10 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9);\ntuples!(EitherOf11 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10);\ntuples!(EitherOf12 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11);\ntuples!(EitherOf13 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12);\ntuples!(EitherOf14 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12, N = 13);\ntuples!(EitherOf15 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12, N = 13, O = 14);\ntuples!(EitherOf16 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12, N = 13, O = 14, P = 15);\n"
  },
  {
    "path": "router/src/matching/path_segment.rs",
    "content": "use std::borrow::Cow;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum PathSegment {\n    Unit,\n    Static(Cow<'static, str>),\n    Param(Cow<'static, str>),\n    OptionalParam(Cow<'static, str>),\n    Splat(Cow<'static, str>),\n}\n\nimpl PathSegment {\n    pub fn as_raw_str(&self) -> &str {\n        match self {\n            PathSegment::Unit => \"\",\n            PathSegment::Static(i) => i,\n            PathSegment::Param(i) => i,\n            PathSegment::OptionalParam(i) => i,\n            PathSegment::Splat(i) => i,\n        }\n    }\n}\n\npub trait ExpandOptionals {\n    fn expand_optionals(&self) -> Vec<Vec<PathSegment>>;\n}\n\nimpl ExpandOptionals for Vec<PathSegment> {\n    fn expand_optionals(&self) -> Vec<Vec<PathSegment>> {\n        let mut segments = vec![self.to_vec()];\n        let mut checked = Vec::new();\n        while let Some(next_to_check) = segments.pop() {\n            let mut had_optional = false;\n            for (idx, segment) in next_to_check.iter().enumerate() {\n                if let PathSegment::OptionalParam(name) = segment {\n                    had_optional = true;\n                    let mut unit_variant = next_to_check.to_vec();\n                    unit_variant.remove(idx);\n                    let mut param_variant = next_to_check.to_vec();\n                    param_variant[idx] = PathSegment::Param(name.clone());\n                    segments.push(unit_variant);\n                    segments.push(param_variant);\n                    break;\n                }\n            }\n            if !had_optional {\n                checked.push(next_to_check.to_vec());\n            }\n        }\n        checked\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{ExpandOptionals, PathSegment};\n\n    #[test]\n    fn expand_optionals_on_plain() {\n        let plain = vec![\n            PathSegment::Static(\"a\".into()),\n            PathSegment::Param(\"b\".into()),\n        ];\n        assert_eq!(plain.expand_optionals(), vec![plain]);\n    }\n\n    #[test]\n    fn expand_optionals_once() {\n        let plain = vec![\n            PathSegment::OptionalParam(\"a\".into()),\n            PathSegment::Static(\"b\".into()),\n        ];\n        assert_eq!(\n            plain.expand_optionals(),\n            vec![\n                vec![\n                    PathSegment::Param(\"a\".into()),\n                    PathSegment::Static(\"b\".into())\n                ],\n                vec![PathSegment::Static(\"b\".into())]\n            ]\n        );\n    }\n\n    #[test]\n    fn expand_optionals_twice() {\n        let plain = vec![\n            PathSegment::OptionalParam(\"a\".into()),\n            PathSegment::OptionalParam(\"b\".into()),\n            PathSegment::Static(\"c\".into()),\n        ];\n        assert_eq!(\n            plain.expand_optionals(),\n            vec![\n                vec![\n                    PathSegment::Param(\"a\".into()),\n                    PathSegment::Param(\"b\".into()),\n                    PathSegment::Static(\"c\".into()),\n                ],\n                vec![\n                    PathSegment::Param(\"a\".into()),\n                    PathSegment::Static(\"c\".into()),\n                ],\n                vec![\n                    PathSegment::Param(\"b\".into()),\n                    PathSegment::Static(\"c\".into()),\n                ],\n                vec![PathSegment::Static(\"c\".into())]\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "router/src/matching/resolve_path.rs",
    "content": "use std::borrow::Cow;\n\npub fn resolve_path<'a>(\n    base: &'a str,\n    path: &'a str,\n    from: Option<&'a str>,\n) -> Cow<'a, str> {\n    if has_scheme(path) {\n        path.into()\n    } else {\n        let base_path = normalize(base, false);\n        let from_path = from.map(|from| normalize(from, false));\n        let result = if let Some(from_path) = from_path {\n            if path.starts_with('/') {\n                base_path\n            } else if from_path.find(base_path.as_ref()) != Some(0) {\n                base_path + from_path\n            } else {\n                from_path\n            }\n        } else {\n            base_path\n        };\n\n        let result_empty = result.is_empty();\n        let prefix = if result_empty { \"/\".into() } else { result };\n\n        prefix + normalize(path, result_empty)\n    }\n}\n\nfn has_scheme(path: &str) -> bool {\n    path.starts_with(\"//\")\n        || path.starts_with(\"tel:\")\n        || path.starts_with(\"mailto:\")\n        || path\n            .split_once(\"://\")\n            .map(|(prefix, _)| {\n                prefix.chars().all(\n                    |c: char| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9'),\n                )\n            })\n            .unwrap_or(false)\n}\n\n#[doc(hidden)]\nfn normalize(path: &str, omit_slash: bool) -> Cow<'_, str> {\n    let s = path.trim_start_matches('/');\n    let trim_end = s\n        .chars()\n        .rev()\n        .take_while(|c| *c == '/')\n        .count()\n        .saturating_sub(1);\n    let s = &s[0..s.len() - trim_end];\n    if s.is_empty() || omit_slash || begins_with_query_or_hash(s) {\n        s.into()\n    } else {\n        format!(\"/{s}\").into()\n    }\n}\n\nfn begins_with_query_or_hash(text: &str) -> bool {\n    matches!(text.chars().next(), Some('#') | Some('?'))\n}\n\n/* TODO can remove?\n#[doc(hidden)]\npub fn join_paths<'a>(from: &'a str, to: &'a str) -> String {\n    let from = remove_wildcard(&normalize(from, false));\n    from + normalize(to, false).as_ref()\n}\n\nfn remove_wildcard(text: &str) -> String {\n    text.rsplit_once('*')\n        .map(|(prefix, _)| prefix)\n        .unwrap_or(text)\n        .trim_end_matches('/')\n        .to_string()\n}\n*/\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    #[test]\n    fn normalize_query_string_with_opening_slash() {\n        assert_eq!(normalize(\"/?foo=bar\", false), \"?foo=bar\");\n    }\n\n    #[test]\n    fn normalize_retain_trailing_slash() {\n        assert_eq!(normalize(\"foo/bar/\", false), \"/foo/bar/\");\n    }\n\n    #[test]\n    fn normalize_dedup_trailing_slashes() {\n        assert_eq!(normalize(\"foo/bar/////\", false), \"/foo/bar/\");\n    }\n}\n"
  },
  {
    "path": "router/src/matching/vertical/mod.rs",
    "content": "use super::PartialPathMatch;\n\npub trait ChooseRoute {\n    fn choose_route<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a>>;\n}\n"
  },
  {
    "path": "router/src/method.rs",
    "content": "/// Represents an HTTP method that can be handled by this route.\n#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]\npub enum Method {\n    /// The [`GET`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) method\n    /// requests a representation of the specified resource.\n    #[default]\n    Get,\n    /// The [`POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) method\n    /// submits an entity to the specified resource, often causing a change in\n    /// state or side effects on the server.\n    Post,\n    /// The [`PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) method\n    /// replaces all current representations of the target resource with the request payload.\n    Put,\n    /// The [`DELETE`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) method\n    /// deletes the specified resource.\n    Delete,\n    /// The [`PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) method\n    /// applies partial modifications to a resource.\n    Patch,\n}\n"
  },
  {
    "path": "router/src/navigate.rs",
    "content": "use crate::location::State;\n\n/// Options that can be used to configure a navigation. Used with [use_navigate](crate::hooks::use_navigate).\n#[derive(Clone, Debug)]\npub struct NavigateOptions {\n    /// Whether the URL being navigated to should be resolved relative to the current route.\n    pub resolve: bool,\n    /// If `true` the new location will replace the current route in the history stack, meaning\n    /// the \"back\" button will skip over the current route. (Defaults to `false`).\n    pub replace: bool,\n    /// If `true`, the router will scroll to the top of the window at the end of navigation.\n    /// Defaults to `true`.\n    pub scroll: bool,\n    /// [State](https://developer.mozilla.org/en-US/docs/Web/API/History/state) that should be pushed\n    /// onto the history stack during navigation.\n    pub state: State,\n}\n\nimpl Default for NavigateOptions {\n    fn default() -> Self {\n        Self {\n            resolve: true,\n            replace: false,\n            scroll: true,\n            state: State::new(None),\n        }\n    }\n}\n"
  },
  {
    "path": "router/src/nested_router.rs",
    "content": "use crate::{\n    flat_router::MatchedRoute,\n    hooks::Matched,\n    location::{LocationProvider, Url},\n    matching::RouteDefs,\n    params::ParamsMap,\n    view_transition::start_view_transition,\n    ChooseView, MatchInterface, MatchNestedRoutes, MatchParams, PathSegment,\n    RouteList, RouteListing, RouteMatchId,\n};\nuse any_spawner::Executor;\nuse either_of::{Either, EitherOf3};\nuse futures::{\n    channel::oneshot,\n    future::{join_all, AbortHandle, Abortable},\n    FutureExt,\n};\nuse leptos::{\n    attr::any_attribute::AnyAttribute,\n    component,\n    oco::Oco,\n    prelude::{ArcStoredValue, WriteValue},\n};\nuse or_poisoned::OrPoisoned;\nuse reactive_graph::{\n    computed::{ArcMemo, ScopedFuture},\n    owner::{provide_context, use_context, Owner},\n    signal::{ArcRwSignal, ArcTrigger},\n    traits::{Get, GetUntracked, Notify, ReadUntracked, Set, Track, Write},\n    transition::AsyncTransition,\n    wrappers::write::SignalSetter,\n};\nuse send_wrapper::SendWrapper;\nuse std::{\n    cell::RefCell,\n    fmt::Debug,\n    future::Future,\n    iter, mem,\n    pin::Pin,\n    rc::Rc,\n    sync::{Arc, Mutex},\n};\nuse tachys::{\n    hydration::Cursor,\n    reactive_graph::{OwnedView, Suspend},\n    ssr::StreamBuilder,\n    view::{\n        add_attr::AddAnyAttr,\n        any_view::{AnyView, IntoAny},\n        either::EitherOf3State,\n        Mountable, Position, PositionState, Render, RenderHtml,\n    },\n};\n\npub(crate) struct NestedRoutesView<Loc, Defs, FalFn> {\n    pub location: Option<Loc>,\n    pub routes: RouteDefs<Defs>,\n    pub outer_owner: Owner,\n    pub current_url: ArcRwSignal<Url>,\n    pub base: Option<Oco<'static, str>>,\n    pub fallback: FalFn,\n    pub set_is_routing: Option<SignalSetter<bool>>,\n    pub transition: bool,\n}\n\n/// Retained view state for the nested router.\npub(crate) struct NestedRouteViewState<Fal>\nwhere\n    Fal: Render,\n{\n    path: String,\n    current_url: ArcRwSignal<Url>,\n    outlets: Vec<RouteContext>,\n    // TODO loading fallback\n    #[allow(clippy::type_complexity)]\n    view: Rc<RefCell<EitherOf3State<(), Fal, AnyView>>>,\n    // held to keep the Owner alive until the router is dropped\n    #[allow(unused)]\n    outer_owner: Owner,\n    abort_navigation: ArcStoredValue<Option<AbortHandle>>,\n}\n\nimpl<Loc, Defs, FalFn, Fal> Render for NestedRoutesView<Loc, Defs, FalFn>\nwhere\n    Loc: LocationProvider,\n    Defs: MatchNestedRoutes,\n    FalFn: FnOnce() -> Fal,\n    Fal: Render + 'static,\n{\n    // TODO support fallback while loading\n    type State = NestedRouteViewState<Fal>;\n\n    fn build(self) -> Self::State {\n        let NestedRoutesView {\n            routes,\n            outer_owner,\n            current_url,\n            fallback,\n            base,\n            ..\n        } = self;\n\n        let mut loaders = Vec::new();\n        let mut outlets = Vec::new();\n        let url = current_url.read_untracked();\n        let path = url.path().to_string();\n\n        // match the route\n        let new_match = routes.match_route(url.path());\n\n        // start with an empty view because we'll be loading routes async\n        let view = EitherOf3::A(()).build();\n        let view = Rc::new(RefCell::new(view));\n        let matched_view = match new_match {\n            None => EitherOf3::B(fallback()),\n            Some(route) => {\n                route.build_nested_route(\n                    &url,\n                    base,\n                    &mut loaders,\n                    &mut outlets,\n                    &outer_owner,\n                );\n                drop(url);\n\n                EitherOf3::C(top_level_outlet(&outlets, &outer_owner))\n            }\n        };\n\n        Executor::spawn_local({\n            let view = Rc::clone(&view);\n            let loaders = mem::take(&mut loaders);\n            ScopedFuture::new(async move {\n                let triggers = join_all(loaders).await;\n                for trigger in triggers {\n                    trigger.notify();\n                }\n                matched_view.rebuild(&mut *view.borrow_mut());\n            })\n        });\n\n        NestedRouteViewState {\n            path,\n            current_url,\n            outlets,\n            view,\n            outer_owner,\n            abort_navigation: Default::default(),\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let url_snapshot = self.current_url.get_untracked();\n\n        // if the path is the same, we do not need to re-route\n        // we can just update the search query and go about our day\n        if url_snapshot.path() == state.path {\n            for outlet in &state.outlets {\n                outlet.url.set(url_snapshot.to_owned());\n            }\n            return;\n        }\n\n        // since the path didn't match, we'll update the retained path for future diffing\n        state.path.clear();\n        state.path.push_str(url_snapshot.path());\n\n        let new_match = self.routes.match_route(url_snapshot.path());\n\n        *state.current_url.write_untracked() = url_snapshot;\n\n        match new_match {\n            None => {\n                EitherOf3::<(), Fal, AnyView>::B((self.fallback)())\n                    .rebuild(&mut state.view.borrow_mut());\n                state.outlets.clear();\n                if let Some(loc) = self.location {\n                    loc.ready_to_complete();\n                }\n            }\n            Some(route) => {\n                if let Some(set_is_routing) = self.set_is_routing {\n                    set_is_routing.set(true);\n                }\n\n                let mut preloaders = Vec::new();\n                let mut full_loaders = Vec::new();\n                let different_level = route.rebuild_nested_route(\n                    &self.current_url.read_untracked(),\n                    self.base,\n                    &mut 0,\n                    &mut preloaders,\n                    &mut full_loaders,\n                    &mut state.outlets,\n                    self.set_is_routing.is_some(),\n                    0,\n                    &self.outer_owner,\n                );\n\n                let (abort_handle, abort_registration) =\n                    AbortHandle::new_pair();\n\n                if let Some(prev_handle) =\n                    state.abort_navigation.write_value().replace(abort_handle)\n                {\n                    prev_handle.abort();\n                }\n\n                let location = self.location.clone();\n                let is_back = location\n                    .as_ref()\n                    .map(|nav| nav.is_back().get_untracked())\n                    .unwrap_or(false);\n                Executor::spawn_local(async move {\n                    let triggers = Abortable::new(\n                        join_all(preloaders),\n                        abort_registration,\n                    );\n                    if let Ok(triggers) = triggers.await {\n                        // tell each one of the outlet triggers that it's ready\n                        let notify = move || {\n                            for trigger in triggers {\n                                trigger.notify();\n                            }\n                        };\n                        if self.transition {\n                            start_view_transition(\n                                different_level,\n                                is_back,\n                                notify,\n                            );\n                        } else {\n                            notify();\n                        }\n                    }\n                });\n\n                let abort_navigation = state.abort_navigation.clone();\n                Executor::spawn_local(async move {\n                    join_all(full_loaders).await;\n                    _ = abort_navigation.write_value().take();\n                    if let Some(set_is_routing) = self.set_is_routing {\n                        set_is_routing.set(false);\n                    }\n                    if let Some(loc) = location {\n                        loc.ready_to_complete();\n                    }\n                });\n\n                // if it was on the fallback, show the view instead\n                if matches!(state.view.borrow().state, EitherOf3::B(_)) {\n                    EitherOf3::<(), Fal, AnyView>::C(top_level_outlet(\n                        &state.outlets,\n                        &self.outer_owner,\n                    ))\n                    .rebuild(&mut *state.view.borrow_mut());\n                }\n            }\n        }\n    }\n}\n\nimpl<Loc, Defs, Fal, FalFn> AddAnyAttr for NestedRoutesView<Loc, Defs, FalFn>\nwhere\n    Loc: LocationProvider + Send,\n    Defs: MatchNestedRoutes + Send + 'static,\n    FalFn: FnOnce() -> Fal + Send + 'static,\n    Fal: RenderHtml + 'static,\n{\n    type Output<SomeNewAttr: leptos::attr::Attribute> =\n        NestedRoutesView<Loc, Defs, FalFn>;\n\n    fn add_any_attr<NewAttr: leptos::attr::Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        todo!()\n    }\n}\n\nimpl<Loc, Defs, FalFn, Fal> RenderHtml for NestedRoutesView<Loc, Defs, FalFn>\nwhere\n    Loc: LocationProvider + Send,\n    Defs: MatchNestedRoutes + Send + 'static,\n    FalFn: FnOnce() -> Fal + Send + 'static,\n    Fal: RenderHtml + 'static,\n{\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0; // TODO\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // if this is being run on the server for the first time, generating all possible routes\n        if RouteList::is_generating() {\n            // add routes\n            let (base, routes) = self.routes.generate_routes();\n            let routes = routes\n                .into_iter()\n                .map(|data| {\n                    let path = base\n                        .into_iter()\n                        .flat_map(|base| {\n                            iter::once(PathSegment::Static(\n                                base.to_string().into(),\n                            ))\n                        })\n                        .chain(data.segments)\n                        .collect::<Vec<_>>();\n                    RouteListing::new(\n                        path,\n                        data.ssr_mode,\n                        data.methods,\n                        data.regenerate,\n                    )\n                })\n                .collect::<Vec<_>>();\n\n            // add fallback\n            // TODO fix: causes overlapping route issues on Axum\n            /*routes.push(RouteListing::new(\n                [PathSegment::Static(\n                    base.unwrap_or_default().to_string().into(),\n                )],\n                SsrMode::Async,\n                [\n                    Method::Get,\n                    Method::Post,\n                    Method::Put,\n                    Method::Patch,\n                    Method::Delete,\n                ],\n                None,\n            ));*/\n\n            RouteList::register(RouteList::from(routes));\n        } else {\n            let NestedRoutesView {\n                routes,\n                outer_owner,\n                current_url,\n                fallback,\n                base,\n                ..\n            } = self;\n            let current_url = current_url.read_untracked();\n\n            let mut outlets = Vec::new();\n            let new_match = routes.match_route(current_url.path());\n            let view = match new_match {\n                None => Either::Left(fallback()),\n                Some(route) => {\n                    let mut loaders = Vec::new();\n                    route.build_nested_route(\n                        &current_url,\n                        base,\n                        &mut loaders,\n                        &mut outlets,\n                        &outer_owner,\n                    );\n\n                    // outlets will not send their views if the loaders are never polled\n                    // the loaders are async so that they can lazy-load routes in the browser,\n                    // but they should always be synchronously available on the server\n                    join_all(mem::take(&mut loaders))\n                        .now_or_never()\n                        .expect(\"async routes not supported in SSR\");\n\n                    Either::Right(top_level_outlet(&outlets, &outer_owner))\n                }\n            };\n            view.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let NestedRoutesView {\n            routes,\n            outer_owner,\n            current_url,\n            fallback,\n            base,\n            ..\n        } = self;\n        let current_url = current_url.read_untracked();\n\n        let mut outlets = Vec::new();\n        let new_match = routes.match_route(current_url.path());\n        let view = match new_match {\n            None => Either::Left(fallback()),\n            Some(route) => {\n                let mut loaders = Vec::new();\n                route.build_nested_route(\n                    &current_url,\n                    base,\n                    &mut loaders,\n                    &mut outlets,\n                    &outer_owner,\n                );\n\n                let preload_owners = outlets\n                    .iter()\n                    .map(|o| o.preload_owner.clone())\n                    .collect::<Vec<_>>();\n                outer_owner\n                    .with(|| Owner::on_cleanup(move || drop(preload_owners)));\n\n                // outlets will not send their views if the loaders are never polled\n                // the loaders are async so that they can lazy-load routes in the browser,\n                // but they should always be synchronously available on the server\n                join_all(mem::take(&mut loaders))\n                    .now_or_never()\n                    .expect(\"async routes not supported in SSR\");\n\n                Either::Right(top_level_outlet(&outlets, &outer_owner))\n            }\n        };\n        view.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let NestedRoutesView {\n            routes,\n            outer_owner,\n            current_url,\n            fallback,\n            base,\n            ..\n        } = self;\n\n        let mut loaders = Vec::new();\n        let mut outlets = Vec::new();\n        let url = current_url.read_untracked();\n        let path = url.path().to_string();\n\n        // match the route\n        let new_match = routes.match_route(url.path());\n\n        // start with an empty view because we'll be loading routes async\n        let view = Rc::new(RefCell::new(\n            match new_match {\n                None => EitherOf3::B(fallback()),\n                Some(route) => {\n                    route.build_nested_route(\n                        &url,\n                        base,\n                        &mut loaders,\n                        &mut outlets,\n                        &outer_owner,\n                    );\n                    drop(url);\n\n                    join_all(mem::take(&mut loaders)).now_or_never().expect(\n                        \"lazy routes not supported with hydrate_body(); use \\\n                         hydrate_lazy() instead\",\n                    );\n                    EitherOf3::C(top_level_outlet(&outlets, &outer_owner))\n                }\n            }\n            .hydrate::<FROM_SERVER>(cursor, position),\n        ));\n\n        NestedRouteViewState {\n            path,\n            current_url,\n            outlets,\n            view,\n            outer_owner,\n            abort_navigation: Default::default(),\n        }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let NestedRoutesView {\n            routes,\n            outer_owner,\n            current_url,\n            fallback,\n            base,\n            ..\n        } = self;\n\n        let mut loaders = Vec::new();\n        let mut outlets = Vec::new();\n        let url = current_url.read_untracked();\n        let path = url.path().to_string();\n\n        // match the route\n        let new_match = routes.match_route(url.path());\n\n        // start with an empty view because we'll be loading routes async\n        let view = Rc::new(RefCell::new(\n            match new_match {\n                None => EitherOf3::B(fallback()),\n                Some(route) => {\n                    route.build_nested_route(\n                        &url,\n                        base,\n                        &mut loaders,\n                        &mut outlets,\n                        &outer_owner,\n                    );\n                    drop(url);\n\n                    join_all(mem::take(&mut loaders)).await;\n                    EitherOf3::C(top_level_outlet(&outlets, &outer_owner))\n                }\n            }\n            .hydrate::<true>(cursor, position),\n        ));\n\n        NestedRouteViewState {\n            path,\n            current_url,\n            outlets,\n            view,\n            outer_owner,\n            abort_navigation: Default::default(),\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\ntype OutletViewFn = Box<dyn FnMut(Owner) -> Suspend<AnyView> + Send>;\n\npub(crate) struct RouteContext {\n    id: RouteMatchId,\n    trigger: ArcTrigger,\n    url: ArcRwSignal<Url>,\n    params: ArcRwSignal<ParamsMap>,\n    pub matched: ArcRwSignal<String>,\n    base: Option<Oco<'static, str>>,\n    view_fn: Arc<Mutex<OutletViewFn>>,\n    owner: Arc<Mutex<Option<Owner>>>,\n    preload_owner: Owner,\n    child: ChildRoute,\n}\n\n#[derive(Clone)]\npub(crate) struct ChildRoute(Arc<Mutex<Option<RouteContext>>>);\n\nimpl Debug for RouteContext {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"RouteContext\")\n            .field(\"id\", &self.id)\n            .field(\"trigger\", &self.trigger)\n            .field(\"url\", &self.url)\n            .field(\"params\", &self.params)\n            .field(\"matched\", &self.matched)\n            .field(\"base\", &self.base)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl Clone for RouteContext {\n    fn clone(&self) -> Self {\n        Self {\n            url: self.url.clone(),\n            id: self.id,\n            trigger: self.trigger.clone(),\n            params: self.params.clone(),\n            matched: self.matched.clone(),\n            base: self.base.clone(),\n            view_fn: Arc::clone(&self.view_fn),\n            owner: Arc::clone(&self.owner),\n            child: self.child.clone(),\n            preload_owner: self.preload_owner.clone(),\n        }\n    }\n}\n\ntrait AddNestedRoute {\n    fn build_nested_route(\n        self,\n        url: &Url,\n        base: Option<Oco<'static, str>>,\n        loaders: &mut Vec<Pin<Box<dyn Future<Output = ArcTrigger>>>>,\n        outlets: &mut Vec<RouteContext>,\n        outer_owner: &Owner,\n    );\n\n    #[allow(clippy::too_many_arguments)]\n    fn rebuild_nested_route(\n        self,\n        url: &Url,\n        base: Option<Oco<'static, str>>,\n        items: &mut usize,\n        loaders: &mut Vec<Pin<Box<dyn Future<Output = ArcTrigger>>>>,\n        full_loaders: &mut Vec<oneshot::Receiver<Option<Owner>>>,\n        outlets: &mut Vec<RouteContext>,\n        set_is_routing: bool,\n        level: u8,\n        outer_owner: &Owner,\n    ) -> u8;\n}\n\nimpl<Match> AddNestedRoute for Match\nwhere\n    Match: MatchInterface + MatchParams,\n{\n    fn build_nested_route(\n        self,\n        url: &Url,\n        base: Option<Oco<'static, str>>,\n        loaders: &mut Vec<Pin<Box<dyn Future<Output = ArcTrigger>>>>,\n        outlets: &mut Vec<RouteContext>,\n        outer_owner: &Owner,\n    ) {\n        let orig_url = url;\n\n        // the params signal can be updated to allow the same outlet to update to changes in the\n        // params, even if there's not a route match change\n        let params = ArcRwSignal::new(self.to_params().into_iter().collect());\n\n        // the URL signal is used for access to things like search query\n        // this is provided per nested route, specifically so that navigating *away* from a route\n        // does not continuing updating its URL signal, which could do things like triggering\n        // resources to run again\n        let url = ArcRwSignal::new(url.to_owned());\n\n        // the matched signal will also be updated on every match\n        // it's used for relative route resolution\n        let matched = ArcRwSignal::new(self.as_matched().to_string());\n        let (parent_params, parent_matches): (Vec<_>, Vec<_>) = outlets\n            .iter()\n            .map(|route| (route.params.clone(), route.matched.clone()))\n            .unzip();\n        let params_including_parents = {\n            let params = params.clone();\n            ArcMemo::new({\n                move |_| {\n                    parent_params\n                        .iter()\n                        .flat_map(|params| params.get().into_iter())\n                        .chain(params.get())\n                        .collect::<ParamsMap>()\n                }\n            })\n        };\n        let matched_including_parents = {\n            let matched = matched.clone();\n            ArcMemo::new({\n                move |_| {\n                    parent_matches\n                        .iter()\n                        .map(|matched| matched.get())\n                        .chain(iter::once(matched.get()))\n                        .collect::<String>()\n                }\n            })\n        };\n\n        // the trigger and channel will be used to send new boxed AnyViews to the Outlet;\n        // whenever we match a different route, the trigger will be triggered and a new view will\n        // be sent through the channel to be rendered by the Outlet\n        //\n        // combining a trigger and a channel allows us to pass ownership of the view;\n        // storing a view in a signal would mean we need to keep a copy stored in the signal and\n        // require that we can clone it out\n        let trigger = ArcTrigger::new();\n\n        // add this outlet to the end of the outlet stack used for diffing\n        let outlet = RouteContext {\n            id: self.as_id(),\n            url,\n            trigger: trigger.clone(),\n            params,\n            matched,\n            view_fn: Arc::new(Mutex::new(Box::new(|_owner| {\n                Suspend::new(Box::pin(async { ().into_any() }))\n            }))),\n            base: base.clone(),\n            child: ChildRoute(Arc::new(Mutex::new(None))),\n            owner: Arc::new(Mutex::new(None)),\n            preload_owner: outer_owner.child(),\n        };\n        if !outlets.is_empty() {\n            let prev_index = outlets.len().saturating_sub(1);\n            *outlets[prev_index].child.0.lock().or_poisoned() =\n                Some(outlet.clone());\n        }\n        outlets.push(outlet.clone());\n\n        // send the initial view through the channel, and recurse through the children\n        let (view, child) = self.into_view_and_child();\n\n        loaders.push(Box::pin(ScopedFuture::new({\n            let url = outlet.url.clone();\n            let matched = Matched(matched_including_parents);\n            let view_fn = Arc::clone(&outlet.view_fn);\n            let route_owner = Arc::clone(&outlet.owner);\n            let outlet = outlet.clone();\n            let params = params_including_parents.clone();\n            let url = url.clone();\n            let matched = matched.clone();\n            async move {\n                provide_context(params.clone());\n                provide_context(url.clone());\n                provide_context(matched.clone());\n                outlet\n                    .preload_owner\n                    .with(|| {\n                        provide_context(params.clone());\n                        provide_context(url.clone());\n                        provide_context(matched.clone());\n                        ScopedFuture::new(view.preload())\n                    })\n                    .await;\n                let child = outlet.child.clone();\n                *view_fn.lock().or_poisoned() =\n                    Box::new(move |owner_where_used| {\n                        *route_owner.lock().or_poisoned() =\n                            Some(owner_where_used.clone());\n                        let view = view.clone();\n                        let child = child.clone();\n                        let params = params.clone();\n                        let url = url.clone();\n                        let matched = matched.clone();\n                        owner_where_used.with({\n                            let matched = matched.clone();\n                            || {\n                                let child = child.clone();\n                                Suspend::new(Box::pin(async move {\n                                    provide_context(child.clone());\n                                    provide_context(params.clone());\n                                    provide_context(url.clone());\n                                    provide_context(matched.clone());\n                                    let view = SendWrapper::new(\n                                        ScopedFuture::new(view.choose()),\n                                    );\n                                    let view = view.await;\n                                    let view = MatchedRoute(\n                                        matched.0.get_untracked(),\n                                        view,\n                                    );\n\n                                    OwnedView::new(view).into_any()\n                                })\n                                    as Pin<\n                                        Box<\n                                            dyn Future<Output = AnyView> + Send,\n                                        >,\n                                    >)\n                            }\n                        })\n                    });\n                trigger\n            }\n        })));\n\n        // recursively continue building the tree\n        // this is important because to build the view, we need access to the outlet\n        // and the outlet will be returned from building this child\n        if let Some(child) = child {\n            child.build_nested_route(\n                orig_url,\n                base,\n                loaders,\n                outlets,\n                outer_owner,\n            );\n        }\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn rebuild_nested_route(\n        self,\n        url: &Url,\n        base: Option<Oco<'static, str>>,\n        items: &mut usize,\n        preloaders: &mut Vec<Pin<Box<dyn Future<Output = ArcTrigger>>>>,\n        full_loaders: &mut Vec<oneshot::Receiver<Option<Owner>>>,\n        outlets: &mut Vec<RouteContext>,\n        set_is_routing: bool,\n        level: u8,\n        outer_owner: &Owner,\n    ) -> u8 {\n        let (parent_params, parent_matches): (Vec<_>, Vec<_>) = outlets\n            .iter()\n            .take(*items)\n            .map(|route| (route.params.clone(), route.matched.clone()))\n            .unzip();\n\n        if outlets.get(*items).is_some() && *items > 0 {\n            *outlets[*items - 1].child.0.lock().or_poisoned() =\n                Some(outlets[*items].clone());\n        }\n\n        let current = outlets.get_mut(*items);\n        match current {\n            // if there's nothing currently in the routes at this point, build from here\n            None => {\n                self.build_nested_route(\n                    url,\n                    base,\n                    preloaders,\n                    outlets,\n                    outer_owner,\n                );\n                level\n            }\n            Some(current) => {\n                // a unique ID for each route, which allows us to compare when we get new matches\n                // if two IDs are the same, we do not rerender, but only update the params\n                // if the IDs are different, we need to replace the remainder of the tree\n                let id = self.as_id();\n\n                // build new params and matched strings\n                let new_params =\n                    self.to_params().into_iter().collect::<ParamsMap>();\n                let new_match = self.as_matched().to_owned();\n\n                let (view, child) = self.into_view_and_child();\n\n                // if the IDs don't match, everything below in the tree needs to be swapped:\n                // 1) replace this outlet with the next view, with a new owner and new signals for\n                //    URL/params\n                // 2) remove other outlets that are lower down in the match tree\n                // 3) build the rest of the list of matched routes, rather than rebuilding,\n                //    as all lower outlets needs to be replaced\n                if id != current.id {\n                    // update the ID of the match at this depth, so that futures rebuilds diff\n                    // against the new ID, not the original one\n                    current.id = id;\n\n                    // create new URL and params signals\n                    let old_url = mem::replace(\n                        &mut current.url,\n                        ArcRwSignal::new(url.to_owned()),\n                    );\n                    let old_params = mem::replace(\n                        &mut current.params,\n                        ArcRwSignal::new(new_params),\n                    );\n                    let old_matched = mem::replace(\n                        &mut current.matched,\n                        ArcRwSignal::new(new_match),\n                    );\n                    let old_preload_owner = mem::replace(\n                        &mut current.preload_owner,\n                        outer_owner.child(),\n                    );\n                    let matched_including_parents = {\n                        ArcMemo::new({\n                            let matched = current.matched.clone();\n                            move |_| {\n                                parent_matches\n                                    .iter()\n                                    .map(|matched| matched.get())\n                                    .chain(iter::once(matched.get()))\n                                    .collect::<String>()\n                            }\n                        })\n                    };\n                    let params_including_parents = {\n                        let params = current.params.clone();\n                        ArcMemo::new({\n                            move |_| {\n                                parent_params\n                                    .iter()\n                                    .flat_map(|params| params.get().into_iter())\n                                    .chain(params.get())\n                                    .collect::<ParamsMap>()\n                            }\n                        })\n                    };\n\n                    let (full_tx, full_rx) = oneshot::channel();\n                    let full_tx = Mutex::new(Some(full_tx));\n                    full_loaders.push(full_rx);\n                    let outlet = current.clone();\n\n                    // send the new view, with the new owner, through the channel to the Outlet,\n                    // and notify the trigger so that the reactive view inside the Outlet tracking\n                    // the trigger runs again\n                    preloaders.push(Box::pin(ScopedFuture::new({\n                        let trigger = current.trigger.clone();\n                        let url = current.url.clone();\n                        let matched = Matched(matched_including_parents);\n                        let view_fn = Arc::clone(&current.view_fn);\n                        let route_owner = Arc::clone(&current.owner);\n                        let child = outlet.child.clone();\n                        async move {\n                            let child = child.clone();\n                            outlet\n                                .preload_owner\n                                .with(|| {\n                                    provide_context(\n                                        params_including_parents.clone(),\n                                    );\n                                    provide_context(url.clone());\n                                    provide_context(matched.clone());\n                                    ScopedFuture::new(async {\n                                        if set_is_routing {\n                                            AsyncTransition::run(|| {\n                                                view.preload()\n                                            })\n                                            .await;\n                                        } else {\n                                            view.preload().await;\n                                        }\n                                    })\n                                })\n                                .await;\n                            *view_fn.lock().or_poisoned() =\n                                Box::new(move |owner_where_used| {\n                                    let prev_owner = route_owner\n                                        .lock()\n                                        .or_poisoned()\n                                        .replace(owner_where_used.clone());\n                                    let view = view.clone();\n                                    let full_tx =\n                                        full_tx.lock().or_poisoned().take();\n                                    let child = child.clone();\n                                    let params =\n                                        params_including_parents.clone();\n                                    let url = url.clone();\n                                    let matched = matched.clone();\n                                    Suspend::new(Box::pin(async move {\n                                        let view = SendWrapper::new(\n                                            owner_where_used.with(|| {\n                                                provide_context(child.clone());\n                                                provide_context(params);\n                                                provide_context(url);\n                                                provide_context(matched);\n                                                ScopedFuture::new(async move {\n                                                    if set_is_routing {\n                                                        AsyncTransition::run(\n                                                            || view.choose(),\n                                                        )\n                                                        .await\n                                                    } else {\n                                                        view.choose().await\n                                                    }\n                                                })\n                                            }),\n                                        );\n\n                                        let view = view.await;\n\n                                        if let Some(tx) = full_tx {\n                                            _ = tx.send(prev_owner);\n                                        }\n                                        owner_where_used.with(|| {\n                                            OwnedView::new(view).into_any()\n                                        })\n                                    }))\n                                });\n\n                            drop(old_params);\n                            drop(old_url);\n                            drop(old_matched);\n                            drop(old_preload_owner);\n                            trigger\n                        }\n                    })));\n\n                    // remove all the items lower in the tree\n                    // if this match is different, all its children will also be different\n                    outlets.truncate(*items + 1);\n\n                    // if this children has matches, then rebuild the lower section of the tree\n                    if let Some(child) = child {\n                        child.build_nested_route(\n                            url,\n                            base,\n                            preloaders,\n                            outlets,\n                            outer_owner,\n                        );\n                    } else {\n                        *outlets[*items].child.0.lock().or_poisoned() = None;\n                    }\n\n                    return level;\n                }\n\n                // otherwise, set the params and URL signals,\n                // then just keep rebuilding recursively, checking the remaining routes in the list\n                current.matched.set(new_match);\n                current.params.set(new_params);\n                current.url.set(url.to_owned());\n                if let Some(child) = child {\n                    *items += 1;\n                    child.rebuild_nested_route(\n                        url,\n                        base,\n                        items,\n                        preloaders,\n                        full_loaders,\n                        outlets,\n                        set_is_routing,\n                        level + 1,\n                        outer_owner,\n                    )\n                } else {\n                    *current.child.0.lock().or_poisoned() = None;\n                    level\n                }\n            }\n        }\n    }\n}\n\nimpl<Fal> Mountable for NestedRouteViewState<Fal>\nwhere\n    Fal: Render,\n{\n    fn unmount(&mut self) {\n        self.view.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &leptos::tachys::renderer::types::Element,\n        marker: Option<&leptos::tachys::renderer::types::Node>,\n    ) {\n        self.view.mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.view.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<tachys::renderer::types::Element> {\n        self.view.elements()\n    }\n}\n\nfn top_level_outlet(outlets: &[RouteContext], outer_owner: &Owner) -> AnyView {\n    let outlet = outlets.first().unwrap();\n    let child = outlet.child.clone();\n    let view_fn = outlet.view_fn.clone();\n    let trigger = outlet.trigger.clone();\n    outer_owner.clone().with(|| {\n        provide_context(child.clone());\n        let outer_owner = outer_owner.clone();\n        (move || {\n            trigger.track();\n            let mut view_fn = view_fn.lock().or_poisoned();\n            view_fn(outer_owner.child())\n        })\n        .into_any()\n    })\n}\n\n/// Displays the child route nested in a parent route, allowing you to control exactly where\n/// that child route is displayed. Renders nothing if there is no nested child.\n#[component]\npub fn Outlet() -> impl RenderHtml\nwhere\n{\n    let ChildRoute(child) = use_context()\n        .expect(\"<Outlet/> used without RouteContext being provided.\");\n    let child = child.lock().or_poisoned().clone();\n    let outer_owner = Owner::current().unwrap();\n    child.map(|child| {\n        move || {\n            child.trigger.track();\n            let mut view_fn = child.view_fn.lock().or_poisoned();\n            view_fn(outer_owner.child())\n        }\n    })\n}\n"
  },
  {
    "path": "router/src/params.rs",
    "content": "use crate::location::Url;\nuse std::{borrow::Cow, str::FromStr, sync::Arc};\nuse thiserror::Error;\n\ntype ParamsMapInner = Vec<(Cow<'static, str>, Vec<String>)>;\n\n/// A key-value map of the current named route params and their values.\n#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]\npub struct ParamsMap(ParamsMapInner);\n\nimpl ParamsMap {\n    /// Creates an empty map.\n    #[inline(always)]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Creates an empty map with the given capacity.\n    #[inline(always)]\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self(Vec::with_capacity(capacity))\n    }\n\n    /// Inserts a value into the map.\n    ///\n    /// If a value with that key already exists, the new value will be added to it.\n    /// To replace the value instead, see [`replace`](Self::replace).\n    pub fn insert(&mut self, key: impl Into<Cow<'static, str>>, value: String) {\n        let value = Url::unescape(&value);\n\n        let key = key.into();\n        if let Some(prev) = self.0.iter_mut().find(|(k, _)| k == &key) {\n            prev.1.push(value);\n        } else {\n            self.0.push((key, vec![value]));\n        }\n    }\n\n    /// Inserts a value into the map, replacing any existing value for that key.\n    pub fn replace(\n        &mut self,\n        key: impl Into<Cow<'static, str>>,\n        value: String,\n    ) {\n        let value = Url::unescape(&value);\n\n        let key = key.into();\n        if let Some(prev) = self.0.iter_mut().find(|(k, _)| k == &key) {\n            prev.1.clear();\n            prev.1.push(value);\n        } else {\n            self.0.push((key, vec![value]));\n        }\n    }\n\n    /// Gets the most-recently-added value of this param from the map.\n    pub fn get(&self, key: &str) -> Option<String> {\n        self.get_str(key).map(ToOwned::to_owned)\n    }\n\n    /// Gets all references to a param of this name from the map.\n    pub fn get_all(&self, key: &str) -> Option<Vec<String>> {\n        self.0\n            .iter()\n            .find_map(|(k, v)| if k == key { Some(v.clone()) } else { None })\n    }\n\n    /// Gets an iterator for all the most-recently-added values on the map\n    pub fn latest_values(&self) -> ParamsMapIterRef<'_> {\n        let inner: Vec<_> = self\n            .0\n            .iter()\n            .flat_map(|(k, v)| v.last().map(|v| (k, v.as_str())))\n            .collect();\n        ParamsMapIterRef(inner.into_iter())\n    }\n\n    /// Gets a reference to the most-recently-added value of this param from the map.\n    pub fn get_str(&self, key: &str) -> Option<&str> {\n        self.0.iter().find_map(|(k, v)| {\n            if k == key {\n                v.last().map(|i| i.as_str())\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Removes a value from the map.\n    #[inline(always)]\n    pub fn remove(&mut self, key: &str) -> Option<Vec<String>> {\n        for i in 0..self.0.len() {\n            if self.0[i].0 == key {\n                return Some(self.0.swap_remove(i).1);\n            }\n        }\n        None\n    }\n\n    /// Converts the map to a query string.\n    pub fn to_query_string(&self) -> String {\n        let mut buf = String::new();\n        if !self.0.is_empty() {\n            buf.push('?');\n            for (k, vs) in &self.0 {\n                for v in vs {\n                    buf.push_str(&Url::escape(k));\n                    buf.push('=');\n                    buf.push_str(&Url::escape(v));\n                    buf.push('&');\n                }\n            }\n            if buf.len() > 1 {\n                buf.pop();\n            }\n        }\n        buf\n    }\n}\n\nimpl<K, V> FromIterator<(K, V)> for ParamsMap\nwhere\n    K: Into<Cow<'static, str>>,\n    V: Into<String>,\n{\n    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {\n        let mut map = Self::new();\n\n        for (key, value) in iter {\n            map.insert(key, value.into());\n        }\n        map\n    }\n}\n\nimpl IntoIterator for ParamsMap {\n    type Item = (Cow<'static, str>, String);\n    type IntoIter = ParamsMapIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        let inner = self.0.into_iter().fold(vec![], |mut c, (k, vs)| {\n            for v in vs {\n                c.push((k.clone(), v));\n            }\n            c\n        });\n        ParamsMapIter(inner.into_iter())\n    }\n}\n\nimpl<'a> IntoIterator for &'a ParamsMap {\n    type Item = (&'a Cow<'static, str>, &'a str);\n    type IntoIter = ParamsMapIterRef<'a>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        let inner: Vec<_> = self\n            .0\n            .iter()\n            .flat_map(|(k, v)| v.iter().map(move |v| (k, v.as_str())))\n            .collect();\n        ParamsMapIterRef(inner.into_iter())\n    }\n}\n\n/// An iterator over the keys and values of a [`ParamsMap`].\n#[derive(Debug)]\npub struct ParamsMapIter(\n    <Vec<(Cow<'static, str>, String)> as IntoIterator>::IntoIter,\n);\n\nimpl Iterator for ParamsMapIter {\n    type Item = (Cow<'static, str>, String);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next()\n    }\n}\n\n/// An iterator over the references of the keys and values of a [`ParamMap`].\n#[derive(Debug)]\npub struct ParamsMapIterRef<'a>(\n    <Vec<(&'a Cow<'static, str>, &'a str)> as IntoIterator>::IntoIter,\n);\n\nimpl<'a> Iterator for ParamsMapIterRef<'a> {\n    type Item = (&'a Cow<'static, str>, &'a str);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next()\n    }\n}\n\n/// A simple method of deserializing key-value data (like route params or URL search)\n/// into a concrete data type. `Self` should typically be a struct in which\n/// each field's type implements [`FromStr`].\npub trait Params\nwhere\n    Self: Sized,\n{\n    /// Attempts to deserialize the map into the given type.\n    fn from_map(map: &ParamsMap) -> Result<Self, ParamsError>;\n}\n\nimpl Params for () {\n    #[inline(always)]\n    fn from_map(_map: &ParamsMap) -> Result<Self, ParamsError> {\n        Ok(())\n    }\n}\n\n/// Converts some parameter value from the URL into a typed parameter with the given name.\npub trait IntoParam\nwhere\n    Self: Sized,\n{\n    /// Converts the param.\n    fn into_param(value: Option<&str>, name: &str)\n        -> Result<Self, ParamsError>;\n}\n\nimpl<T> IntoParam for Option<T>\nwhere\n    T: FromStr,\n    <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,\n{\n    fn into_param(\n        value: Option<&str>,\n        _name: &str,\n    ) -> Result<Self, ParamsError> {\n        match value {\n            None => Ok(None),\n            Some(value) => match T::from_str(value) {\n                Ok(value) => Ok(Some(value)),\n                Err(e) => Err(ParamsError::Params(Arc::new(e))),\n            },\n        }\n    }\n}\n\n/// Helpers for the `Params` derive macro to allow specialization without nightly.\npub mod macro_helpers {\n    use crate::params::{IntoParam, ParamsError};\n    use std::{str::FromStr, sync::Arc};\n\n    /// This struct is never actually created; it just exists so that we can impl associated\n    /// functions on it.\n    pub struct Wrapper<T>(T);\n\n    impl<T: IntoParam> Wrapper<T> {\n        /// This is the 'preferred' impl to be used for all `T` that implement `IntoParam`.\n        /// Because it is directly on the struct, the compiler will pick this over the impl from\n        /// the `Fallback` trait.\n        #[inline]\n        pub fn __into_param(\n            value: Option<&str>,\n            name: &str,\n        ) -> Result<T, ParamsError> {\n            T::into_param(value, name)\n        }\n    }\n\n    /// If the Fallback trait is in scope, then the compiler has two possible implementations for\n    /// `__into_params`. It will pick the one from this trait if the inherent one doesn't exist.\n    /// (which it won't if `T` does not implement `IntoParam`)\n    pub trait Fallback<T>: Sized\n    where\n        T: FromStr,\n        <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,\n    {\n        /// Fallback function in case the inherent impl on the Wrapper struct does not exist for\n        /// `T`\n        #[inline]\n        fn __into_param(\n            value: Option<&str>,\n            name: &str,\n        ) -> Result<T, ParamsError> {\n            let value = value\n                .ok_or_else(|| ParamsError::MissingParam(name.to_string()))?;\n            T::from_str(value).map_err(|e| ParamsError::Params(Arc::new(e)))\n        }\n    }\n\n    impl<T> Fallback<T> for Wrapper<T>\n    where\n        T: FromStr,\n        <T as FromStr>::Err: std::error::Error + Send + Sync + 'static,\n    {\n    }\n}\n/// Errors that can occur while parsing params using [`Params`].\n#[derive(Error, Debug, Clone)]\npub enum ParamsError {\n    /// A field was missing from the route params.\n    #[error(\"could not find parameter {0}\")]\n    MissingParam(String),\n    /// Something went wrong while deserializing a field.\n    #[error(\"failed to deserialize parameters\")]\n    Params(Arc<dyn std::error::Error + Send + Sync>),\n}\n\nimpl PartialEq for ParamsError {\n    fn eq(&self, other: &Self) -> bool {\n        match (self, other) {\n            (Self::MissingParam(l0), Self::MissingParam(r0)) => l0 == r0,\n            (Self::Params(_), Self::Params(_)) => false,\n            _ => false,\n        }\n    }\n}\n\n#[cfg(all(test, feature = \"ssr\"))]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn paramsmap_to_query_string() {\n        let mut map = ParamsMap::new();\n        let key = \"param\".to_string();\n        let value1 = \"a\".to_string();\n        let value2 = \"b\".to_string();\n        map.insert(key.clone(), value1);\n        map.insert(key, value2);\n        let query_string = map.to_query_string();\n        assert_eq!(&query_string, \"?param=a&param=b\")\n    }\n}\n"
  },
  {
    "path": "router/src/reactive.rs",
    "content": "use crate::{\n    location::Location,\n    matching::Params,\n    route::MatchedRoute,\n    router::{FallbackOrView, Router},\n    static_render::StaticDataMap,\n    PathSegment, RouteList, RouteListing, SsrMode,\n};\nuse reactive_graph::{\n    memo::Memo,\n    signal::ArcRwSignal,\n    traits::{SignalGet, SignalSet, SignalWith, Track},\n    untrack, Owner,\n};\nuse std::{marker::PhantomData, mem};\nuse tachydom::{\n    hydration::Cursor,\n    log,\n    renderer::Renderer,\n    ssr::StreamBuilder,\n    view::{Mountable, Position, PositionState, Render, RenderHtml},\n};\n\n#[allow(non_snake_case)]\npub fn ReactiveRouter<Rndr, Loc, DefFn, Defs, FallbackFn, Fallback>(\n    mut location: Loc,\n    routes: DefFn,\n    fallback: FallbackFn,\n) -> impl RenderHtml\nwhere\n    DefFn: Fn() -> Defs + 'static,\n    Defs: 'static,\n    Loc: Location + Clone + 'static,\n    Rndr: Renderer + 'static,\n    Rndr::Element: Clone,\n    Rndr::Node: Clone,\n    FallbackFn: Fn() -> Fallback + Clone + 'static,\n    Fallback: Render + 'static,\n    Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,\n    <Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: RenderHtml,\n{\n    // create a reactive URL signal that will drive the router view\n    let url = ArcRwSignal::new(location.try_to_url().unwrap_or_default());\n\n    // initialize the location service with a router hook that will update\n    // this URL signal\n    location.set_navigation_hook({\n        let url = url.clone();\n        move |new_url| {\n            tachydom::log(&format!(\"setting url to {new_url:?}\"));\n            url.set(new_url)\n        }\n    });\n    location.init();\n\n    // return a reactive router that will update if and only if the URL signal changes\n    let owner = Owner::current().unwrap();\n    move || {\n        url.track();\n        ReactiveRouterInner {\n            owner: owner.clone(),\n            inner: Router::new(location.clone(), routes(), fallback.clone()),\n            fal: PhantomData,\n        }\n    }\n}\n\nstruct ReactiveRouterInner<Rndr, Loc, Defs, FallbackFn, Fallback>\nwhere\n    Rndr: Renderer,\n{\n    owner: Owner,\n    inner: Router<Rndr, Loc, Defs, FallbackFn>,\n    fal: PhantomData<Fallback>,\n}\n\nimpl<Rndr, Loc, Defs, FallbackFn, Fallback> Render\n    for ReactiveRouterInner<Rndr, Loc, Defs, FallbackFn, Fallback>\nwhere\n    Loc: Location,\n    Rndr: Renderer,\n    Rndr::Element: Clone,\n    Rndr::Node: Clone,\n    FallbackFn: Fn() -> Fallback,\n    Fallback: Render,\n    Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,\n    <Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: Render,\n{\n    type State =\n        ReactiveRouterInnerState<Rndr, Loc, Defs, FallbackFn, Fallback>;\n\n    fn build(self) -> Self::State {\n        let (prev_id, inner) = self.inner.fallback_or_view();\n        let owner = self.owner.with(Owner::new);\n        ReactiveRouterInnerState {\n            inner: owner.with(|| inner.build()),\n            owner,\n            prev_id,\n            fal: PhantomData,\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (new_id, view) = self.inner.fallback_or_view();\n        if new_id != state.prev_id {\n            state.owner = self.owner.with(Owner::new)\n            // previous root is dropped here -- TODO check if that's correct or should wait\n        };\n        state.owner.with(|| view.rebuild(&mut state.inner));\n    }\n}\n\nimpl<Rndr, Loc, Defs, FallbackFn, Fallback> RenderHtml\n    for ReactiveRouterInner<Rndr, Loc, Defs, FallbackFn, Fallback>\nwhere\n    Loc: Location,\n    Rndr: Renderer,\n    Rndr::Element: Clone,\n    Rndr::Node: Clone,\n    FallbackFn: Fn() -> Fallback,\n    Fallback: Render,\n    Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,\n    <Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: RenderHtml,\n{\n    const MIN_LENGTH: usize = <<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output as RenderHtml>::MIN_LENGTH;\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool, extra_attrs: Vec<AnyAttribute>) {\n        // if this is being run on the server for the first time, generating all possible routes\n        if RouteList::is_generating() {\n            let mut routes = RouteList::new();\n\n            // add routes\n            self.inner.generate_route_list(&mut routes);\n\n            // add fallback\n            routes.push(RouteListing::from_path([PathSegment::Static(\n                \"\".into(),\n            )]));\n\n            RouteList::register(routes);\n        } else {\n            let (id, view) = self.inner.fallback_or_view();\n            view.to_html_with_buf(buf, position, escape)\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool, extra_attrs: Vec<AnyAttribute>) where\n        Self: Sized,\n    {\n        self.inner\n            .fallback_or_view()\n            .1\n            .to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape)\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let (prev_id, inner) = self.inner.fallback_or_view();\n        let owner = self.owner.with(Owner::new);\n        ReactiveRouterInnerState {\n            inner: owner\n                .with(|| inner.hydrate::<FROM_SERVER>(cursor, position)),\n            owner,\n            prev_id,\n            fal: PhantomData,\n        }\n    }\n}\n\nstruct ReactiveRouterInnerState<Rndr, Loc, Defs, FallbackFn, Fallback>\nwhere\n    Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,\n    <Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: Render,\n    Rndr: Renderer,\n{\n    owner: Owner,\n    prev_id: &'static str,\n    inner: <<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output as Render>::State,\n    fal: PhantomData<Fallback>,\n}\n\nimpl<Rndr, Loc, Defs, FallbackFn, Fallback> Mountable\n    for ReactiveRouterInnerState<Rndr, Loc, Defs, FallbackFn, Fallback>\nwhere\n    Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,\n    <Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: Render,\n    Rndr: Renderer,\n{\n    fn unmount(&mut self) {\n        self.inner.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &leptos::tachys::renderer::types::Element,\n        marker: Option<&leptos::tachys::renderer::types::Node>,\n    ) {\n        self.inner.mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.inner.insert_before_this(child)\n    }\n}\n\npub struct ReactiveMatchedRoute {\n    pub(crate) search_params: ArcRwSignal<Params<String>>,\n    pub(crate) params: ArcRwSignal<Params<&'static str>>,\n    pub(crate) matched: ArcRwSignal<String>,\n}\n\nimpl ReactiveMatchedRoute {\n    pub fn param(&self, key: &str) -> Memo<Option<String>> {\n        let params = self.params.clone();\n        let key = key.to_owned();\n        Memo::new(move |_| {\n            params.with(|p| {\n                p.iter().find(|n| n.0 == key).map(|(_, v)| v.to_string())\n            })\n        })\n    }\n\n    pub fn search(&self, key: &str) -> Memo<Option<String>> {\n        let params = self.search_params.clone();\n        let key = key.to_owned();\n        Memo::new(move |_| {\n            params.with(|p| {\n                p.iter().find(|n| n.0 == key).map(|(_, v)| v.to_string())\n            })\n        })\n    }\n}\n\npub fn reactive_route<ViewFn, View>(\n    view_fn: ViewFn,\n) -> impl Fn(MatchedRoute) -> ReactiveRoute<ViewFn, View>\nwhere\n    ViewFn: Fn(&ReactiveMatchedRoute) -> View + Clone,\n    View: Render,\n    Rndr: Renderer,\n{\n    move |matched| ReactiveRoute {\n        view_fn: view_fn.clone(),\n        matched,\n        ty: PhantomData,\n    }\n}\n\npub struct ReactiveRoute<ViewFn, View>\nwhere\n    ViewFn: Fn(&ReactiveMatchedRoute) -> View,\n    View: Render,\n    Rndr: Renderer,\n{\n    view_fn: ViewFn,\n    matched: MatchedRoute,\n    ty: PhantomData,\n}\n\nimpl<ViewFn, View> Render for ReactiveRoute<ViewFn, View>\nwhere\n    ViewFn: Fn(&ReactiveMatchedRoute) -> View,\n    View: Render,\n    Rndr: Renderer,\n{\n    type State = ReactiveRouteState<View::State>;\n\n    fn build(self) -> Self::State {\n        let MatchedRoute {\n            search_params,\n            params,\n            matched,\n        } = self.matched;\n        let matched = ReactiveMatchedRoute {\n            search_params: ArcRwSignal::new(search_params),\n            params: ArcRwSignal::new(params),\n            matched: ArcRwSignal::new(matched),\n        };\n        let view_state = untrack(|| (self.view_fn)(&matched).build());\n        ReactiveRouteState {\n            matched,\n            view_state,\n        }\n    }\n\n    fn rebuild(mut self, state: &mut Self::State) {\n        let ReactiveRouteState { matched, .. } = state;\n        matched\n            .search_params\n            .set(mem::take(&mut self.matched.search_params));\n        matched.params.set(mem::take(&mut self.matched.params));\n        matched.matched.set(mem::take(&mut self.matched.matched));\n    }\n}\n\nimpl<ViewFn, View> RenderHtml for ReactiveRoute<ViewFn, View>\nwhere\n    ViewFn: Fn(&ReactiveMatchedRoute) -> View,\n    View: RenderHtml,\n    Rndr: Renderer,\n    Rndr::Node: Clone,\n    Rndr::Element: Clone,\n{\n    const MIN_LENGTH: usize = 0;\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool, extra_attrs: Vec<AnyAttribute>) {\n        let MatchedRoute {\n            search_params,\n            params,\n            matched,\n        } = self.matched;\n        let matched = ReactiveMatchedRoute {\n            search_params: ArcRwSignal::new(search_params),\n            params: ArcRwSignal::new(params),\n            matched: ArcRwSignal::new(matched),\n        };\n        untrack(|| {\n            (self.view_fn)(&matched).to_html_with_buf(buf, position, escape)\n        });\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool, extra_attrs: Vec<AnyAttribute>) where\n        Self: Sized,\n    {\n        let MatchedRoute {\n            search_params,\n            params,\n            matched,\n        } = self.matched;\n        let matched = ReactiveMatchedRoute {\n            search_params: ArcRwSignal::new(search_params),\n            params: ArcRwSignal::new(params),\n            matched: ArcRwSignal::new(matched),\n        };\n        untrack(|| {\n            (self.view_fn)(&matched)\n                .to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape)\n        });\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let MatchedRoute {\n            search_params,\n            params,\n            matched,\n        } = self.matched;\n        let matched = ReactiveMatchedRoute {\n            search_params: ArcRwSignal::new(search_params),\n            params: ArcRwSignal::new(params),\n            matched: ArcRwSignal::new(matched),\n        };\n        let view_state = untrack(|| {\n            (self.view_fn)(&matched).hydrate::<FROM_SERVER>(cursor, position)\n        });\n        ReactiveRouteState {\n            matched,\n            view_state,\n        }\n    }\n}\n\npub struct ReactiveRouteState<State> {\n    view_state: State,\n    matched: ReactiveMatchedRoute,\n}\n\nimpl<State> Drop for ReactiveRouteState<State> {\n    fn drop(&mut self) {\n        log(\"dropping ReactiveRouteState\");\n    }\n}\n\nimpl<T> Mountable for ReactiveRouteState<T>\nwhere\n    T: Mountable,\n{\n    fn unmount(&mut self) {\n        self.view_state.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &<R as Renderer>::Element,\n        marker: Option<&<R as Renderer>::Node>,\n    ) {\n        self.view_state.mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.view_state.insert_before_this(child)\n    }\n}\n"
  },
  {
    "path": "router/src/ssr_mode.rs",
    "content": "use crate::static_routes::StaticRoute;\n\n/// Indicates which rendering mode should be used for this route during server-side rendering.\n///\n/// Leptos supports the following ways of rendering HTML that contains `async` data loaded\n/// under `<Suspense/>`.\n/// 1. **Synchronous** (use any mode except `Async`, don't depend on any resource): Serve an HTML shell that includes `fallback` for any `Suspense`. Load data on the client (using `create_local_resource`), replacing `fallback` once they're loaded.\n///     - *Pros*: App shell appears very quickly: great TTFB (time to first byte).\n///     - *Cons*: Resources load relatively slowly; you need to wait for JS + Wasm to load before even making a request.\n/// 2. **Out-of-order streaming** (`OutOfOrder`, the default): Serve an HTML shell that includes `fallback` for any `Suspense`. Load data on the **server**, streaming it down to the client as it resolves, and streaming down HTML for `Suspense` nodes.\n///     - *Pros*: Combines the best of **synchronous** and `Async`, with a very fast shell and resources that begin loading on the server.\n///     - *Cons*: Requires JS for suspended fragments to appear in correct order. Weaker meta tag support when it depends on data that's under suspense (has already streamed down `<head>`)\n/// 3. **Partially-blocked out-of-order streaming** (`PartiallyBlocked`): Using `create_blocking_resource` with out-of-order streaming still sends fallbacks and relies on JavaScript to fill them in with the fragments. Partially-blocked streaming does this replacement on the server, making for a slower response but requiring no JavaScript to show blocking resources.\n///     - *Pros*: Works better if JS is disabled.\n///     - *Cons*: Slower initial response because of additional string manipulation on server.\n/// 4. **In-order streaming** (`InOrder`): Walk through the tree, returning HTML synchronously as in synchronous rendering and out-of-order streaming until you hit a `Suspense`. At that point, wait for all its data to load, then render it, then the rest of the tree.\n///     - *Pros*: Does not require JS for HTML to appear in correct order.\n///     - *Cons*: Loads the shell more slowly than out-of-order streaming or synchronous rendering because it needs to pause at every `Suspense`. Cannot begin hydration until the entire page has loaded, so earlier pieces\n///       of the page will not be interactive until the suspended chunks have loaded.\n/// 5. **`Async`**: Load all resources on the server. Wait until all data are loaded, and render HTML in one sweep.\n///     - *Pros*: Better handling for meta tags (because you know async data even before you render the `<head>`). Faster complete load than **synchronous** because async resources begin loading on server.\n///     - *Cons*: Slower load time/TTFB: you need to wait for all async resources to load before displaying anything on the client.\n/// 6. **`Static`**: Renders the page when the server starts up, or incrementally, using the\n///    configuration provided by a [`StaticRoute`].\n///\n/// The mode defaults to out-of-order streaming. For a path that includes multiple nested routes, the most\n/// restrictive mode will be used: i.e., if even a single nested route asks for `Async` rendering, the whole initial\n/// request will be rendered `Async`. (`Async` is the most restricted requirement, followed by `InOrder`, `PartiallyBlocked`, and `OutOfOrder`.)\n#[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]\npub enum SsrMode {\n    /// **Out-of-order streaming** (`OutOfOrder`, the default): Serve an HTML shell that includes `fallback` for any `Suspense`. Load data on the **server**, streaming it down to the client as it resolves, and streaming down HTML for `Suspense` nodes.\n    ///     - *Pros*: Combines the best of **synchronous** and `Async`, with a very fast shell and resources that begin loading on the server.\n    ///     - *Cons*: Requires JS for suspended fragments to appear in correct order. Weaker meta tag support when it depends on data that's under suspense (has already streamed down `<head>`)\n    #[default]\n    OutOfOrder,\n    /// **Partially-blocked out-of-order streaming** (`PartiallyBlocked`): Using `create_blocking_resource` with out-of-order streaming still sends fallbacks and relies on JavaScript to fill them in with the fragments. Partially-blocked streaming does this replacement on the server, making for a slower response but requiring no JavaScript to show blocking resources.\n    ///     - *Pros*: Works better if JS is disabled.\n    ///     - *Cons*: Slower initial response because of additional string manipulation on server.\n    PartiallyBlocked,\n    /// **In-order streaming** (`InOrder`): Walk through the tree, returning HTML synchronously as in synchronous rendering and out-of-order streaming until you hit a `Suspense`. At that point, wait for all its data to load, then render it, then the rest of the tree.\n    ///     - *Pros*: Does not require JS for HTML to appear in correct order.\n    ///     - *Cons*: Loads the shell more slowly than out-of-order streaming or synchronous rendering because it needs to pause at every `Suspense`. Cannot begin hydration until the entire page has loaded, so earlier pieces\n    ///       of the page will not be interactive until the suspended chunks have loaded.\n    InOrder,\n    /// **`Async`**: Load all resources on the server. Wait until all data are loaded, and render HTML in one sweep.\n    ///    - *Pros*: Better handling for meta tags (because you know async data even before you render the `<head>`). Faster complete load than **synchronous** because async resources begin loading on server.\n    ///    - *Cons*: Slower load time/TTFB: you need to wait for all async resources to load before displaying anything on the client.\n    Async,\n    /// **`Static`**: Renders the page when the server starts up, or incrementally, using the\n    ///    configuration provided by a [`StaticRoute`].\n    Static(StaticRoute),\n}\n"
  },
  {
    "path": "router/src/static_routes.rs",
    "content": "use crate::{hooks::RawParamsMap, params::ParamsMap, PathSegment};\nuse futures::{channel::oneshot, stream, Stream, StreamExt};\nuse leptos::task::spawn;\nuse reactive_graph::{owner::Owner, traits::GetUntracked};\nuse std::{\n    fmt::{Debug, Display},\n    future::Future,\n    ops::Deref,\n    pin::Pin,\n    sync::Arc,\n};\n\ntype PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;\ntype PinnedStream<T> = Pin<Box<dyn Stream<Item = T> + Send>>;\n\n/// A reference-counted pointer to a function that can generate a set of params for static site\n/// generation.\npub type StaticParams = Arc<StaticParamsFn>;\n/// A function that generates a set of params for generating a static route.\npub type StaticParamsFn =\n    dyn Fn() -> PinnedFuture<StaticParamsMap> + Send + Sync + 'static;\n\n/// A function that defines when a statically-generated page should be regenerated.\n#[derive(Clone)]\n#[allow(clippy::type_complexity)]\npub struct RegenerationFn(\n    Arc<dyn Fn(&ParamsMap) -> PinnedStream<()> + Send + Sync>,\n);\n\nimpl Debug for RegenerationFn {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"RegenerationFn\").finish_non_exhaustive()\n    }\n}\n\nimpl Deref for RegenerationFn {\n    type Target = dyn Fn(&ParamsMap) -> PinnedStream<()> + Send + Sync;\n\n    fn deref(&self) -> &Self::Target {\n        &*self.0\n    }\n}\n\nimpl PartialEq for RegenerationFn {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.0, &other.0)\n    }\n}\n\n/// Defines how a static route should be generated.\n#[derive(Clone, Default)]\npub struct StaticRoute {\n    pub(crate) prerender_params: Option<StaticParams>,\n    pub(crate) regenerate: Option<RegenerationFn>,\n}\n\nimpl StaticRoute {\n    /// Creates a new static route listing.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Defines a set of params that should be prerendered on server start-up, depending on some\n    /// asynchronous function that returns their values.\n    pub fn prerender_params<Fut>(\n        mut self,\n        params: impl Fn() -> Fut + Send + Sync + 'static,\n    ) -> Self\n    where\n        Fut: Future<Output = StaticParamsMap> + Send + 'static,\n    {\n        self.prerender_params = Some(Arc::new(move || Box::pin(params())));\n        self\n    }\n\n    /// Defines when the route should be regenerated.\n    pub fn regenerate<St>(\n        mut self,\n        invalidate: impl Fn(&ParamsMap) -> St + Send + Sync + 'static,\n    ) -> Self\n    where\n        St: Stream<Item = ()> + Send + 'static,\n    {\n        self.regenerate = Some(RegenerationFn(Arc::new(move |params| {\n            Box::pin(invalidate(params))\n        })));\n        self\n    }\n\n    /// Returns a set of params that should be prerendered.\n    pub async fn to_prerendered_params(&self) -> Option<StaticParamsMap> {\n        match &self.prerender_params {\n            None => None,\n            Some(params) => Some(params().await),\n        }\n    }\n}\n\nimpl Debug for StaticRoute {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"StaticRoute\").finish_non_exhaustive()\n    }\n}\n\nimpl PartialOrd for StaticRoute {\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for StaticRoute {\n    fn cmp(&self, _other: &Self) -> std::cmp::Ordering {\n        std::cmp::Ordering::Equal\n    }\n}\n\nimpl PartialEq for StaticRoute {\n    fn eq(&self, other: &Self) -> bool {\n        let prerender = match (&self.prerender_params, &other.prerender_params)\n        {\n            (None, None) => true,\n            (None, Some(_)) | (Some(_), None) => false,\n            (Some(this), Some(that)) => Arc::ptr_eq(this, that),\n        };\n        prerender && (self.regenerate == other.regenerate)\n    }\n}\n\nimpl Eq for StaticRoute {}\n\n/// A map of params for static routes.\n#[derive(Debug, Clone, Default)]\npub struct StaticParamsMap(pub Vec<(String, Vec<String>)>);\n\nimpl StaticParamsMap {\n    /// Create a new empty `StaticParamsMap`.\n    #[inline]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Insert a value into the map.\n    #[inline]\n    pub fn insert(&mut self, key: impl ToString, value: Vec<String>) {\n        let key = key.to_string();\n        for item in self.0.iter_mut() {\n            if item.0 == key {\n                item.1 = value;\n                return;\n            }\n        }\n        self.0.push((key, value));\n    }\n\n    /// Get a value from the map.\n    #[inline]\n    pub fn get(&self, key: &str) -> Option<&Vec<String>> {\n        self.0\n            .iter()\n            .find_map(|entry| (entry.0 == key).then_some(&entry.1))\n    }\n}\n\nimpl IntoIterator for StaticParamsMap {\n    type Item = (String, Vec<String>);\n    type IntoIter = StaticParamsIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        StaticParamsIter(self.0.into_iter())\n    }\n}\n\n/// An iterator over a set of (key, value) pairs for statically-routed params.\n#[derive(Debug)]\npub struct StaticParamsIter(\n    <Vec<(String, Vec<String>)> as IntoIterator>::IntoIter,\n);\n\nimpl Iterator for StaticParamsIter {\n    type Item = (String, Vec<String>);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next()\n    }\n}\n\nimpl<A> FromIterator<A> for StaticParamsMap\nwhere\n    A: Into<(String, Vec<String>)>,\n{\n    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {\n        Self(iter.into_iter().map(Into::into).collect())\n    }\n}\n\n#[doc(hidden)]\n#[derive(Debug)]\npub struct StaticPath {\n    segments: Vec<PathSegment>,\n}\n\nimpl StaticPath {\n    pub fn new(segments: Vec<PathSegment>) -> StaticPath {\n        Self { segments }\n    }\n\n    pub fn into_paths(\n        self,\n        params: Option<StaticParamsMap>,\n    ) -> Vec<ResolvedStaticPath> {\n        use PathSegment::*;\n        let mut paths = vec![ResolvedStaticPath {\n            path: String::new(),\n        }];\n\n        for segment in &self.segments {\n            match segment {\n                Unit => {}\n                Static(s) => {\n                    paths = paths\n                        .into_iter()\n                        .map(|p| {\n                            if s.starts_with(\"/\") || s.is_empty() {\n                                ResolvedStaticPath {\n                                    path: format!(\"{}{s}\", p.path),\n                                }\n                            } else {\n                                ResolvedStaticPath {\n                                    path: format!(\"{}/{s}\", p.path),\n                                }\n                            }\n                        })\n                        .collect::<Vec<_>>();\n                }\n                Param(name) | Splat(name) => {\n                    let mut new_paths = vec![];\n                    if let Some(params) = params.as_ref() {\n                        for path in paths {\n                            if let Some(params) = params.get(name) {\n                                for val in params.iter() {\n                                    new_paths.push(if val.starts_with(\"/\") {\n                                        ResolvedStaticPath {\n                                            path: format!(\n                                                \"{}{}\",\n                                                path.path, val\n                                            ),\n                                        }\n                                    } else {\n                                        ResolvedStaticPath {\n                                            path: format!(\n                                                \"{}/{}\",\n                                                path.path, val\n                                            ),\n                                        }\n                                    });\n                                }\n                            }\n                        }\n                    }\n                    paths = new_paths;\n                }\n                OptionalParam(_) => todo!(),\n            }\n        }\n        paths\n    }\n}\n\n/// A path to be used in static route generation.\n#[derive(Debug, Clone, PartialEq)]\npub struct ResolvedStaticPath {\n    pub(crate) path: String,\n}\n\nimpl ResolvedStaticPath {\n    /// Defines a path to be used for static route generation.\n    pub fn new(path: impl Into<String>) -> Self {\n        Self { path: path.into() }\n    }\n}\n\nimpl AsRef<str> for ResolvedStaticPath {\n    fn as_ref(&self) -> &str {\n        self.path.as_ref()\n    }\n}\n\nimpl Display for ResolvedStaticPath {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        Display::fmt(&self.path, f)\n    }\n}\n\nimpl ResolvedStaticPath {\n    /// Builds the page that corresponds to this path.\n    pub async fn build<Fut, WriterFut>(\n        self,\n        render_fn: impl Fn(&ResolvedStaticPath) -> Fut + Send + Clone + 'static,\n        writer: impl Fn(&ResolvedStaticPath, &Owner, String) -> WriterFut\n            + Send\n            + Clone\n            + 'static,\n        was_404: impl Fn(&Owner) -> bool + Send + Clone + 'static,\n        regenerate: Vec<RegenerationFn>,\n    ) -> (Owner, Option<String>)\n    where\n        Fut: Future<Output = (Owner, String)> + Send + 'static,\n        WriterFut: Future<Output = Result<(), std::io::Error>> + Send + 'static,\n    {\n        let (tx, rx) = oneshot::channel();\n\n        // spawns a separate task for each path it's rendering\n        // this allows us to parallelize all static site rendering,\n        // and also to create long-lived tasks\n        spawn({\n            let render_fn = render_fn.clone();\n            let writer = writer.clone();\n            let was_error = was_404.clone();\n            async move {\n                // render and write the initial page\n                let (owner, html) = render_fn(&self).await;\n\n                // if rendering this page resulted in an error (404, 500, etc.)\n                // then we should not cache it: the `was_error` function can handle notifying\n                // the user that there was an error, and the server can give a dynamic response\n                // that will include the 404 or 500\n                if was_error(&owner) {\n                    // can ignore errors from channel here, because it just means we're not\n                    // awaiting the Future\n                    _ = tx.send((owner.clone(), Some(html)));\n                } else {\n                    if let Err(e) = writer(&self, &owner, html).await {\n                        #[cfg(feature = \"tracing\")]\n                        tracing::warn!(\"{e}\");\n\n                        #[cfg(not(feature = \"tracing\"))]\n                        eprintln!(\"{e}\");\n                    }\n                    _ = tx.send((owner.clone(), None));\n                }\n\n                // if there's a regeneration function, keep looping\n                let params = if regenerate.is_empty() {\n                    None\n                } else {\n                    Some(\n                        owner\n                            .use_context_bidirectional::<RawParamsMap>()\n                            .expect(\n                                \"using static routing, but couldn't find \\\n                                 ParamsMap\",\n                            )\n                            .get_untracked(),\n                    )\n                };\n                let mut regenerate = stream::select_all(\n                    regenerate\n                        .into_iter()\n                        .map(|r| owner.with(|| r(params.as_ref().unwrap()))),\n                );\n                while regenerate.next().await.is_some() {\n                    let (owner, html) = render_fn(&self).await;\n                    if !was_error(&owner) {\n                        if let Err(e) = writer(&self, &owner, html).await {\n                            #[cfg(feature = \"tracing\")]\n                            tracing::warn!(\"{e}\");\n\n                            #[cfg(not(feature = \"tracing\"))]\n                            eprintln!(\"{e}\");\n                        }\n                    }\n                    owner.unset_with_forced_cleanup();\n                }\n            }\n        });\n\n        rx.await.unwrap()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn static_path_segments_into_path_ignore_empty_segments() {\n        let segments = StaticPath::new(vec![\n            PathSegment::Static(\"\".into()),\n            PathSegment::Static(\"post\".into()),\n        ]);\n        assert_eq!(\n            segments.into_paths(None),\n            vec![ResolvedStaticPath::new(\"/post\")]\n        );\n    }\n\n    #[test]\n    fn static_path_segments_into_path_flatten_param() {\n        let mut params = StaticParamsMap::new();\n        params\n            .0\n            .push((\"slug\".into(), vec![\"first\".into(), \"second\".into()]));\n        let segments = StaticPath::new(vec![\n            PathSegment::Static(\"/post\".into()),\n            PathSegment::Param(\"slug\".into()),\n        ]);\n        assert_eq!(\n            segments.into_paths(Some(params)),\n            vec![\n                ResolvedStaticPath::new(\"/post/first\"),\n                ResolvedStaticPath::new(\"/post/second\")\n            ]\n        );\n    }\n\n    #[test]\n    fn static_path_segments_into_path_no_double_slash() {\n        let segments = StaticPath::new(vec![\n            PathSegment::Static(\"/post\".into()),\n            PathSegment::Static(\"/leptos\".into()),\n        ]);\n        assert_eq!(\n            segments.into_paths(None),\n            vec![ResolvedStaticPath::new(\"/post/leptos\")]\n        );\n\n        let mut params = StaticParamsMap::new();\n        params\n            .0\n            .push((\"slug\".into(), vec![\"/first\".into(), \"/second\".into()]));\n        let segments = StaticPath::new(vec![\n            PathSegment::Static(\"/post\".into()),\n            PathSegment::Param(\"slug\".into()),\n        ]);\n        assert_eq!(\n            segments.into_paths(Some(params)),\n            vec![\n                ResolvedStaticPath::new(\"/post/first\"),\n                ResolvedStaticPath::new(\"/post/second\")\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "router_macro/Cargo.toml",
    "content": "[package]\nname = \"leptos_router_macro\"\nversion = \"0.8.6\"\nauthors = [\"Greg Johnston\", \"Ben Wishovich\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Router utility macros for the Leptos web framework.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[lib]\nproc-macro = true\n\n[dependencies]\nproc-macro-error2 = { default-features = false, workspace = true }\nproc-macro2 = { workspace = true, default-features = true }\nquote = { workspace = true, default-features = true }\nsyn = { features = [\"full\"], workspace = true, default-features = true }\n\n[dev-dependencies]\nleptos = { path = \"../leptos\" }\nleptos_router = { path = \"../router\" }\nleptos_macro = { path = \"../leptos_macro\" }\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(leptos_debuginfo)'] }\n"
  },
  {
    "path": "router_macro/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "router_macro/src/lib.rs",
    "content": "//! A macro to make path definitions easier with [`leptos_router`].\n//!\n//! [`leptos_router`]: https://docs.rs/leptos_router/latest/leptos_router/components/fn.Route.html\n\n#![deny(missing_docs)]\n\nuse proc_macro::{TokenStream, TokenTree};\nuse proc_macro2::Span;\nuse proc_macro_error2::{abort, proc_macro_error, set_dummy};\nuse quote::{format_ident, quote, ToTokens};\nuse syn::{\n    spanned::Spanned, FnArg, Ident, ImplItem, ItemImpl, Path, Type, TypePath,\n};\n\nconst RFC3986_UNRESERVED: [char; 4] = ['-', '.', '_', '~'];\nconst RFC3986_PCHAR_OTHER: [char; 1] = ['@'];\n\n/// Constructs a path for use in a [`Route`] definition.\n///\n/// Note that this is an optional convenience. Manually defining route segments\n/// is equivalent.\n///\n/// # Examples\n///\n/// ```rust\n/// use leptos_router::{\n///     path, OptionalParamSegment, ParamSegment, StaticSegment,\n///     WildcardSegment,\n/// };\n///\n/// let path = path!(\"/foo/:bar/:baz?/*any\");\n/// let output = (\n///     StaticSegment(\"foo\"),\n///     ParamSegment(\"bar\"),\n///     OptionalParamSegment(\"baz\"),\n///     WildcardSegment(\"any\"),\n/// );\n///\n/// assert_eq!(path, output);\n/// ```\n/// [`Route`]: https://docs.rs/leptos_router/latest/leptos_router/components/fn.Route.html\n#[proc_macro_error2::proc_macro_error]\n#[proc_macro]\npub fn path(tokens: TokenStream) -> TokenStream {\n    let mut parser = SegmentParser::new(tokens);\n    parser.parse_all();\n    let segments = Segments(parser.segments);\n    segments.into_token_stream().into()\n}\n\n#[derive(Debug, PartialEq)]\nstruct Segments(pub Vec<Segment>);\n\n#[derive(Debug, PartialEq)]\nenum Segment {\n    Static(String),\n    Param(String),\n    OptionalParam(String),\n    Wildcard(String),\n}\n\nstruct SegmentParser {\n    input: proc_macro::token_stream::IntoIter,\n    segments: Vec<Segment>,\n}\n\nimpl SegmentParser {\n    pub fn new(input: TokenStream) -> Self {\n        Self {\n            input: input.into_iter(),\n            segments: Vec::new(),\n        }\n    }\n}\n\nimpl SegmentParser {\n    pub fn parse_all(&mut self) {\n        for input in self.input.by_ref() {\n            match input {\n                TokenTree::Literal(lit) => {\n                    let lit = lit.to_string();\n                    if lit.contains(\"//\") {\n                        abort!(\n                            proc_macro2::Span::call_site(),\n                            \"Consecutive '/' is not allowed\"\n                        );\n                    }\n                    Self::parse_str(\n                        &mut self.segments,\n                        lit.trim_start_matches(['\"', '/'])\n                            .trim_end_matches(['\"', '/']),\n                    );\n                    if lit.ends_with(r#\"/\"\"#) && lit != r#\"\"/\"\"# {\n                        self.segments.push(Segment::Static(\"/\".to_string()));\n                    }\n                }\n                TokenTree::Group(_) => unimplemented!(),\n                TokenTree::Ident(_) => unimplemented!(),\n                TokenTree::Punct(_) => unimplemented!(),\n            }\n        }\n    }\n\n    pub fn parse_str(segments: &mut Vec<Segment>, current_str: &str) {\n        if [\"\", \"*\"].contains(&current_str) {\n            return;\n        }\n\n        for segment in current_str.split('/') {\n            if let Some(segment) = segment.strip_prefix(':') {\n                if let Some(segment) = segment.strip_suffix('?') {\n                    segments.push(Segment::OptionalParam(segment.to_string()));\n                } else {\n                    segments.push(Segment::Param(segment.to_string()));\n                }\n            } else if let Some(segment) = segment.strip_prefix('*') {\n                segments.push(Segment::Wildcard(segment.to_string()));\n            } else {\n                segments.push(Segment::Static(segment.to_string()));\n            }\n        }\n    }\n}\n\nimpl Segment {\n    fn is_valid(segment: &str) -> bool {\n        segment == \"/\"\n            || segment.chars().all(|c| {\n                c.is_ascii_digit()\n                    || c.is_ascii_lowercase()\n                    || c.is_ascii_uppercase()\n                    || RFC3986_UNRESERVED.contains(&c)\n                    || RFC3986_PCHAR_OTHER.contains(&c)\n            })\n    }\n\n    fn ensure_valid(&self) {\n        match self {\n            Self::Wildcard(s) if !Self::is_valid(s) => {\n                abort!(Span::call_site(), \"Invalid wildcard segment: {}\", s)\n            }\n            Self::Static(s) if !Self::is_valid(s) => {\n                abort!(Span::call_site(), \"Invalid static segment: {}\", s)\n            }\n            Self::Param(s) if !Self::is_valid(s) => {\n                abort!(Span::call_site(), \"Invalid param segment: {}\", s)\n            }\n            _ => (),\n        }\n    }\n}\n\nimpl Segments {\n    fn ensure_valid(&self) {\n        if let Some((_last, segments)) = self.0.split_last() {\n            if let Some(Segment::Wildcard(s)) =\n                segments.iter().find(|s| matches!(s, Segment::Wildcard(_)))\n            {\n                abort!(Span::call_site(), \"Wildcard must be at end: {}\", s)\n            }\n        }\n    }\n}\n\nimpl ToTokens for Segment {\n    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {\n        self.ensure_valid();\n        match self {\n            Segment::Wildcard(s) => {\n                tokens.extend(quote! { leptos_router::WildcardSegment(#s) });\n            }\n            Segment::Static(s) => {\n                tokens.extend(quote! { leptos_router::StaticSegment(#s) });\n            }\n            Segment::Param(p) => {\n                tokens.extend(quote! { leptos_router::ParamSegment(#p) });\n            }\n            Segment::OptionalParam(p) => {\n                tokens\n                    .extend(quote! { leptos_router::OptionalParamSegment(#p) });\n            }\n        }\n    }\n}\n\nimpl ToTokens for Segments {\n    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {\n        self.ensure_valid();\n        match self.0.as_slice() {\n            [] => tokens.extend(quote! { () }),\n            [segment] => tokens.extend(quote! { (#segment,) }),\n            segments => tokens.extend(quote! { (#(#segments),*) }),\n        }\n    }\n}\n\n/// When added to an [`impl LazyRoute`] implementation block, this will automatically\n/// add a [`lazy`] annotation to the `view` method, which will cause the code for the view\n/// to lazy-load concurrently with the `data` being loaded for the route.\n///\n/// ```rust\n/// use leptos::prelude::*;\n/// use leptos_router::{lazy_route, LazyRoute};\n///\n/// // the route definition\n/// #[derive(Debug)]\n/// struct BlogListingRoute {\n///     titles: Resource<Vec<String>>\n/// }\n///\n/// #[lazy_route]\n/// impl LazyRoute for BlogListingRoute {\n///     fn data() -> Self {\n///         Self {\n///             titles: Resource::new(|| (), |_| async {\n///                 vec![/* todo: load blog posts */]\n///             })\n///         }\n///     }\n///\n///     // this function will be lazy-loaded, concurrently with data()\n///     fn view(this: Self) -> AnyView {\n///         let BlogListingRoute { titles } = this;\n///\n///         // ... now you can use the `posts` resource with Suspense, etc.,\n///         // and return AnyView by calling .into_any() on a view\n///         # ().into_any()\n///     }\n/// }\n/// ```\n///\n/// [`impl LazyRoute`]: https://docs.rs/leptos_router/latest/leptos_router/trait.LazyRoute.html\n/// [`lazy`]: https://docs.rs/leptos_macro/latest/leptos_macro/macro.lazy.html\n#[proc_macro_attribute]\n#[proc_macro_error]\npub fn lazy_route(\n    args: proc_macro::TokenStream,\n    s: TokenStream,\n) -> TokenStream {\n    lazy_route_impl(args, s)\n}\n\nfn lazy_route_impl(\n    _args: proc_macro::TokenStream,\n    s: TokenStream,\n) -> TokenStream {\n    set_dummy(s.clone().into());\n\n    let mut im = syn::parse::<ItemImpl>(s.clone()).unwrap_or_else(|e| {\n        abort!(e.span(), \"`lazy_route` can only be used on an `impl` block\")\n    });\n    if im.trait_.is_none() {\n        abort!(\n            im.span(),\n            \"`lazy_route` can only be used on an `impl LazyRoute for ...` \\\n             block\"\n        )\n    }\n\n    let self_ty = im.self_ty.clone();\n    let ty_name_to_snake = match &*self_ty {\n        Type::Path(TypePath {\n            path: Path { segments, .. },\n            ..\n        }) => segments.last().unwrap().ident.to_string(),\n        _ => abort!(self_ty.span(), \"only path types are supported\"),\n    };\n    let lazy_view_ident =\n        Ident::new(&format!(\"__{ty_name_to_snake}_View\"), im.self_ty.span());\n    let preload_ident = format_ident!(\"__preload_{lazy_view_ident}\");\n\n    im.items.push(\n        syn::parse::<ImplItem>(\n            quote! {\n                async fn preload() {\n                    // TODO for 0.9 this is not precise\n                    // we don't split routes for wasm32 ssr\n                    // but we don't require a `hydrate`/`csr` feature on leptos_router\n                    #[cfg(target_arch = \"wasm32\")]\n                    #preload_ident().await;\n                }\n            }\n            .into(),\n        )\n        .unwrap_or_else(|e| {\n            abort!(e.span(), \"could not parse preload item impl\")\n        }),\n    );\n\n    let item = im.items.iter_mut().find_map(|item| match item {\n        ImplItem::Fn(inner) => {\n            if inner.sig.ident.to_string().as_str() == \"view\" {\n                Some(inner)\n            } else {\n                None\n            }\n        }\n        _ => None,\n    });\n\n    match item {\n        None => s,\n        Some(fun) => {\n            if let Some(a) = fun.sig.asyncness {\n                abort!(a.span(), \"`view` method should not be async\")\n            }\n            fun.sig.asyncness = Some(Default::default());\n\n            let first_arg = fun.sig.inputs.first().unwrap_or_else(|| {\n                abort!(fun.sig.span(), \"must have an argument\")\n            });\n            let FnArg::Typed(first_arg) = first_arg else {\n                abort!(\n                    first_arg.span(),\n                    \"this must be a typed argument like `this: Self`\"\n                )\n            };\n            let first_arg_pat = &*first_arg.pat;\n            let body = std::mem::replace(\n                &mut fun.block,\n                syn::parse(\n                    quote! {\n                        {\n                            #lazy_view_ident(#first_arg_pat).await\n                        }\n                    }\n                    .into(),\n                )\n                .unwrap(),\n            );\n\n            quote! {\n                #[allow(non_snake_case)]\n                #[::leptos::lazy]\n                fn #lazy_view_ident(#first_arg_pat: #self_ty) -> ::leptos::prelude::AnyView {\n                    #body\n                }\n\n                #im\n            }.into()\n        }\n    }\n}\n"
  },
  {
    "path": "router_macro/tests/path.rs",
    "content": "use leptos_router::{\n    OptionalParamSegment, ParamSegment, StaticSegment, WildcardSegment,\n};\nuse leptos_router_macro::path;\n\n#[test]\nfn parses_empty_string() {\n    let output = path!(\"\");\n    assert!(output.eq(&()));\n}\n\n#[test]\nfn parses_single_slash() {\n    let output = path!(\"/\");\n    assert!(output.eq(&()));\n}\n\n#[test]\nfn parses_single_asterisk() {\n    let output = path!(\"*\");\n    assert!(output.eq(&()));\n}\n\n#[test]\nfn parses_slash_asterisk() {\n    let output = path!(\"/*\");\n    assert!(output.eq(&()));\n}\n\n#[test]\nfn parses_asterisk_any() {\n    let output = path!(\"/foo/:bar/*any\");\n    assert_eq!(\n        output,\n        (\n            StaticSegment(\"foo\"),\n            ParamSegment(\"bar\"),\n            WildcardSegment(\"any\")\n        )\n    );\n}\n\n#[test]\nfn parses_hyphen() {\n    let output = path!(\"/foo/bar-baz\");\n    assert_eq!(output, (StaticSegment(\"foo\"), StaticSegment(\"bar-baz\")));\n}\n\n#[test]\nfn parses_rfc3976_unreserved() {\n    let output = path!(\"/-._~\");\n    assert_eq!(output, (StaticSegment(\"-._~\"),));\n}\n\n#[test]\nfn parses_rfc3976_pchar_other() {\n    let output = path!(\"/@\");\n    assert_eq!(output, (StaticSegment(\"@\"),));\n}\n\n#[test]\nfn parses_no_slashes() {\n    let output = path!(\"home\");\n    assert_eq!(output, (StaticSegment(\"home\"),));\n}\n\n#[test]\nfn parses_no_leading_slash() {\n    let output = path!(\"home\");\n    assert_eq!(output, (StaticSegment(\"home\"),));\n}\n\n#[test]\nfn parses_trailing_slash() {\n    let output = path!(\"/home/\");\n    assert_eq!(output, (StaticSegment(\"home\"), StaticSegment(\"/\")));\n}\n\n#[test]\nfn parses_single_static() {\n    let output = path!(\"/home\");\n    assert_eq!(output, (StaticSegment(\"home\"),));\n}\n\n#[test]\nfn parses_single_param() {\n    let output = path!(\"/:id\");\n    assert_eq!(output, (ParamSegment(\"id\"),));\n}\n\n#[test]\nfn parses_optional_param() {\n    let output = path!(\"/:id?\");\n    assert_eq!(output, (OptionalParamSegment(\"id\"),));\n}\n\n#[test]\nfn parses_static_and_param() {\n    let output = path!(\"/home/:id\");\n    assert_eq!(output, (StaticSegment(\"home\"), ParamSegment(\"id\"),));\n}\n\n#[test]\nfn parses_mixed_segment_types() {\n    let output = path!(\"/foo/:bar/*baz\");\n    assert_eq!(\n        output,\n        (\n            StaticSegment(\"foo\"),\n            ParamSegment(\"bar\"),\n            WildcardSegment(\"baz\")\n        )\n    );\n}\n\n#[test]\nfn parses_trailing_slash_after_param() {\n    let output = path!(\"/foo/:bar/\");\n    assert_eq!(\n        output,\n        (\n            StaticSegment(\"foo\"),\n            ParamSegment(\"bar\"),\n            StaticSegment(\"/\")\n        )\n    );\n}\n\n#[test]\nfn parses_consecutive_static() {\n    let output = path!(\"/foo/bar/baz\");\n    assert_eq!(\n        output,\n        (\n            StaticSegment(\"foo\"),\n            StaticSegment(\"bar\"),\n            StaticSegment(\"baz\")\n        )\n    );\n}\n\n#[test]\nfn parses_consecutive_param() {\n    let output = path!(\"/:foo/:bar/:baz\");\n    assert_eq!(\n        output,\n        (\n            ParamSegment(\"foo\"),\n            ParamSegment(\"bar\"),\n            ParamSegment(\"baz\")\n        )\n    );\n}\n\n#[test]\nfn parses_consecutive_optional_param() {\n    let output = path!(\"/:foo?/:bar?/:baz?\");\n    assert_eq!(\n        output,\n        (\n            OptionalParamSegment(\"foo\"),\n            OptionalParamSegment(\"bar\"),\n            OptionalParamSegment(\"baz\")\n        )\n    );\n}\n\n#[test]\nfn parses_complex() {\n    let output = path!(\"/home/:id/foo/:bar/:baz?/*any\");\n    assert_eq!(\n        output,\n        (\n            StaticSegment(\"home\"),\n            ParamSegment(\"id\"),\n            StaticSegment(\"foo\"),\n            ParamSegment(\"bar\"),\n            OptionalParamSegment(\"baz\"),\n            WildcardSegment(\"any\"),\n        )\n    );\n}\n\n// #[test]\n// fn deny_consecutive_slashes() {\n//     let _ = path!(\"/////foo///bar/////baz/\");\n// }\n//\n// #[test]\n// fn deny_invalid_segment() {\n//     let _ = path!(\"/foo/^/\");\n// }\n//\n// #[test]\n// fn deny_non_trailing_wildcard_segment() {\n//     let _ = path!(\"/home/*any/end\");\n// }\n//\n// #[test]\n// fn deny_invalid_wildcard() {\n//     let _ = path!(\"/home/any*\");\n// }\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "# Stable options\nedition = \"2021\"\nmax_width = 80\n\n# Unstable options\nimports_granularity = \"Crate\"\nformat_strings = true\ngroup_imports = \"One\"\nformat_code_in_doc_comments = true\n"
  },
  {
    "path": "scripts/axum_integration_service_mode.bat",
    "content": "SET LEPTOS_OUTPUT_NAME=\"service_mode\"\ncargo leptos --manifest-path integrations\\axum\\tests\\service_mode\\Cargo.toml build\n"
  },
  {
    "path": "scripts/axum_integration_service_mode.sh",
    "content": "#!/bin/sh\nexport LEPTOS_OUTPUT_NAME=\"service_mode\"\ncargo leptos --manifest-path integrations/axum/tests/service_mode/Cargo.toml build\n"
  },
  {
    "path": "scripts/bump.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nLAST_TAG=$(git describe --tags --abbrev=0 --match \"v*\")\n\n# Get package name and manifest_path for all members\nPACKAGES=$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[] | \"\\(.name):::\\(.manifest_path)\"')\n\nfor PKG in $PACKAGES; do\n    NAME=\"${PKG%%:::*}\"\n    MANIFEST_PATH=\"${PKG##*:::}\"\n    DIR=$(dirname \"$MANIFEST_PATH\")\n\n    # Look for release commit for this member up to the last tag\n    RELEASE_COMMIT=$(git log --oneline --grep=\"^$NAME-v\" --format=\"%H\" \"$LAST_TAG\"..HEAD | head -n1)\n\n    if [[ -z \"$RELEASE_COMMIT\" ]]; then\n        # No release commit found, use the latest release tag commit\n        RELEASE_COMMIT=$(git rev-list -n 1 \"$LAST_TAG\")\n    fi\n\n    # Check if any file in the package directory changed since the member's release commit or latest tag release\n    if git diff --quiet \"$RELEASE_COMMIT\"..HEAD -- \"$DIR\"; then\n        continue\n    fi\n\n    echo \"Changes detected in $NAME ($DIR)\"\n    PS3=\"Select version bump for $NAME: \"\n    select BUMP in patch minor major; do\n        if [[ \"$BUMP\" == \"patch\" || \"$BUMP\" == \"minor\" || \"$BUMP\" == \"major\" ]]; then\n            break\n        else\n            echo \"Invalid option\"\n        fi\n    done\n\n    if cargo set-version --help >/dev/null 2>&1; then\n        cargo set-version --bump \"$BUMP\" --package \"$NAME\"\n    else\n        echo \"Please install cargo-edit first.\"\n        exit 1\n    fi\n\n    echo \"$NAME bumped to $BUMP\"\ndone\n"
  },
  {
    "path": "scripts/update_nightly.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nexit_error() {\n    echo \"ERROR: $1\" >&2\n    exit 1\n}\n\ncurrent_dir=$(pwd)\n\nif [[ \"$current_dir\" != */leptos ]]; then\n    exit_error \"Current directory does not end with leptos\"\nfi\n\n# Check if a date is provided as an argument\nif [ $# -eq 1 ]; then\n    NEW_DATE=$1\n    echo -n \"Will use the provided date: \"\nelse\n    # Use current date if no date is provided\n    NEW_DATE=$(date +\"%Y-%m-%d\")\n    echo -n \"Will use the current date: \"\nfi\n\necho \"$NEW_DATE\"\n\n# Detect if it is darwin sed\nif sed --version 2>/dev/null | grep -q GNU; then\n    SED_COMMAND=\"sed -i\"\nelse\n    SED_COMMAND='sed -i \"\"'\nfi\n\nMATCH=\"nightly-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\"\nREPLACE=\"nightly-$NEW_DATE\"\n\n# Find all occurrences of the pattern\ngit grep -l \"$MATCH\" | while read -r file; do\n    # Replace the pattern in each file\n    $SED_COMMAND \"s/$MATCH/$REPLACE/g\" \"$file\"\n    echo \"Updated $file\"\ndone\n\necho \"All occurrences of 'nightly-XXXX-XX-XX' have been replaced with 'nightly-$NEW_DATE'\"\n"
  },
  {
    "path": "server_fn/Cargo.toml",
    "content": "[package]\nname = \"server_fn\"\nauthors = [\"Greg Johnston\", \"Ben Wishovich\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"RPC for any web framework.\"\nreadme = \"../README.md\"\nversion = \"0.8.11\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nthrow_error = { workspace = true }\nserver_fn_macro_default = { workspace = true }\n# used for hashing paths in #[server] macro\nconst_format = { workspace = true, default-features = true }\nconst-str = { workspace = true, default-features = true }\nrustversion = { workspace = true, default-features = true }\nxxhash-rust = { features = [\n  \"const_xxh64\",\n], workspace = true, default-features = true }\n# used across multiple features\nserde = { features = [\"derive\"], workspace = true, default-features = true }\nsend_wrapper = { features = [\n  \"futures\",\n], optional = true, workspace = true, default-features = true }\nthiserror = { workspace = true, default-features = true }\nor_poisoned = { workspace = true, default-features = true }\n\n# registration system\ninventory = { optional = true, workspace = true, default-features = true }\n\n## servers\n# actix\nactix-web = { optional = true, workspace = true, default-features = false, features = [\n  \"ws\",\n] }\nactix-ws = { optional = true, workspace = true, default-features = true }\n\n# axum\naxum = { optional = true, default-features = false, features = [\n  \"multipart\",\n], workspace = true }\ntower = { optional = true, workspace = true, default-features = true }\ntower-layer = { optional = true, workspace = true, default-features = true }\n\n## input encodings\nserde_qs = { workspace = true, default-features = true }\nmulter = { optional = true, workspace = true, default-features = true }\n\n## output encodings\n# serde\nserde_json = { workspace = true, default-features = true }\nserde-lite = { features = [\n  \"derive\",\n], optional = true, workspace = true, default-features = true }\nfutures = { workspace = true, default-features = true }\nhttp = { workspace = true, default-features = true }\nciborium = { optional = true, workspace = true, default-features = true }\npostcard = { features = [\n  \"alloc\",\n], optional = true, workspace = true, default-features = true }\nhyper = { optional = true, workspace = true, default-features = true }\nbytes = { workspace = true, default-features = true }\nhttp-body-util = { optional = true, workspace = true, default-features = true }\nrkyv = { optional = true, workspace = true, default-features = true }\nrmp-serde = { optional = true, workspace = true, default-features = true }\nbase64 = { workspace = true, default-features = true }\nbitcode = { optional = true, workspace = true, default-features = true }\n\n# client\ngloo-net = { optional = true, workspace = true, default-features = true }\njs-sys = { optional = true, workspace = true, default-features = true }\nwasm-bindgen = { workspace = true, optional = true, default-features = true }\nwasm-bindgen-futures = { optional = true, workspace = true, default-features = true }\nwasm-streams = { optional = true, workspace = true, default-features = true }\nweb-sys = { optional = true, features = [\n  \"console\",\n  \"ReadableStream\",\n  \"ReadableStreamDefaultReader\",\n  \"AbortController\",\n  \"AbortSignal\",\n], workspace = true, default-features = true }\n\n# reqwest client\nreqwest = { default-features = false, optional = true, features = [\n  \"multipart\",\n  \"stream\",\n], workspace = true }\ntokio-tungstenite = { optional = true, workspace = true, default-features = true }\nurl = { workspace = true, default-features = true }\npin-project-lite = { workspace = true, default-features = true }\ntokio = { features = [\n  \"rt\",\n], optional = true, workspace = true, default-features = true }\n\n[build-dependencies]\nrustc_version = { workspace = true, default-features = true }\n\n[dev-dependencies]\ntrybuild = { workspace = true, default-features = true }\n\n[features]\naxum-no-default = [\n  \"ssr\",\n  \"generic\",\n  \"dep:axum\",\n  \"dep:hyper\",\n  \"dep:http-body-util\",\n  \"dep:tower\",\n  \"dep:tower-layer\",\n]\nform-redirects = []\nactix-no-default = [\"ssr\", \"dep:actix-web\", \"dep:actix-ws\", \"dep:send_wrapper\"]\nactix = [\"actix-web/default\", \"actix-no-default\"]\naxum = [\"axum/default\", \"axum-no-default\", \"axum/ws\", \"dep:tokio\"]\nbrowser = [\n  \"dep:gloo-net\",\n  \"dep:js-sys\",\n  \"dep:send_wrapper\",\n  \"dep:wasm-bindgen\",\n  \"dep:web-sys\",\n  \"dep:wasm-streams\",\n  \"dep:wasm-bindgen-futures\",\n]\nserde-lite = [\"dep:serde-lite\"]\nmultipart = [\"browser\", \"dep:multer\"]\ncbor = [\"dep:ciborium\"]\nrkyv = [\"dep:rkyv\"]\nmsgpack = [\"dep:rmp-serde\"]\npostcard = [\"dep:postcard\"]\nbitcode = [\"dep:bitcode\"]\nbitcode-serde = [\"dep:bitcode\", \"bitcode?/serde\"]\ndefault-tls = [\"reqwest?/default-tls\"]\nrustls = [\"reqwest?/rustls\", \"tokio-tungstenite?/rustls\"]\nreqwest = [\"dep:reqwest\", \"dep:tokio-tungstenite\", \"dep:tokio\"]\nssr = [\"inventory\"]\ngeneric = []\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n# disables some feature combos for testing in CI\n[package.metadata.cargo-all-features]\ndenylist = [\n  \"rustls\",\n  \"default-tls\",\n  \"form-redirects\",\n  \"gloo-net\",\n  \"js-sys\",\n  \"wasm-bindgen\",\n  \"web-sys\",\n  \"tower\",\n  \"tower-layer\",\n  \"send_wrapper\",\n  \"ciborium\",\n  \"hyper\",\n  \"inventory\",\n  \"rkyv\",\n]\nskip_feature_sets = [\n  [\n    \"actix\",\n    \"axum\",\n  ],\n  [\n    \"actix\",\n    \"generic\",\n  ],\n  [\n    \"browser\",\n    \"actix\",\n  ],\n  [\n    \"browser\",\n    \"axum\",\n  ],\n  [\n    \"browser\",\n    \"reqwest\",\n  ],\n  [\n    \"browser\",\n    \"generic\",\n  ],\n  [\n    \"default-tls\",\n    \"rustls\",\n  ],\n  [\n    \"browser\",\n    \"ssr\",\n  ],\n  [\n    \"axum-no-default\",\n    \"actix\",\n  ],\n  [\n    \"axum-no-default\",\n    \"browser\",\n  ],\n  [\n    \"axum-no-default\",\n    \"generic\",\n  ],\n  [\n    \"rkyv\",\n    \"json\",\n  ],\n  [\n    \"rkyv\",\n    \"cbor\",\n  ],\n  [\n    \"rkyv\",\n    \"url\",\n  ],\n  [\n    \"rkyv\",\n    \"serde-lite\",\n  ],\n  [\n    \"url\",\n    \"json\",\n  ],\n  [\n    \"url\",\n    \"cbor\",\n  ],\n  [\n    \"url\",\n    \"serde-lite\",\n  ],\n  [\n    \"postcard\",\n    \"json\",\n  ],\n  [\n    \"postcard\",\n    \"cbor\",\n  ],\n  [\n    \"postcard\",\n    \"url\",\n  ],\n  [\n    \"postcard\",\n    \"serde-lite\",\n  ],\n  [\n    \"postcard\",\n    \"rkyv\",\n  ],\n]\nmax_combination_size = 2\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = [\n  'cfg(leptos_debuginfo)',\n  'cfg(rustc_nightly)',\n] }\n"
  },
  {
    "path": "server_fn/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "server_fn/build.rs",
    "content": "use rustc_version::{version_meta, Channel};\n\nfn main() {\n    // Set cfg flags depending on release channel\n    if matches!(version_meta().unwrap().channel, Channel::Nightly) {\n        println!(\"cargo:rustc-cfg=rustc_nightly\");\n    }\n}\n"
  },
  {
    "path": "server_fn/server_fn_macro_default/Cargo.toml",
    "content": "[package]\nname = \"server_fn_macro_default\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"The default implementation of the server_fn macro without a context\"\nversion = \"0.8.5\"\nedition.workspace = true\n\n[lib]\nproc-macro = true\n\n[dependencies]\nsyn = { workspace = true, default-features = true }\nserver_fn_macro = { workspace = true }\n\n[features]\nnightly = [\"server_fn_macro/nightly\"]\nssr = [\"server_fn_macro/ssr\"]\nactix = [\"server_fn_macro/actix\"]\naxum = [\"server_fn_macro/axum\"]\n\n[package.metadata.cargo-all-features]\nmax_combination_size = 2\nskip_feature_sets = [[\"nightly\"]]\n"
  },
  {
    "path": "server_fn/server_fn_macro_default/Makefile.toml",
    "content": "extend = { path = \"../../cargo-make/main.toml\" }\n\n[tasks.check-format]\nenv = { LEPTOS_PROJECT_DIRECTORY = \"../../\" }\n"
  },
  {
    "path": "server_fn/server_fn_macro_default/src/lib.rs",
    "content": "#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\n//! This crate contains the default implementation of the #[macro@crate::server] macro without additional context from the server.\n//! See the [server_fn_macro] crate for more information.\n\nuse proc_macro::TokenStream;\nuse server_fn_macro::server_macro_impl;\nuse syn::__private::ToTokens;\n\n/// Declares that a function is a [server function](https://docs.rs/server_fn/).\n/// This means that its body will only run on the server, i.e., when the `ssr`\n/// feature is enabled on this crate.\n///\n/// ## Usage\n/// ```rust,ignore\n/// #[server]\n/// pub async fn blog_posts(\n///     category: String,\n/// ) -> Result<Vec<BlogPost>, ServerFnError> {\n///     let posts = load_posts(&category).await?;\n///     // maybe do some other work\n///     Ok(posts)\n/// }\n/// ```\n///\n/// ## Named Arguments\n///\n/// You can any combination of the following named arguments:\n/// - `name`: sets the identifier for the server function’s type, which is a struct created\n///   to hold the arguments (defaults to the function identifier in PascalCase)\n/// - `prefix`: a prefix at which the server function handler will be mounted (defaults to `/api`)\n/// - `endpoint`: specifies the exact path at which the server function handler will be mounted,\n///   relative to the prefix (defaults to the function name followed by unique hash)\n/// - `input`: the encoding for the arguments (defaults to `PostUrl`)\n/// - `input_derive`: a list of derives to be added on the generated input struct (defaults to `(Clone, serde::Serialize, serde::Deserialize)` if `input` is set to a custom struct, won't have an effect otherwise)\n/// - `output`: the encoding for the response (defaults to `Json`)\n/// - `client`: a custom `Client` implementation that will be used for this server fn\n/// - `encoding`: (legacy, may be deprecated in future) specifies the encoding, which may be one\n///   of the following (not case sensitive)\n///     - `\"Url\"`: `POST` request with URL-encoded arguments and JSON response\n///     - `\"GetUrl\"`: `GET` request with URL-encoded arguments and JSON response\n///     - `\"Cbor\"`: `POST` request with CBOR-encoded arguments and response\n///     - `\"GetCbor\"`: `GET` request with URL-encoded arguments and CBOR response\n/// - `req` and `res` specify the HTTP request and response types to be used on the server (these\n///   should usually only be necessary if you are integrating with a server other than Actix/Axum)\n/// ```rust,ignore\n/// #[server(\n///   name = SomeStructName,\n///   prefix = \"/my_api\",\n///   endpoint = \"my_fn\",\n///   input = Cbor,\n///   output = Json\n/// )]\n/// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<usize, ServerFnError> {\n///   todo!()\n/// }\n///\n/// // expands to\n/// #[derive(Deserialize, Serialize)]\n/// struct SomeStructName {\n///   input: Vec<String>\n/// }\n///\n/// impl ServerFn for SomeStructName {\n///   const PATH: &'static str = \"/my_api/my_fn\";\n///\n///   // etc.\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n    match server_macro_impl(\n        args.into(),\n        s.into(),\n        Some(syn::parse_quote!(server_fn)),\n        option_env!(\"SERVER_FN_PREFIX\").unwrap_or(\"/api\"),\n        None,\n        None,\n    ) {\n        Err(e) => e.to_compile_error().into(),\n        Ok(s) => s.to_token_stream().into(),\n    }\n}\n"
  },
  {
    "path": "server_fn/src/client.rs",
    "content": "use crate::{request::ClientReq, response::ClientRes};\nuse bytes::Bytes;\nuse futures::{Sink, Stream};\nuse std::{future::Future, sync::OnceLock};\n\nstatic ROOT_URL: OnceLock<&'static str> = OnceLock::new();\n\n/// Set the root server URL that all server function paths are relative to for the client.\n///\n/// If this is not set, it defaults to the origin.\npub fn set_server_url(url: &'static str) {\n    ROOT_URL.set(url).unwrap();\n}\n\n/// Returns the root server URL for all server functions.\npub fn get_server_url() -> &'static str {\n    ROOT_URL.get().copied().unwrap_or(\"\")\n}\n\n/// A client defines a pair of request/response types and the logic to send\n/// and receive them.\n///\n/// This trait is implemented for things like a browser `fetch` request or for\n/// the `reqwest` trait. It should almost never be necessary to implement it\n/// yourself, unless you’re trying to use an alternative HTTP crate on the client side.\npub trait Client<Error, InputStreamError = Error, OutputStreamError = Error> {\n    /// The type of a request sent by this client.\n    type Request: ClientReq<Error> + Send + 'static;\n    /// The type of a response received by this client.\n    type Response: ClientRes<Error> + Send + 'static;\n\n    /// Sends the request and receives a response.\n    fn send(\n        req: Self::Request,\n    ) -> impl Future<Output = Result<Self::Response, Error>> + Send;\n\n    /// Opens a websocket connection to the server.\n    #[allow(clippy::type_complexity)]\n    fn open_websocket(\n        path: &str,\n    ) -> impl Future<\n        Output = Result<\n            (\n                impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n                impl Sink<Bytes> + Send + 'static,\n            ),\n            Error,\n        >,\n    > + Send;\n\n    /// Spawn a future that runs in the background.\n    fn spawn(future: impl Future<Output = ()> + Send + 'static);\n}\n\n#[cfg(feature = \"browser\")]\n/// Implements [`Client`] for a `fetch` request in the browser.\npub mod browser {\n    use super::{get_server_url, Client};\n    use crate::{\n        error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n        request::browser::{BrowserRequest, RequestInner},\n        response::browser::BrowserResponse,\n    };\n    use bytes::Bytes;\n    use futures::{Sink, SinkExt, StreamExt};\n    use gloo_net::websocket::{Message, WebSocketError};\n    use send_wrapper::SendWrapper;\n    use std::future::Future;\n\n    /// Implements [`Client`] for a `fetch` request in the browser.\n    pub struct BrowserClient;\n\n    impl<\n            Error: FromServerFnError,\n            InputStreamError: FromServerFnError,\n            OutputStreamError: FromServerFnError,\n        > Client<Error, InputStreamError, OutputStreamError> for BrowserClient\n    {\n        type Request = BrowserRequest;\n        type Response = BrowserResponse;\n\n        fn send(\n            req: Self::Request,\n        ) -> impl Future<Output = Result<Self::Response, Error>> + Send\n        {\n            SendWrapper::new(async move {\n                let req = req.0.take();\n                let RequestInner {\n                    request,\n                    mut abort_ctrl,\n                } = req;\n                let res = request\n                    .send()\n                    .await\n                    .map(|res| BrowserResponse(SendWrapper::new(res)))\n                    .map_err(|e| {\n                        ServerFnErrorErr::Request(e.to_string())\n                            .into_app_error()\n                    });\n\n                // at this point, the future has successfully resolved without being dropped, so we\n                // can prevent the `AbortController` from firing\n                if let Some(ctrl) = abort_ctrl.as_mut() {\n                    ctrl.prevent_cancellation();\n                }\n                res\n            })\n        }\n\n        fn open_websocket(\n            url: &str,\n        ) -> impl Future<\n            Output = Result<\n                (\n                    impl futures::Stream<Item = Result<Bytes, Bytes>>\n                        + Send\n                        + 'static,\n                    impl futures::Sink<Bytes> + Send + 'static,\n                ),\n                Error,\n            >,\n        > + Send {\n            let mut websocket_server_url = get_server_url().to_string();\n            if let Some(postfix) = websocket_server_url.strip_prefix(\"http://\")\n            {\n                websocket_server_url = format!(\"ws://{postfix}\");\n            } else if let Some(postfix) =\n                websocket_server_url.strip_prefix(\"https://\")\n            {\n                websocket_server_url = format!(\"wss://{postfix}\");\n            }\n            let url = format!(\"{websocket_server_url}{url}\");\n            SendWrapper::new(async move {\n                let websocket =\n                    gloo_net::websocket::futures::WebSocket::open(&url)\n                        .map_err(|err| {\n                            web_sys::console::error_1(&err.to_string().into());\n                            Error::from_server_fn_error(\n                                ServerFnErrorErr::Request(err.to_string()),\n                            )\n                        })?;\n                let (sink, stream) = websocket.split();\n\n                let stream = stream.map(|message| match message {\n                    Ok(message) => Ok(match message {\n                        Message::Text(text) => Bytes::from(text),\n                        Message::Bytes(bytes) => Bytes::from(bytes),\n                    }),\n                    Err(err) => {\n                        web_sys::console::error_1(&err.to_string().into());\n                        Err(OutputStreamError::from_server_fn_error(\n                            ServerFnErrorErr::Request(err.to_string()),\n                        )\n                        .ser())\n                    }\n                });\n                let stream = SendWrapper::new(stream);\n\n                struct SendWrapperSink<S> {\n                    sink: SendWrapper<S>,\n                }\n\n                impl<S> SendWrapperSink<S> {\n                    fn new(sink: S) -> Self {\n                        Self {\n                            sink: SendWrapper::new(sink),\n                        }\n                    }\n                }\n\n                impl<S, Item> Sink<Item> for SendWrapperSink<S>\n                where\n                    S: Sink<Item> + Unpin,\n                {\n                    type Error = S::Error;\n\n                    fn poll_ready(\n                        self: std::pin::Pin<&mut Self>,\n                        cx: &mut std::task::Context<'_>,\n                    ) -> std::task::Poll<Result<(), Self::Error>>\n                    {\n                        self.get_mut().sink.poll_ready_unpin(cx)\n                    }\n\n                    fn start_send(\n                        self: std::pin::Pin<&mut Self>,\n                        item: Item,\n                    ) -> Result<(), Self::Error> {\n                        self.get_mut().sink.start_send_unpin(item)\n                    }\n\n                    fn poll_flush(\n                        self: std::pin::Pin<&mut Self>,\n                        cx: &mut std::task::Context<'_>,\n                    ) -> std::task::Poll<Result<(), Self::Error>>\n                    {\n                        self.get_mut().sink.poll_flush_unpin(cx)\n                    }\n\n                    fn poll_close(\n                        self: std::pin::Pin<&mut Self>,\n                        cx: &mut std::task::Context<'_>,\n                    ) -> std::task::Poll<Result<(), Self::Error>>\n                    {\n                        self.get_mut().sink.poll_close_unpin(cx)\n                    }\n                }\n\n                let sink = sink.with(|message: Bytes| async move {\n                    Ok::<Message, WebSocketError>(Message::Bytes(\n                        message.into(),\n                    ))\n                });\n                let sink = SendWrapperSink::new(Box::pin(sink));\n\n                Ok((stream, sink))\n            })\n        }\n\n        fn spawn(future: impl Future<Output = ()> + Send + 'static) {\n            wasm_bindgen_futures::spawn_local(future);\n        }\n    }\n}\n\n#[cfg(feature = \"reqwest\")]\n/// Implements [`Client`] for a request made by [`reqwest`].\npub mod reqwest {\n    use super::{get_server_url, Client};\n    use crate::{\n        error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n        request::reqwest::CLIENT,\n    };\n    use bytes::Bytes;\n    use futures::{SinkExt, StreamExt, TryFutureExt};\n    use reqwest::{Request, Response};\n    use std::future::Future;\n\n    /// Implements [`Client`] for a request made by [`reqwest`].\n    pub struct ReqwestClient;\n\n    impl<\n            Error: FromServerFnError,\n            InputStreamError: FromServerFnError,\n            OutputStreamError: FromServerFnError,\n        > Client<Error, InputStreamError, OutputStreamError> for ReqwestClient\n    {\n        type Request = Request;\n        type Response = Response;\n\n        fn send(\n            req: Self::Request,\n        ) -> impl Future<Output = Result<Self::Response, Error>> + Send\n        {\n            CLIENT.execute(req).map_err(|e| {\n                ServerFnErrorErr::Request(e.to_string()).into_app_error()\n            })\n        }\n\n        async fn open_websocket(\n            path: &str,\n        ) -> Result<\n            (\n                impl futures::Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n                impl futures::Sink<Bytes> + Send + 'static,\n            ),\n            Error,\n        > {\n            let mut websocket_server_url = get_server_url().to_string();\n            if let Some(postfix) = websocket_server_url.strip_prefix(\"http://\")\n            {\n                websocket_server_url = format!(\"ws://{postfix}\");\n            } else if let Some(postfix) =\n                websocket_server_url.strip_prefix(\"https://\")\n            {\n                websocket_server_url = format!(\"wss://{postfix}\");\n            }\n            let url = format!(\"{websocket_server_url}{path}\");\n            let (ws_stream, _) =\n                tokio_tungstenite::connect_async(url).await.map_err(|e| {\n                    Error::from_server_fn_error(ServerFnErrorErr::Request(\n                        e.to_string(),\n                    ))\n                })?;\n\n            let (write, read) = ws_stream.split();\n\n            Ok((\n                read.map(|msg| match msg {\n                    Ok(msg) => Ok(msg.into_data()),\n                    Err(e) => Err(OutputStreamError::from_server_fn_error(\n                        ServerFnErrorErr::Request(e.to_string()),\n                    )\n                    .ser()),\n                }),\n                write.with(|msg: Bytes| async move {\n                    Ok::<\n                        tokio_tungstenite::tungstenite::Message,\n                        tokio_tungstenite::tungstenite::Error,\n                    >(\n                        tokio_tungstenite::tungstenite::Message::Binary(msg)\n                    )\n                }),\n            ))\n        }\n\n        fn spawn(future: impl Future<Output = ()> + Send + 'static) {\n            tokio::spawn(future);\n        }\n    }\n}\n"
  },
  {
    "path": "server_fn/src/codec/bitcode.rs",
    "content": "use super::{Patch, Post, Put};\nuse crate::{ContentType, Decodes, Encodes, Format, FormatType};\nuse bytes::Bytes;\n\n/// Serializes and deserializes with [`bitcode`].\npub struct BitcodeEncoding;\n\nimpl ContentType for BitcodeEncoding {\n    const CONTENT_TYPE: &'static str = \"application/bitcode\";\n}\n\nimpl FormatType for BitcodeEncoding {\n    const FORMAT_TYPE: Format = Format::Binary;\n}\n\nimpl<T> Encodes<T> for BitcodeEncoding\nwhere\n    T: bitcode::Encode,\n{\n    type Error = std::convert::Infallible;\n\n    fn encode(value: &T) -> Result<Bytes, Self::Error> {\n        Ok(Bytes::from(bitcode::encode(value)))\n    }\n}\n\nimpl<T> Decodes<T> for BitcodeEncoding\nwhere\n    T: bitcode::DecodeOwned,\n{\n    type Error = bitcode::Error;\n\n    fn decode(bytes: Bytes) -> Result<T, Self::Error> {\n        bitcode::decode(bytes.as_ref())\n    }\n}\n\n/// Pass arguments and receive responses using `bitcode` in a `POST` request.\npub type Bitcode = Post<BitcodeEncoding>;\n\n/// Pass arguments and receive responses using `bitcode` in the body of a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PatchBitcode = Patch<BitcodeEncoding>;\n\n/// Pass arguments and receive responses using `bitcode` in the body of a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PutBitcode = Put<BitcodeEncoding>;\n"
  },
  {
    "path": "server_fn/src/codec/bitcode_serde.rs",
    "content": "use crate::{\n    codec::{Patch, Post, Put},\n    ContentType, Decodes, Encodes, Format, FormatType,\n};\nuse bytes::Bytes;\nuse serde::{de::DeserializeOwned, Serialize};\n\n/// Serializes and deserializes with [`bitcode`]'s `serde` integration.\npub struct BitcodeSerdeEncoding;\n\nimpl ContentType for BitcodeSerdeEncoding {\n    const CONTENT_TYPE: &'static str = \"application/x-bitcode-serde\";\n}\n\nimpl FormatType for BitcodeSerdeEncoding {\n    const FORMAT_TYPE: Format = Format::Binary;\n}\n\nimpl<T> Encodes<T> for BitcodeSerdeEncoding\nwhere\n    T: Serialize,\n{\n    type Error = bitcode::Error;\n\n    fn encode(value: &T) -> Result<Bytes, Self::Error> {\n        bitcode::serialize(value).map(Bytes::from)\n    }\n}\n\nimpl<T> Decodes<T> for BitcodeSerdeEncoding\nwhere\n    T: DeserializeOwned,\n{\n    type Error = bitcode::Error;\n\n    fn decode(bytes: Bytes) -> Result<T, Self::Error> {\n        bitcode::deserialize(bytes.as_ref())\n    }\n}\n\n/// Pass arguments and receive responses using `bitcode`'s serde integration in a `POST` request.\npub type BitcodeSerde = Post<BitcodeSerdeEncoding>;\n\n/// Pass arguments and receive responses using `bitcode`'s serde integration in the body of a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PatchBitcodeSerde = Patch<BitcodeSerdeEncoding>;\n\n/// Pass arguments and receive responses using `bitcode`'s serde integration in the body of a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PutBitcodeSerde = Put<BitcodeSerdeEncoding>;\n"
  },
  {
    "path": "server_fn/src/codec/cbor.rs",
    "content": "use super::{Patch, Post, Put};\nuse crate::{ContentType, Decodes, Encodes, Format, FormatType};\nuse bytes::Bytes;\nuse serde::{de::DeserializeOwned, Serialize};\n\n/// Serializes and deserializes CBOR with [`ciborium`].\npub struct CborEncoding;\n\nimpl ContentType for CborEncoding {\n    const CONTENT_TYPE: &'static str = \"application/cbor\";\n}\n\nimpl FormatType for CborEncoding {\n    const FORMAT_TYPE: Format = Format::Binary;\n}\n\nimpl<T> Encodes<T> for CborEncoding\nwhere\n    T: Serialize,\n{\n    type Error = ciborium::ser::Error<std::io::Error>;\n\n    fn encode(value: &T) -> Result<Bytes, Self::Error> {\n        let mut buffer: Vec<u8> = Vec::new();\n        ciborium::ser::into_writer(value, &mut buffer)?;\n        Ok(Bytes::from(buffer))\n    }\n}\n\nimpl<T> Decodes<T> for CborEncoding\nwhere\n    T: DeserializeOwned,\n{\n    type Error = ciborium::de::Error<std::io::Error>;\n\n    fn decode(bytes: Bytes) -> Result<T, Self::Error> {\n        ciborium::de::from_reader(bytes.as_ref())\n    }\n}\n\n/// Pass arguments and receive responses using `cbor` in a `POST` request.\npub type Cbor = Post<CborEncoding>;\n\n/// Pass arguments and receive responses using `cbor` in the body of a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PatchCbor = Patch<CborEncoding>;\n\n/// Pass arguments and receive responses using `cbor` in the body of a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PutCbor = Put<CborEncoding>;\n"
  },
  {
    "path": "server_fn/src/codec/json.rs",
    "content": "use super::{Patch, Post, Put};\nuse crate::{ContentType, Decodes, Encodes, Format, FormatType};\nuse bytes::Bytes;\nuse serde::{de::DeserializeOwned, Serialize};\n\n/// Serializes and deserializes JSON with [`serde_json`].\npub struct JsonEncoding;\n\nimpl ContentType for JsonEncoding {\n    const CONTENT_TYPE: &'static str = \"application/json\";\n}\n\nimpl FormatType for JsonEncoding {\n    const FORMAT_TYPE: Format = Format::Text;\n}\n\nimpl<T> Encodes<T> for JsonEncoding\nwhere\n    T: Serialize,\n{\n    type Error = serde_json::Error;\n\n    fn encode(output: &T) -> Result<Bytes, Self::Error> {\n        serde_json::to_vec(output).map(Bytes::from)\n    }\n}\n\nimpl<T> Decodes<T> for JsonEncoding\nwhere\n    T: DeserializeOwned,\n{\n    type Error = serde_json::Error;\n\n    fn decode(bytes: Bytes) -> Result<T, Self::Error> {\n        serde_json::from_slice(&bytes)\n    }\n}\n\n/// Pass arguments and receive responses as JSON in the body of a `POST` request.\npub type Json = Post<JsonEncoding>;\n\n/// Pass arguments and receive responses as JSON in the body of a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PatchJson = Patch<JsonEncoding>;\n\n/// Pass arguments and receive responses as JSON in the body of a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PutJson = Put<JsonEncoding>;\n"
  },
  {
    "path": "server_fn/src/codec/mod.rs",
    "content": "//! The serialization/deserialization process for server functions consists of a series of steps,\n//! each of which is represented by a different trait:\n//! 1. [`IntoReq`]: The client serializes the [`ServerFn`] argument type into an HTTP request.\n//! 2. The [`Client`] sends the request to the server.\n//! 3. [`FromReq`]: The server deserializes the HTTP request back into the [`ServerFn`] type.\n//! 4. The server calls [`ServerFn::run_body`] on the data.\n//! 5. [`IntoRes`]: The server serializes the [`ServerFn::Output`] type into an HTTP response.\n//! 6. The server integration applies any middleware from [`ServerFn::middlewares`] and responds to the request.\n//! 7. [`FromRes`]: The client deserializes the response back into the [`ServerFn::Output`] type.\n//!\n//! Rather than a limited number of encodings, this crate allows you to define server functions that\n//! mix and match the input encoding and output encoding. To define a new encoding, you simply implement\n//! an input combination ([`IntoReq`] and [`FromReq`]) and/or an output encoding ([`IntoRes`] and [`FromRes`]).\n//! This genuinely is an and/or: while some encodings can be used for both input and output (`Json`, `Cbor`, `Rkyv`),\n//! others can only be used for input (`GetUrl`, `MultipartData`).\n\n#[cfg(feature = \"cbor\")]\nmod cbor;\n#[cfg(feature = \"cbor\")]\npub use cbor::*;\n\nmod json;\npub use json::*;\n\n#[cfg(feature = \"serde-lite\")]\nmod serde_lite;\n#[cfg(feature = \"serde-lite\")]\npub use serde_lite::*;\n\n#[cfg(feature = \"rkyv\")]\nmod rkyv;\n#[cfg(feature = \"rkyv\")]\npub use rkyv::*;\n\nmod url;\npub use url::*;\n\n#[cfg(feature = \"multipart\")]\nmod multipart;\n#[cfg(feature = \"multipart\")]\npub use multipart::*;\n\n#[cfg(feature = \"msgpack\")]\nmod msgpack;\n#[cfg(feature = \"msgpack\")]\npub use msgpack::*;\n\n#[cfg(feature = \"postcard\")]\nmod postcard;\n#[cfg(feature = \"postcard\")]\npub use postcard::*;\n\n#[cfg(feature = \"bitcode\")]\nmod bitcode;\n#[cfg(feature = \"bitcode\")]\npub use bitcode::*;\n\n#[cfg(feature = \"bitcode-serde\")]\nmod bitcode_serde;\n#[cfg(feature = \"bitcode-serde\")]\npub use bitcode_serde::*;\n\nmod patch;\npub use patch::*;\nmod post;\npub use post::*;\nmod put;\npub use put::*;\nmod stream;\nuse crate::ContentType;\nuse futures::Future;\nuse http::Method;\npub use stream::*;\n\n/// Serializes a data type into an HTTP request, on the client.\n///\n/// Implementations use the methods of the [`ClientReq`](crate::request::ClientReq) trait to\n/// convert data into a request body. They are often quite short, usually consisting\n/// of just two steps:\n/// 1. Serializing the data into some [`String`], [`Bytes`](bytes::Bytes), or [`Stream`](futures::Stream).\n/// 2. Creating a request with a body of that type.\n///\n/// For example, here’s the implementation for [`Json`].\n///\n/// ```rust,ignore\n/// impl<E, T, Request> IntoReq<Json, Request, E> for T\n/// where\n///     Request: ClientReq<E>,\n///     T: Serialize + Send,\n/// {\n///     fn into_req(\n///         self,\n///         path: &str,\n///         accepts: &str,\n///     ) -> Result<Request, E> {\n///         // try to serialize the data\n///         let data = serde_json::to_string(&self)\n///             .map_err(|e| ServerFnErrorErr::Serialization(e.to_string()).into_app_error())?;\n///         // and use it as the body of a POST request\n///         Request::try_new_post(path, accepts, Json::CONTENT_TYPE, data)\n///     }\n/// }\n/// ```\npub trait IntoReq<Encoding, Request, E> {\n    /// Attempts to serialize the arguments into an HTTP request.\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E>;\n}\n\n/// Deserializes an HTTP request into the data type, on the server.\n///\n/// Implementations use the methods of the [`Req`](crate::Req) trait to access whatever is\n/// needed from the request. They are often quite short, usually consisting\n/// of just two steps:\n/// 1. Extracting the request body into some [`String`], [`Bytes`](bytes::Bytes), or [`Stream`](futures::Stream).\n/// 2. Deserializing that data into the data type.\n///\n/// For example, here’s the implementation for [`Json`].\n///\n/// ```rust,ignore\n/// impl<E, T, Request> FromReq<Json, Request, E> for T\n/// where\n///     // require the Request implement `Req`\n///     Request: Req<E> + Send + 'static,\n///     // require that the type can be deserialized with `serde`\n///     T: DeserializeOwned,\n///     E: FromServerFnError,\n/// {\n///     async fn from_req(\n///         req: Request,\n///     ) -> Result<Self, E> {\n///         // try to convert the body of the request into a `String`\n///         let string_data = req.try_into_string().await?;\n///         // deserialize the data\n///         serde_json::from_str(&string_data)\n///             .map_err(|e| ServerFnErrorErr::Args(e.to_string()).into_app_error())\n///     }\n/// }\n/// ```\npub trait FromReq<Encoding, Request, E>\nwhere\n    Self: Sized,\n{\n    /// Attempts to deserialize the arguments from a request.\n    fn from_req(req: Request) -> impl Future<Output = Result<Self, E>> + Send;\n}\n\n/// Serializes the data type into an HTTP response.\n///\n/// Implementations use the methods of the [`Res`](crate::Res) trait to create a\n/// response. They are often quite short, usually consisting\n/// of just two steps:\n/// 1. Serializing the data type to a [`String`], [`Bytes`](bytes::Bytes), or a [`Stream`](futures::Stream).\n/// 2. Creating a response with that serialized value as its body.\n///\n/// For example, here’s the implementation for [`Json`].\n///\n/// ```rust,ignore\n/// impl<E, T, Response> IntoRes<Json, Response, E> for T\n/// where\n///     Response: Res<E>,\n///     T: Serialize + Send,\n///     E: FromServerFnError,\n/// {\n///     async fn into_res(self) -> Result<Response, E> {\n///         // try to serialize the data\n///         let data = serde_json::to_string(&self)\n///             .map_err(|e| ServerFnErrorErr::Serialization(e.to_string()).into())?;\n///         // and use it as the body of a response\n///         Response::try_from_string(Json::CONTENT_TYPE, data)\n///     }\n/// }\n/// ```\npub trait IntoRes<Encoding, Response, E> {\n    /// Attempts to serialize the output into an HTTP response.\n    fn into_res(self) -> impl Future<Output = Result<Response, E>> + Send;\n}\n\n/// Deserializes the data type from an HTTP response.\n///\n/// Implementations use the methods of the [`ClientRes`](crate::ClientRes) trait to extract\n/// data from a response. They are often quite short, usually consisting\n/// of just two steps:\n/// 1. Extracting a [`String`], [`Bytes`](bytes::Bytes), or a [`Stream`](futures::Stream)\n///    from the response body.\n/// 2. Deserializing the data type from that value.\n///\n/// For example, here’s the implementation for [`Json`].\n///\n/// ```rust,ignore\n/// impl<E, T, Response> FromRes<Json, Response, E> for T\n/// where\n///     Response: ClientRes<E> + Send,\n///     T: DeserializeOwned + Send,\n///     E: FromServerFnError,\n/// {\n///     async fn from_res(\n///         res: Response,\n///     ) -> Result<Self, E> {\n///         // extracts the request body\n///         let data = res.try_into_string().await?;\n///         // and tries to deserialize it as JSON\n///         serde_json::from_str(&data)\n///             .map_err(|e| ServerFnErrorErr::Deserialization(e.to_string()).into_app_error())\n///     }\n/// }\n/// ```\npub trait FromRes<Encoding, Response, E>\nwhere\n    Self: Sized,\n{\n    /// Attempts to deserialize the outputs from a response.\n    fn from_res(res: Response) -> impl Future<Output = Result<Self, E>> + Send;\n}\n\n/// Defines a particular encoding format, which can be used for serializing or deserializing data.\npub trait Encoding: ContentType {\n    /// The HTTP method used for requests.\n    ///\n    /// This should be `POST` in most cases.\n    const METHOD: Method;\n}\n"
  },
  {
    "path": "server_fn/src/codec/msgpack.rs",
    "content": "use crate::{\n    codec::{Patch, Post, Put},\n    ContentType, Decodes, Encodes, Format, FormatType,\n};\nuse bytes::Bytes;\nuse serde::{de::DeserializeOwned, Serialize};\n\n/// Serializes and deserializes MessagePack with [`rmp_serde`].\npub struct MsgPackEncoding;\n\nimpl ContentType for MsgPackEncoding {\n    const CONTENT_TYPE: &'static str = \"application/msgpack\";\n}\n\nimpl FormatType for MsgPackEncoding {\n    const FORMAT_TYPE: Format = Format::Binary;\n}\n\nimpl<T> Encodes<T> for MsgPackEncoding\nwhere\n    T: Serialize,\n{\n    type Error = rmp_serde::encode::Error;\n\n    fn encode(value: &T) -> Result<Bytes, Self::Error> {\n        rmp_serde::to_vec(value).map(Bytes::from)\n    }\n}\n\nimpl<T> Decodes<T> for MsgPackEncoding\nwhere\n    T: DeserializeOwned,\n{\n    type Error = rmp_serde::decode::Error;\n\n    fn decode(bytes: Bytes) -> Result<T, Self::Error> {\n        rmp_serde::from_slice(&bytes)\n    }\n}\n\n/// Pass arguments and receive responses as MessagePack in a `POST` request.\npub type MsgPack = Post<MsgPackEncoding>;\n\n/// Pass arguments and receive responses as MessagePack in the body of a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PatchMsgPack = Patch<MsgPackEncoding>;\n\n/// Pass arguments and receive responses as MessagePack in the body of a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PutMsgPack = Put<MsgPackEncoding>;\n"
  },
  {
    "path": "server_fn/src/codec/multipart.rs",
    "content": "use super::{Encoding, FromReq};\nuse crate::{\n    error::{FromServerFnError, ServerFnErrorWrapper},\n    request::{browser::BrowserFormData, ClientReq, Req},\n    ContentType, IntoReq,\n};\nuse futures::StreamExt;\nuse http::Method;\nuse multer::Multipart;\nuse web_sys::FormData;\n\n/// Encodes multipart form data.\n///\n/// You should primarily use this if you are trying to handle file uploads.\npub struct MultipartFormData;\n\nimpl ContentType for MultipartFormData {\n    const CONTENT_TYPE: &'static str = \"multipart/form-data\";\n}\n\nimpl Encoding for MultipartFormData {\n    const METHOD: Method = Method::POST;\n}\n\n/// Describes whether the multipart data is on the client side or the server side.\n#[derive(Debug)]\npub enum MultipartData {\n    /// `FormData` from the browser.\n    Client(BrowserFormData),\n    /// Generic multipart form using [`multer`]. This implements [`Stream`](futures::Stream).\n    Server(multer::Multipart<'static>),\n}\n\nimpl MultipartData {\n    /// Extracts the inner data to handle as a stream.\n    ///\n    /// On the server side, this always returns `Some(_)`. On the client side, always returns `None`.\n    pub fn into_inner(self) -> Option<Multipart<'static>> {\n        match self {\n            MultipartData::Client(_) => None,\n            MultipartData::Server(data) => Some(data),\n        }\n    }\n\n    /// Extracts the inner form data on the client side.\n    ///\n    /// On the server side, this always returns `None`. On the client side, always returns `Some(_)`.\n    pub fn into_client_data(self) -> Option<BrowserFormData> {\n        match self {\n            MultipartData::Client(data) => Some(data),\n            MultipartData::Server(_) => None,\n        }\n    }\n}\n\nimpl From<FormData> for MultipartData {\n    fn from(value: FormData) -> Self {\n        MultipartData::Client(value.into())\n    }\n}\n\nimpl<E: FromServerFnError, T, Request> IntoReq<MultipartFormData, Request, E>\n    for T\nwhere\n    Request: ClientReq<E, FormData = BrowserFormData>,\n    T: Into<MultipartData>,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let multi = self.into();\n        Request::try_new_post_multipart(\n            path,\n            accepts,\n            multi.into_client_data().unwrap(),\n        )\n    }\n}\n\nimpl<E, T, Request> FromReq<MultipartFormData, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    T: From<MultipartData>,\n    E: FromServerFnError + Send + Sync,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let boundary = req\n            .to_content_type()\n            .and_then(|ct| multer::parse_boundary(ct).ok())\n            .expect(\"couldn't parse boundary\");\n        let stream = req.try_into_stream()?;\n        let data = multer::Multipart::new(\n            stream.map(|data| data.map_err(|e| ServerFnErrorWrapper(E::de(e)))),\n            boundary,\n        );\n        Ok(MultipartData::Server(data).into())\n    }\n}\n"
  },
  {
    "path": "server_fn/src/codec/patch.rs",
    "content": "use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes};\nuse crate::{\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    request::{ClientReq, Req},\n    response::{ClientRes, TryRes},\n    ContentType, Decodes, Encodes,\n};\nuse std::marker::PhantomData;\n\n/// A codec that encodes the data in the patch body\npub struct Patch<Codec>(PhantomData<Codec>);\n\nimpl<Codec: ContentType> ContentType for Patch<Codec> {\n    const CONTENT_TYPE: &'static str = Codec::CONTENT_TYPE;\n}\n\nimpl<Codec: ContentType> Encoding for Patch<Codec> {\n    const METHOD: http::Method = http::Method::PATCH;\n}\n\nimpl<E, T, Encoding, Request> IntoReq<Patch<Encoding>, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    Encoding: Encodes<T>,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let data = Encoding::encode(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_patch_bytes(\n            path,\n            Encoding::CONTENT_TYPE,\n            accepts,\n            data,\n        )\n    }\n}\n\nimpl<E, T, Request, Encoding> FromReq<Patch<Encoding>, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    Encoding: Decodes<T>,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let data = req.try_into_bytes().await?;\n        let s = Encoding::decode(data).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })?;\n        Ok(s)\n    }\n}\n\nimpl<E, Response, Encoding, T> IntoRes<Patch<Encoding>, Response, E> for T\nwhere\n    Response: TryRes<E>,\n    Encoding: Encodes<T>,\n    E: FromServerFnError + Send,\n    T: Send,\n{\n    async fn into_res(self) -> Result<Response, E> {\n        let data = Encoding::encode(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Response::try_from_bytes(Encoding::CONTENT_TYPE, data)\n    }\n}\n\nimpl<E, Encoding, Response, T> FromRes<Patch<Encoding>, Response, E> for T\nwhere\n    Response: ClientRes<E> + Send,\n    Encoding: Decodes<T>,\n    E: FromServerFnError,\n{\n    async fn from_res(res: Response) -> Result<Self, E> {\n        let data = res.try_into_bytes().await?;\n        let s = Encoding::decode(data).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })?;\n        Ok(s)\n    }\n}\n"
  },
  {
    "path": "server_fn/src/codec/post.rs",
    "content": "use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes};\nuse crate::{\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    request::{ClientReq, Req},\n    response::{ClientRes, TryRes},\n    ContentType, Decodes, Encodes,\n};\nuse std::marker::PhantomData;\n\n/// A codec that encodes the data in the post body\npub struct Post<Codec>(PhantomData<Codec>);\n\nimpl<Codec: ContentType> ContentType for Post<Codec> {\n    const CONTENT_TYPE: &'static str = Codec::CONTENT_TYPE;\n}\n\nimpl<Codec: ContentType> Encoding for Post<Codec> {\n    const METHOD: http::Method = http::Method::POST;\n}\n\nimpl<E, T, Encoding, Request> IntoReq<Post<Encoding>, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    Encoding: Encodes<T>,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let data = Encoding::encode(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_post_bytes(path, Encoding::CONTENT_TYPE, accepts, data)\n    }\n}\n\nimpl<E, T, Request, Encoding> FromReq<Post<Encoding>, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    Encoding: Decodes<T>,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let data = req.try_into_bytes().await?;\n        let s = Encoding::decode(data).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })?;\n        Ok(s)\n    }\n}\n\nimpl<E, Response, Encoding, T> IntoRes<Post<Encoding>, Response, E> for T\nwhere\n    Response: TryRes<E>,\n    Encoding: Encodes<T>,\n    E: FromServerFnError + Send,\n    T: Send,\n{\n    async fn into_res(self) -> Result<Response, E> {\n        let data = Encoding::encode(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Response::try_from_bytes(Encoding::CONTENT_TYPE, data)\n    }\n}\n\nimpl<E, Encoding, Response, T> FromRes<Post<Encoding>, Response, E> for T\nwhere\n    Response: ClientRes<E> + Send,\n    Encoding: Decodes<T>,\n    E: FromServerFnError,\n{\n    async fn from_res(res: Response) -> Result<Self, E> {\n        let data = res.try_into_bytes().await?;\n        let s = Encoding::decode(data).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })?;\n        Ok(s)\n    }\n}\n"
  },
  {
    "path": "server_fn/src/codec/postcard.rs",
    "content": "use crate::{\n    codec::{Patch, Post, Put},\n    ContentType, Decodes, Encodes, Format, FormatType,\n};\nuse bytes::Bytes;\nuse serde::{de::DeserializeOwned, Serialize};\n\n/// A codec for Postcard.\npub struct PostcardEncoding;\n\nimpl ContentType for PostcardEncoding {\n    const CONTENT_TYPE: &'static str = \"application/x-postcard\";\n}\n\nimpl FormatType for PostcardEncoding {\n    const FORMAT_TYPE: Format = Format::Binary;\n}\n\nimpl<T> Encodes<T> for PostcardEncoding\nwhere\n    T: Serialize,\n{\n    type Error = postcard::Error;\n\n    fn encode(value: &T) -> Result<Bytes, Self::Error> {\n        postcard::to_allocvec(value).map(Bytes::from)\n    }\n}\n\nimpl<T> Decodes<T> for PostcardEncoding\nwhere\n    T: DeserializeOwned,\n{\n    type Error = postcard::Error;\n\n    fn decode(bytes: Bytes) -> Result<T, Self::Error> {\n        postcard::from_bytes(&bytes)\n    }\n}\n\n/// Pass arguments and receive responses with postcard in a `POST` request.\npub type Postcard = Post<PostcardEncoding>;\n\n/// Pass arguments and receive responses with postcard in a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PatchPostcard = Patch<PostcardEncoding>;\n\n/// Pass arguments and receive responses with postcard in a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PutPostcard = Put<PostcardEncoding>;\n"
  },
  {
    "path": "server_fn/src/codec/put.rs",
    "content": "use super::{Encoding, FromReq, FromRes, IntoReq, IntoRes};\nuse crate::{\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    request::{ClientReq, Req},\n    response::{ClientRes, TryRes},\n    ContentType, Decodes, Encodes,\n};\nuse std::marker::PhantomData;\n\n/// A codec that encodes the data in the put body\npub struct Put<Codec>(PhantomData<Codec>);\n\nimpl<Codec: ContentType> ContentType for Put<Codec> {\n    const CONTENT_TYPE: &'static str = Codec::CONTENT_TYPE;\n}\n\nimpl<Codec: ContentType> Encoding for Put<Codec> {\n    const METHOD: http::Method = http::Method::PUT;\n}\n\nimpl<E, T, Encoding, Request> IntoReq<Put<Encoding>, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    Encoding: Encodes<T>,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let data = Encoding::encode(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_put_bytes(path, Encoding::CONTENT_TYPE, accepts, data)\n    }\n}\n\nimpl<E, T, Request, Encoding> FromReq<Put<Encoding>, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    Encoding: Decodes<T>,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let data = req.try_into_bytes().await?;\n        let s = Encoding::decode(data).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })?;\n        Ok(s)\n    }\n}\n\nimpl<E, Response, Encoding, T> IntoRes<Put<Encoding>, Response, E> for T\nwhere\n    Response: TryRes<E>,\n    Encoding: Encodes<T>,\n    E: FromServerFnError + Send,\n    T: Send,\n{\n    async fn into_res(self) -> Result<Response, E> {\n        let data = Encoding::encode(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Response::try_from_bytes(Encoding::CONTENT_TYPE, data)\n    }\n}\n\nimpl<E, Encoding, Response, T> FromRes<Put<Encoding>, Response, E> for T\nwhere\n    Response: ClientRes<E> + Send,\n    Encoding: Decodes<T>,\n    E: FromServerFnError,\n{\n    async fn from_res(res: Response) -> Result<Self, E> {\n        let data = res.try_into_bytes().await?;\n        let s = Encoding::decode(data).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })?;\n        Ok(s)\n    }\n}\n"
  },
  {
    "path": "server_fn/src/codec/rkyv.rs",
    "content": "use crate::{\n    codec::{Patch, Post, Put},\n    ContentType, Decodes, Encodes, Format, FormatType,\n};\nuse bytes::Bytes;\nuse rkyv::{\n    api::high::{HighDeserializer, HighSerializer, HighValidator},\n    bytecheck::CheckBytes,\n    rancor,\n    ser::allocator::ArenaHandle,\n    util::AlignedVec,\n    Archive, Deserialize, Serialize,\n};\n\ntype RkyvSerializer<'a> =\n    HighSerializer<AlignedVec, ArenaHandle<'a>, rancor::Error>;\ntype RkyvDeserializer = HighDeserializer<rancor::Error>;\ntype RkyvValidator<'a> = HighValidator<'a, rancor::Error>;\n\n/// Pass arguments and receive responses using `rkyv` in a `POST` request.\npub struct RkyvEncoding;\n\nimpl ContentType for RkyvEncoding {\n    const CONTENT_TYPE: &'static str = \"application/rkyv\";\n}\n\nimpl FormatType for RkyvEncoding {\n    const FORMAT_TYPE: Format = Format::Binary;\n}\n\nimpl<T> Encodes<T> for RkyvEncoding\nwhere\n    T: Archive + for<'a> Serialize<RkyvSerializer<'a>>,\n    T::Archived: Deserialize<T, RkyvDeserializer>\n        + for<'a> CheckBytes<RkyvValidator<'a>>,\n{\n    type Error = rancor::Error;\n\n    fn encode(value: &T) -> Result<Bytes, Self::Error> {\n        let encoded = rkyv::to_bytes::<rancor::Error>(value)?;\n        Ok(Bytes::copy_from_slice(encoded.as_ref()))\n    }\n}\n\nimpl<T> Decodes<T> for RkyvEncoding\nwhere\n    T: Archive + for<'a> Serialize<RkyvSerializer<'a>>,\n    T::Archived: Deserialize<T, RkyvDeserializer>\n        + for<'a> CheckBytes<RkyvValidator<'a>>,\n{\n    type Error = rancor::Error;\n\n    fn decode(bytes: Bytes) -> Result<T, Self::Error> {\n        let mut aligned = AlignedVec::<1024>::new();\n        aligned.extend_from_slice(bytes.as_ref());\n        rkyv::from_bytes::<T, rancor::Error>(aligned.as_ref())\n    }\n}\n\n/// Pass arguments and receive responses as `rkyv` in a `POST` request.\npub type Rkyv = Post<RkyvEncoding>;\n\n/// Pass arguments and receive responses as `rkyv` in a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PatchRkyv = Patch<RkyvEncoding>;\n\n/// Pass arguments and receive responses as `rkyv` in a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PutRkyv = Put<RkyvEncoding>;\n"
  },
  {
    "path": "server_fn/src/codec/serde_lite.rs",
    "content": "use crate::{\n    codec::{Patch, Post, Put},\n    error::ServerFnErrorErr,\n    ContentType, Decodes, Encodes, Format, FormatType,\n};\nuse bytes::Bytes;\nuse serde_lite::{Deserialize, Serialize};\n\n/// Pass arguments and receive responses as JSON in the body of a `POST` request.\npub struct SerdeLiteEncoding;\n\nimpl ContentType for SerdeLiteEncoding {\n    const CONTENT_TYPE: &'static str = \"application/json\";\n}\n\nimpl FormatType for SerdeLiteEncoding {\n    const FORMAT_TYPE: Format = Format::Text;\n}\n\nimpl<T> Encodes<T> for SerdeLiteEncoding\nwhere\n    T: Serialize,\n{\n    type Error = ServerFnErrorErr;\n\n    fn encode(value: &T) -> Result<Bytes, Self::Error> {\n        serde_json::to_vec(\n            &value\n                .serialize()\n                .map_err(|e| ServerFnErrorErr::Serialization(e.to_string()))?,\n        )\n        .map_err(|e| ServerFnErrorErr::Serialization(e.to_string()))\n        .map(Bytes::from)\n    }\n}\n\nimpl<T> Decodes<T> for SerdeLiteEncoding\nwhere\n    T: Deserialize,\n{\n    type Error = ServerFnErrorErr;\n\n    fn decode(bytes: Bytes) -> Result<T, Self::Error> {\n        T::deserialize(\n            &serde_json::from_slice(&bytes).map_err(|e| {\n                ServerFnErrorErr::Deserialization(e.to_string())\n            })?,\n        )\n        .map_err(|e| ServerFnErrorErr::Deserialization(e.to_string()))\n    }\n}\n\n/// Pass arguments and receive responses as JSON in the body of a `POST` request.\npub type SerdeLite = Post<SerdeLiteEncoding>;\n\n/// Pass arguments and receive responses as JSON in the body of a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PatchSerdeLite = Patch<SerdeLiteEncoding>;\n\n/// Pass arguments and receive responses as JSON in the body of a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub type PutSerdeLite = Put<SerdeLiteEncoding>;\n"
  },
  {
    "path": "server_fn/src/codec/stream.rs",
    "content": "use super::{Encoding, FromReq, FromRes, IntoReq};\nuse crate::{\n    error::{FromServerFnError, ServerFnErrorErr},\n    request::{ClientReq, Req},\n    response::{ClientRes, TryRes},\n    ContentType, IntoRes, ServerFnError,\n};\nuse bytes::Bytes;\nuse futures::{Stream, StreamExt, TryStreamExt};\nuse http::Method;\nuse std::{fmt::Debug, pin::Pin};\n\n/// An encoding that represents a stream of bytes.\n///\n/// A server function that uses this as its output encoding should return [`ByteStream`].\n///\n/// ## Browser Support for Streaming Input\n///\n/// Browser fetch requests do not currently support full request duplexing, which\n/// means that that they do begin handling responses until the full request has been sent.\n/// This means that if you use a streaming input encoding, the input stream needs to\n/// end before the output will begin.\n///\n/// Streaming requests are only allowed over HTTP2 or HTTP3.\npub struct Streaming;\n\nimpl ContentType for Streaming {\n    const CONTENT_TYPE: &'static str = \"application/octet-stream\";\n}\n\nimpl Encoding for Streaming {\n    const METHOD: Method = Method::POST;\n}\n\nimpl<E, T, Request> IntoReq<Streaming, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    T: Stream<Item = Bytes> + Send + 'static,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        Request::try_new_post_streaming(\n            path,\n            accepts,\n            Streaming::CONTENT_TYPE,\n            self,\n        )\n    }\n}\n\nimpl<E, T, Request> FromReq<Streaming, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    T: From<ByteStream<E>> + 'static,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let data = req.try_into_stream()?;\n        let s = ByteStream::new(data.map_err(|e| E::de(e)));\n        Ok(s.into())\n    }\n}\n\n/// A stream of bytes.\n///\n/// A server function can return this type if its output encoding is [`Streaming`].\n///\n/// ## Browser Support for Streaming Input\n///\n/// Browser fetch requests do not currently support full request duplexing, which\n/// means that that they do begin handling responses until the full request has been sent.\n/// This means that if you use a streaming input encoding, the input stream needs to\n/// end before the output will begin.\n///\n/// Streaming requests are only allowed over HTTP2 or HTTP3.\npub struct ByteStream<E = ServerFnError>(\n    Pin<Box<dyn Stream<Item = Result<Bytes, E>> + Send>>,\n);\n\nimpl<E> ByteStream<E> {\n    /// Consumes the wrapper, returning a stream of bytes.\n    pub fn into_inner(self) -> impl Stream<Item = Result<Bytes, E>> + Send {\n        self.0\n    }\n}\n\nimpl<E> Debug for ByteStream<E> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_tuple(\"ByteStream\").finish()\n    }\n}\n\nimpl<E> ByteStream<E> {\n    /// Creates a new `ByteStream` from the given stream.\n    pub fn new<T>(\n        value: impl Stream<Item = Result<T, E>> + Send + 'static,\n    ) -> Self\n    where\n        T: Into<Bytes>,\n    {\n        Self(Box::pin(value.map(|value| value.map(Into::into))))\n    }\n}\n\nimpl<E, S, T> From<S> for ByteStream<E>\nwhere\n    S: Stream<Item = T> + Send + 'static,\n    T: Into<Bytes>,\n{\n    fn from(value: S) -> Self {\n        Self(Box::pin(value.map(|data| Ok(data.into()))))\n    }\n}\n\nimpl<E, Response> IntoRes<Streaming, Response, E> for ByteStream<E>\nwhere\n    Response: TryRes<E>,\n    E: FromServerFnError,\n{\n    async fn into_res(self) -> Result<Response, E> {\n        Response::try_from_stream(\n            Streaming::CONTENT_TYPE,\n            self.into_inner().map_err(|e| e.ser()),\n        )\n    }\n}\n\nimpl<E, Response> FromRes<Streaming, Response, E> for ByteStream<E>\nwhere\n    Response: ClientRes<E> + Send,\n    E: FromServerFnError,\n{\n    async fn from_res(res: Response) -> Result<Self, E> {\n        let stream = res.try_into_stream()?;\n        Ok(ByteStream::new(stream.map_err(|e| E::de(e))))\n    }\n}\n\n/// An encoding that represents a stream of text.\n///\n/// A server function that uses this as its output encoding should return [`TextStream`].\n///\n/// ## Browser Support for Streaming Input\n///\n/// Browser fetch requests do not currently support full request duplexing, which\n/// means that that they do begin handling responses until the full request has been sent.\n/// This means that if you use a streaming input encoding, the input stream needs to\n/// end before the output will begin.\n///\n/// Streaming requests are only allowed over HTTP2 or HTTP3.\npub struct StreamingText;\n\nimpl ContentType for StreamingText {\n    const CONTENT_TYPE: &'static str = \"text/plain\";\n}\n\nimpl Encoding for StreamingText {\n    const METHOD: Method = Method::POST;\n}\n\n/// A stream of text.\n///\n/// A server function can return this type if its output encoding is [`StreamingText`].\n///\n/// ## Browser Support for Streaming Input\n///\n/// Browser fetch requests do not currently support full request duplexing, which\n/// means that that they do begin handling responses until the full request has been sent.\n/// This means that if you use a streaming input encoding, the input stream needs to\n/// end before the output will begin.\n///\n/// Streaming requests are only allowed over HTTP2 or HTTP3.\npub struct TextStream<E = ServerFnError>(\n    Pin<Box<dyn Stream<Item = Result<String, E>> + Send>>,\n);\n\nimpl<E> Debug for TextStream<E> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_tuple(\"TextStream\").finish()\n    }\n}\n\nimpl<E> TextStream<E> {\n    /// Creates a new `TextStream` from the given stream.\n    pub fn new(\n        value: impl Stream<Item = Result<String, E>> + Send + 'static,\n    ) -> Self {\n        Self(Box::pin(value.map(|value| value)))\n    }\n}\n\nimpl<E> TextStream<E> {\n    /// Consumes the wrapper, returning a stream of text.\n    pub fn into_inner(self) -> impl Stream<Item = Result<String, E>> + Send {\n        self.0\n    }\n}\n\nimpl<E, S, T> From<S> for TextStream<E>\nwhere\n    S: Stream<Item = T> + Send + 'static,\n    T: Into<String>,\n{\n    fn from(value: S) -> Self {\n        Self(Box::pin(value.map(|data| Ok(data.into()))))\n    }\n}\n\nimpl<E, T, Request> IntoReq<StreamingText, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    T: Into<TextStream<E>>,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let data = self.into();\n        Request::try_new_post_streaming(\n            path,\n            accepts,\n            Streaming::CONTENT_TYPE,\n            data.0.map(|chunk| chunk.unwrap_or_default().into()),\n        )\n    }\n}\n\nimpl<E, T, Request> FromReq<StreamingText, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    T: From<TextStream<E>> + 'static,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let data = req.try_into_stream()?;\n        let s = TextStream::new(data.map(|chunk| match chunk {\n            Ok(bytes) => {\n                let de = String::from_utf8(bytes.to_vec()).map_err(|e| {\n                    E::from_server_fn_error(ServerFnErrorErr::Deserialization(\n                        e.to_string(),\n                    ))\n                })?;\n                Ok(de)\n            }\n            Err(bytes) => Err(E::de(bytes)),\n        }));\n        Ok(s.into())\n    }\n}\n\nimpl<E, Response> IntoRes<StreamingText, Response, E> for TextStream<E>\nwhere\n    Response: TryRes<E>,\n    E: FromServerFnError,\n{\n    async fn into_res(self) -> Result<Response, E> {\n        Response::try_from_stream(\n            Streaming::CONTENT_TYPE,\n            self.into_inner()\n                .map(|stream| stream.map(Into::into).map_err(|e| e.ser())),\n        )\n    }\n}\n\nimpl<E, Response> FromRes<StreamingText, Response, E> for TextStream<E>\nwhere\n    Response: ClientRes<E> + Send,\n    E: FromServerFnError,\n{\n    async fn from_res(res: Response) -> Result<Self, E> {\n        let stream = res.try_into_stream()?;\n        Ok(TextStream(Box::pin(stream.map(|chunk| match chunk {\n            Ok(bytes) => {\n                let de = String::from_utf8(bytes.into()).map_err(|e| {\n                    E::from_server_fn_error(ServerFnErrorErr::Deserialization(\n                        e.to_string(),\n                    ))\n                })?;\n                Ok(de)\n            }\n            Err(bytes) => Err(E::de(bytes)),\n        }))))\n    }\n}\n"
  },
  {
    "path": "server_fn/src/codec/url.rs",
    "content": "use super::{Encoding, FromReq, IntoReq};\nuse crate::{\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    request::{ClientReq, Req},\n    ContentType,\n};\nuse http::Method;\nuse serde::{de::DeserializeOwned, Serialize};\n\n/// Pass arguments as a URL-encoded query string of a `GET` request.\npub struct GetUrl;\n\n/// Pass arguments as the URL-encoded body of a `POST` request.\npub struct PostUrl;\n\n/// Pass arguments as the URL-encoded query string of a `DELETE` request.\n/// **Note**: Browser support for `DELETE` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub struct DeleteUrl;\n\n/// Pass arguments as the URL-encoded body of a `PATCH` request.\n/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub struct PatchUrl;\n\n/// Pass arguments as the URL-encoded body of a `PUT` request.\n/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n/// Consider using a `POST` request if functionality without JS/WASM is required.\npub struct PutUrl;\n\nimpl ContentType for GetUrl {\n    const CONTENT_TYPE: &'static str = \"application/x-www-form-urlencoded\";\n}\n\nimpl Encoding for GetUrl {\n    const METHOD: Method = Method::GET;\n}\n\nimpl<E, T, Request> IntoReq<GetUrl, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    T: Serialize + Send,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let data = serde_qs::to_string(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_get(path, GetUrl::CONTENT_TYPE, accepts, &data)\n    }\n}\n\nimpl<E, T, Request> FromReq<GetUrl, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    T: DeserializeOwned,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let string_data = req.as_query().unwrap_or_default();\n        let args = serde_qs::Config::new(5, false)\n            .deserialize_str::<Self>(string_data)\n            .map_err(|e| {\n                ServerFnErrorErr::Args(e.to_string()).into_app_error()\n            })?;\n        Ok(args)\n    }\n}\n\nimpl ContentType for PostUrl {\n    const CONTENT_TYPE: &'static str = \"application/x-www-form-urlencoded\";\n}\n\nimpl Encoding for PostUrl {\n    const METHOD: Method = Method::POST;\n}\n\nimpl<E, T, Request> IntoReq<PostUrl, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    T: Serialize + Send,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let qs = serde_qs::to_string(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_post(path, PostUrl::CONTENT_TYPE, accepts, qs)\n    }\n}\n\nimpl<E, T, Request> FromReq<PostUrl, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    T: DeserializeOwned,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let string_data = req.try_into_string().await?;\n        let args = serde_qs::Config::new(5, false)\n            .deserialize_str::<Self>(&string_data)\n            .map_err(|e| {\n                ServerFnErrorErr::Args(e.to_string()).into_app_error()\n            })?;\n        Ok(args)\n    }\n}\n\nimpl ContentType for DeleteUrl {\n    const CONTENT_TYPE: &'static str = \"application/x-www-form-urlencoded\";\n}\n\nimpl Encoding for DeleteUrl {\n    const METHOD: Method = Method::DELETE;\n}\n\nimpl<E, T, Request> IntoReq<DeleteUrl, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    T: Serialize + Send,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let data = serde_qs::to_string(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_delete(path, DeleteUrl::CONTENT_TYPE, accepts, &data)\n    }\n}\n\nimpl<E, T, Request> FromReq<DeleteUrl, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    T: DeserializeOwned,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let string_data = req.as_query().unwrap_or_default();\n        let args = serde_qs::Config::new(5, false)\n            .deserialize_str::<Self>(string_data)\n            .map_err(|e| {\n                ServerFnErrorErr::Args(e.to_string()).into_app_error()\n            })?;\n        Ok(args)\n    }\n}\n\nimpl ContentType for PatchUrl {\n    const CONTENT_TYPE: &'static str = \"application/x-www-form-urlencoded\";\n}\n\nimpl Encoding for PatchUrl {\n    const METHOD: Method = Method::PATCH;\n}\n\nimpl<E, T, Request> IntoReq<PatchUrl, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    T: Serialize + Send,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let data = serde_qs::to_string(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_patch(path, PatchUrl::CONTENT_TYPE, accepts, data)\n    }\n}\n\nimpl<E, T, Request> FromReq<PatchUrl, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    T: DeserializeOwned,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let string_data = req.try_into_string().await?;\n        let args = serde_qs::Config::new(5, false)\n            .deserialize_str::<Self>(&string_data)\n            .map_err(|e| {\n                ServerFnErrorErr::Args(e.to_string()).into_app_error()\n            })?;\n        Ok(args)\n    }\n}\n\nimpl ContentType for PutUrl {\n    const CONTENT_TYPE: &'static str = \"application/x-www-form-urlencoded\";\n}\n\nimpl Encoding for PutUrl {\n    const METHOD: Method = Method::PUT;\n}\n\nimpl<E, T, Request> IntoReq<PutUrl, Request, E> for T\nwhere\n    Request: ClientReq<E>,\n    T: Serialize + Send,\n    E: FromServerFnError,\n{\n    fn into_req(self, path: &str, accepts: &str) -> Result<Request, E> {\n        let data = serde_qs::to_string(&self).map_err(|e| {\n            ServerFnErrorErr::Serialization(e.to_string()).into_app_error()\n        })?;\n        Request::try_new_put(path, PutUrl::CONTENT_TYPE, accepts, data)\n    }\n}\n\nimpl<E, T, Request> FromReq<PutUrl, Request, E> for T\nwhere\n    Request: Req<E> + Send + 'static,\n    T: DeserializeOwned,\n    E: FromServerFnError,\n{\n    async fn from_req(req: Request) -> Result<Self, E> {\n        let string_data = req.try_into_string().await?;\n        let args = serde_qs::Config::new(5, false)\n            .deserialize_str::<Self>(&string_data)\n            .map_err(|e| {\n                ServerFnErrorErr::Args(e.to_string()).into_app_error()\n            })?;\n        Ok(args)\n    }\n}\n"
  },
  {
    "path": "server_fn/src/error.rs",
    "content": "#![allow(deprecated)]\n\nuse crate::{ContentType, Decodes, Encodes, Format, FormatType};\nuse base64::{engine::general_purpose::URL_SAFE, Engine as _};\nuse bytes::Bytes;\nuse serde::{Deserialize, Serialize};\nuse std::{\n    fmt::{self, Display, Write},\n    str::FromStr,\n};\nuse throw_error::Error;\nuse url::Url;\n\n/// A custom header that can be used to indicate a server function returned an error.\npub const SERVER_FN_ERROR_HEADER: &str = \"serverfnerror\";\n\nimpl From<ServerFnError> for Error {\n    fn from(e: ServerFnError) -> Self {\n        Error::from(ServerFnErrorWrapper(e))\n    }\n}\n\n/// An empty value indicating that there is no custom error type associated\n/// with this server function.\n#[derive(\n    Debug,\n    Deserialize,\n    Serialize,\n    PartialEq,\n    Eq,\n    Hash,\n    PartialOrd,\n    Ord,\n    Clone,\n    Copy,\n)]\n#[cfg_attr(\n    feature = \"rkyv\",\n    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)\n)]\n#[deprecated(\n    since = \"0.8.0\",\n    note = \"Now server_fn can return any error type other than ServerFnError, \\\n            so the WrappedServerError variant will be removed in 0.9.0\"\n)]\npub struct NoCustomError;\n\n// Implement `Display` for `NoCustomError`\nimpl fmt::Display for NoCustomError {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"Unit Type Displayed\")\n    }\n}\n\nimpl FromStr for NoCustomError {\n    type Err = ();\n\n    fn from_str(_s: &str) -> Result<Self, Self::Err> {\n        Ok(NoCustomError)\n    }\n}\n\n/// Wraps some error type, which may implement any of [`Error`](trait@std::error::Error), [`Clone`], or\n/// [`Display`].\n#[derive(Debug)]\n#[deprecated(\n    since = \"0.8.0\",\n    note = \"Now server_fn can return any error type other than ServerFnError, \\\n            so the WrappedServerError variant will be removed in 0.9.0\"\n)]\npub struct WrapError<T>(pub T);\n\n/// A helper macro to convert a variety of different types into `ServerFnError`.\n/// This should mostly be used if you are implementing `From<ServerFnError>` for `YourError`.\n#[macro_export]\n#[deprecated(\n    since = \"0.8.0\",\n    note = \"Now server_fn can return any error type other than ServerFnError, \\\n            so the WrappedServerError variant will be removed in 0.9.0\"\n)]\nmacro_rules! server_fn_error {\n    () => {{\n        use $crate::{ViaError, WrapError};\n        (&&&&&WrapError(())).to_server_error()\n    }};\n    ($err:expr) => {{\n        use $crate::error::{ViaError, WrapError};\n        match $err {\n            error => (&&&&&WrapError(error)).to_server_error(),\n        }\n    }};\n}\n\n/// This trait serves as the conversion method between a variety of types\n/// and [`ServerFnError`].\n#[deprecated(\n    since = \"0.8.0\",\n    note = \"Now server_fn can return any error type other than ServerFnError, \\\n            so users should place their custom error type instead of \\\n            ServerFnError\"\n)]\npub trait ViaError<E> {\n    /// Converts something into an error.\n    fn to_server_error(&self) -> ServerFnError<E>;\n}\n\n// This impl should catch if you fed it a [`ServerFnError`] already.\nimpl<E: ServerFnErrorKind + std::error::Error + Clone> ViaError<E>\n    for &&&&WrapError<ServerFnError<E>>\n{\n    fn to_server_error(&self) -> ServerFnError<E> {\n        self.0.clone()\n    }\n}\n\n// A type tag for ServerFnError so we can special case it\n#[deprecated]\npub(crate) trait ServerFnErrorKind {}\n\nimpl ServerFnErrorKind for ServerFnError {}\n\n// This impl should catch passing () or nothing to server_fn_error\nimpl ViaError<NoCustomError> for &&&WrapError<()> {\n    fn to_server_error(&self) -> ServerFnError {\n        ServerFnError::WrappedServerError(NoCustomError)\n    }\n}\n\n// This impl will catch any type that implements any type that impls\n// Error and Clone, so that it can be wrapped into ServerFnError\nimpl<E: std::error::Error + Clone> ViaError<E> for &&WrapError<E> {\n    fn to_server_error(&self) -> ServerFnError<E> {\n        ServerFnError::WrappedServerError(self.0.clone())\n    }\n}\n\n// If it doesn't impl Error, but does impl Display and Clone,\n// we can still wrap it in String form\nimpl<E: Display + Clone> ViaError<E> for &WrapError<E> {\n    fn to_server_error(&self) -> ServerFnError<E> {\n        ServerFnError::ServerError(self.0.to_string())\n    }\n}\n\n// This is what happens if someone tries to pass in something that does\n// not meet the above criteria\nimpl<E> ViaError<E> for WrapError<E> {\n    #[track_caller]\n    fn to_server_error(&self) -> ServerFnError<E> {\n        panic!(\n            \"At {}, you call `to_server_error()` or use  `server_fn_error!` \\\n             with a value that does not implement `Clone` and either `Error` \\\n             or `Display`.\",\n            std::panic::Location::caller()\n        );\n    }\n}\n\n/// A type that can be used as the return type of the server function for easy error conversion with `?` operator.\n/// This type can be replaced with any other error type that implements `FromServerFnError`.\n///\n/// Unlike [`ServerFnErrorErr`], this does not implement [`Error`](trait@std::error::Error).\n/// This means that other error types can easily be converted into it using the\n/// `?` operator.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\n#[cfg_attr(\n    feature = \"rkyv\",\n    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)\n)]\npub enum ServerFnError<E = NoCustomError> {\n    #[deprecated(\n        since = \"0.8.0\",\n        note = \"Now server_fn can return any error type other than \\\n                ServerFnError, so users should place their custom error type \\\n                instead of ServerFnError\"\n    )]\n    /// A user-defined custom error type, which defaults to [`NoCustomError`].\n    WrappedServerError(E),\n    /// Error while trying to register the server function (only occurs in case of poisoned RwLock).\n    Registration(String),\n    /// Occurs on the client if there is a network error while trying to run function on server.\n    Request(String),\n    /// Occurs on the server if there is an error creating an HTTP response.\n    Response(String),\n    /// Occurs when there is an error while actually running the function on the server.\n    ServerError(String),\n    /// Occurs when there is an error while actually running the middleware on the server.\n    MiddlewareError(String),\n    /// Occurs on the client if there is an error deserializing the server's response.\n    Deserialization(String),\n    /// Occurs on the client if there is an error serializing the server function arguments.\n    Serialization(String),\n    /// Occurs on the server if there is an error deserializing one of the arguments that's been sent.\n    Args(String),\n    /// Occurs on the server if there's a missing argument.\n    MissingArg(String),\n}\n\nimpl ServerFnError<NoCustomError> {\n    /// Constructs a new [`ServerFnError::ServerError`] from some other type.\n    pub fn new(msg: impl ToString) -> Self {\n        Self::ServerError(msg.to_string())\n    }\n}\n\nimpl<CustErr> From<CustErr> for ServerFnError<CustErr> {\n    fn from(value: CustErr) -> Self {\n        ServerFnError::WrappedServerError(value)\n    }\n}\n\nimpl<E: std::error::Error> From<E> for ServerFnError {\n    fn from(value: E) -> Self {\n        ServerFnError::ServerError(value.to_string())\n    }\n}\n\nimpl<CustErr> Display for ServerFnError<CustErr>\nwhere\n    CustErr: Display,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(\n            f,\n            \"{}\",\n            match self {\n                ServerFnError::Registration(s) => format!(\n                    \"error while trying to register the server function: {s}\"\n                ),\n                ServerFnError::Request(s) => format!(\n                    \"error reaching server to call server function: {s}\"\n                ),\n                ServerFnError::ServerError(s) =>\n                    format!(\"error running server function: {s}\"),\n                ServerFnError::MiddlewareError(s) =>\n                    format!(\"error running middleware: {s}\"),\n                ServerFnError::Deserialization(s) =>\n                    format!(\"error deserializing server function results: {s}\"),\n                ServerFnError::Serialization(s) =>\n                    format!(\"error serializing server function arguments: {s}\"),\n                ServerFnError::Args(s) => format!(\n                    \"error deserializing server function arguments: {s}\"\n                ),\n                ServerFnError::MissingArg(s) => format!(\"missing argument {s}\"),\n                ServerFnError::Response(s) =>\n                    format!(\"error generating HTTP response: {s}\"),\n                ServerFnError::WrappedServerError(e) => format!(\"{e}\"),\n            }\n        )\n    }\n}\n\n/// Serializes and deserializes JSON with [`serde_json`].\npub struct ServerFnErrorEncoding;\n\nimpl ContentType for ServerFnErrorEncoding {\n    const CONTENT_TYPE: &'static str = \"text/plain\";\n}\n\nimpl FormatType for ServerFnErrorEncoding {\n    const FORMAT_TYPE: Format = Format::Text;\n}\n\nimpl<CustErr> Encodes<ServerFnError<CustErr>> for ServerFnErrorEncoding\nwhere\n    CustErr: Display,\n{\n    type Error = std::fmt::Error;\n\n    fn encode(output: &ServerFnError<CustErr>) -> Result<Bytes, Self::Error> {\n        let mut buf = String::new();\n        let result = match output {\n            ServerFnError::WrappedServerError(e) => {\n                write!(&mut buf, \"WrappedServerFn|{e}\")\n            }\n            ServerFnError::Registration(e) => {\n                write!(&mut buf, \"Registration|{e}\")\n            }\n            ServerFnError::Request(e) => write!(&mut buf, \"Request|{e}\"),\n            ServerFnError::Response(e) => write!(&mut buf, \"Response|{e}\"),\n            ServerFnError::ServerError(e) => {\n                write!(&mut buf, \"ServerError|{e}\")\n            }\n            ServerFnError::MiddlewareError(e) => {\n                write!(&mut buf, \"MiddlewareError|{e}\")\n            }\n            ServerFnError::Deserialization(e) => {\n                write!(&mut buf, \"Deserialization|{e}\")\n            }\n            ServerFnError::Serialization(e) => {\n                write!(&mut buf, \"Serialization|{e}\")\n            }\n            ServerFnError::Args(e) => write!(&mut buf, \"Args|{e}\"),\n            ServerFnError::MissingArg(e) => {\n                write!(&mut buf, \"MissingArg|{e}\")\n            }\n        };\n\n        match result {\n            Ok(()) => Ok(Bytes::from(buf)),\n            Err(e) => Err(e),\n        }\n    }\n}\n\nimpl<CustErr> Decodes<ServerFnError<CustErr>> for ServerFnErrorEncoding\nwhere\n    CustErr: FromStr,\n{\n    type Error = String;\n\n    fn decode(bytes: Bytes) -> Result<ServerFnError<CustErr>, Self::Error> {\n        let data = String::from_utf8(bytes.to_vec())\n            .map_err(|err| format!(\"UTF-8 conversion error: {err}\"))?;\n\n        data.split_once('|')\n            .ok_or_else(|| {\n                format!(\"Invalid format: missing delimiter in {data:?}\")\n            })\n            .and_then(|(ty, data)| match ty {\n                \"WrappedServerFn\" => CustErr::from_str(data)\n                    .map(ServerFnError::WrappedServerError)\n                    .map_err(|_| {\n                        format!(\"Failed to parse CustErr from {data:?}\")\n                    }),\n                \"Registration\" => {\n                    Ok(ServerFnError::Registration(data.to_string()))\n                }\n                \"Request\" => Ok(ServerFnError::Request(data.to_string())),\n                \"Response\" => Ok(ServerFnError::Response(data.to_string())),\n                \"ServerError\" => {\n                    Ok(ServerFnError::ServerError(data.to_string()))\n                }\n                \"MiddlewareError\" => {\n                    Ok(ServerFnError::MiddlewareError(data.to_string()))\n                }\n                \"Deserialization\" => {\n                    Ok(ServerFnError::Deserialization(data.to_string()))\n                }\n                \"Serialization\" => {\n                    Ok(ServerFnError::Serialization(data.to_string()))\n                }\n                \"Args\" => Ok(ServerFnError::Args(data.to_string())),\n                \"MissingArg\" => Ok(ServerFnError::MissingArg(data.to_string())),\n                _ => Err(format!(\"Unknown error type: {ty}\")),\n            })\n    }\n}\n\nimpl<CustErr> FromServerFnError for ServerFnError<CustErr>\nwhere\n    CustErr: std::fmt::Debug + Display + FromStr + 'static,\n{\n    type Encoder = ServerFnErrorEncoding;\n\n    fn from_server_fn_error(value: ServerFnErrorErr) -> Self {\n        match value {\n            ServerFnErrorErr::Registration(value) => {\n                ServerFnError::Registration(value)\n            }\n            ServerFnErrorErr::Request(value) => ServerFnError::Request(value),\n            ServerFnErrorErr::ServerError(value) => {\n                ServerFnError::ServerError(value)\n            }\n            ServerFnErrorErr::MiddlewareError(value) => {\n                ServerFnError::MiddlewareError(value)\n            }\n            ServerFnErrorErr::Deserialization(value) => {\n                ServerFnError::Deserialization(value)\n            }\n            ServerFnErrorErr::Serialization(value) => {\n                ServerFnError::Serialization(value)\n            }\n            ServerFnErrorErr::Args(value) => ServerFnError::Args(value),\n            ServerFnErrorErr::MissingArg(value) => {\n                ServerFnError::MissingArg(value)\n            }\n            ServerFnErrorErr::Response(value) => ServerFnError::Response(value),\n            ServerFnErrorErr::UnsupportedRequestMethod(value) => {\n                ServerFnError::Request(value)\n            }\n        }\n    }\n}\n\nimpl<E> std::error::Error for ServerFnError<E>\nwhere\n    E: std::error::Error + 'static,\n    ServerFnError<E>: std::fmt::Display,\n{\n    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n        match self {\n            ServerFnError::WrappedServerError(e) => Some(e),\n            _ => None,\n        }\n    }\n}\n\n/// Type for errors that can occur when using server functions. If you need to return a custom error type from a server function, implement `FromServerFnError` for your custom error type.\n#[derive(\n    thiserror::Error, Debug, Clone, PartialEq, Eq, Serialize, Deserialize,\n)]\n#[cfg_attr(\n    feature = \"rkyv\",\n    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)\n)]\npub enum ServerFnErrorErr {\n    /// Error while trying to register the server function (only occurs in case of poisoned RwLock).\n    #[error(\"error while trying to register the server function: {0}\")]\n    Registration(String),\n    /// Occurs on the client if trying to use an unsupported `HTTP` method when building a request.\n    #[error(\"error trying to build `HTTP` method request: {0}\")]\n    UnsupportedRequestMethod(String),\n    /// Occurs on the client if there is a network error while trying to run function on server.\n    #[error(\"error reaching server to call server function: {0}\")]\n    Request(String),\n    /// Occurs when there is an error while actually running the function on the server.\n    #[error(\"error running server function: {0}\")]\n    ServerError(String),\n    /// Occurs when there is an error while actually running the middleware on the server.\n    #[error(\"error running middleware: {0}\")]\n    MiddlewareError(String),\n    /// Occurs on the client if there is an error deserializing the server's response.\n    #[error(\"error deserializing server function results: {0}\")]\n    Deserialization(String),\n    /// Occurs on the client if there is an error serializing the server function arguments.\n    #[error(\"error serializing server function arguments: {0}\")]\n    Serialization(String),\n    /// Occurs on the server if there is an error deserializing one of the arguments that's been sent.\n    #[error(\"error deserializing server function arguments: {0}\")]\n    Args(String),\n    /// Occurs on the server if there's a missing argument.\n    #[error(\"missing argument {0}\")]\n    MissingArg(String),\n    /// Occurs on the server if there is an error creating an HTTP response.\n    #[error(\"error creating response {0}\")]\n    Response(String),\n}\n\n/// Associates a particular server function error with the server function\n/// found at a particular path.\n///\n/// This can be used to pass an error from the server back to the client\n/// without JavaScript/WASM supported, by encoding it in the URL as a query string.\n/// This is useful for progressive enhancement.\n#[derive(Debug)]\npub struct ServerFnUrlError<E> {\n    path: String,\n    error: E,\n}\n\nimpl<E: FromServerFnError> ServerFnUrlError<E> {\n    /// Creates a new structure associating the server function at some path\n    /// with a particular error.\n    pub fn new(path: impl Display, error: E) -> Self {\n        Self {\n            path: path.to_string(),\n            error,\n        }\n    }\n\n    /// The error itself.\n    pub fn error(&self) -> &E {\n        &self.error\n    }\n\n    /// The path of the server function that generated this error.\n    pub fn path(&self) -> &str {\n        &self.path\n    }\n\n    /// Adds an encoded form of this server function error to the given base URL.\n    pub fn to_url(&self, base: &str) -> Result<Url, url::ParseError> {\n        let mut url = Url::parse(base)?;\n        url.query_pairs_mut()\n            .append_pair(\"__path\", &self.path)\n            .append_pair(\"__err\", &URL_SAFE.encode(self.error.ser()));\n        Ok(url)\n    }\n\n    /// Replaces any ServerFnUrlError info from the URL in the given string\n    /// with the serialized success value given.\n    pub fn strip_error_info(path: &mut String) {\n        if let Ok(mut url) = Url::parse(&*path) {\n            // NOTE: This is gross, but the Serializer you get from\n            // .query_pairs_mut() isn't an Iterator so you can't just .retain().\n            let pairs_previously = url\n                .query_pairs()\n                .map(|(k, v)| (k.to_string(), v.to_string()))\n                .collect::<Vec<_>>();\n            let mut pairs = url.query_pairs_mut();\n            pairs.clear();\n            for (key, value) in pairs_previously\n                .into_iter()\n                .filter(|(key, _)| key != \"__path\" && key != \"__err\")\n            {\n                pairs.append_pair(&key, &value);\n            }\n            drop(pairs);\n            *path = url.to_string();\n        }\n    }\n\n    /// Decodes an error from a URL.\n    pub fn decode_err(err: &str) -> E {\n        let decoded = match URL_SAFE.decode(err) {\n            Ok(decoded) => decoded,\n            Err(err) => {\n                return ServerFnErrorErr::Deserialization(err.to_string())\n                    .into_app_error();\n            }\n        };\n        E::de(decoded.into())\n    }\n}\n\nimpl<E> From<ServerFnUrlError<E>> for ServerFnError<E> {\n    fn from(error: ServerFnUrlError<E>) -> Self {\n        error.error.into()\n    }\n}\n\nimpl<E> From<ServerFnUrlError<ServerFnError<E>>> for ServerFnError<E> {\n    fn from(error: ServerFnUrlError<ServerFnError<E>>) -> Self {\n        error.error\n    }\n}\n\n#[derive(Debug, thiserror::Error)]\n#[doc(hidden)]\n/// Only used instantly only when a framework needs E: Error.\npub struct ServerFnErrorWrapper<E: FromServerFnError>(pub E);\n\nimpl<E: FromServerFnError> Display for ServerFnErrorWrapper<E> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(\n            f,\n            \"{}\",\n            <E::Encoder as FormatType>::into_encoded_string(self.0.ser())\n        )\n    }\n}\n\nimpl<E: FromServerFnError> FromStr for ServerFnErrorWrapper<E> {\n    type Err = base64::DecodeError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let bytes =\n            <E::Encoder as FormatType>::from_encoded_string(s).map_err(|e| {\n                E::from_server_fn_error(ServerFnErrorErr::Deserialization(\n                    e.to_string(),\n                ))\n            });\n        let bytes = match bytes {\n            Ok(bytes) => bytes,\n            Err(err) => return Ok(Self(err)),\n        };\n        let err = E::de(bytes);\n        Ok(Self(err))\n    }\n}\n\n/// A trait for types that can be returned from a server function.\npub trait FromServerFnError: std::fmt::Debug + Sized + 'static {\n    /// The encoding strategy used to serialize and deserialize this error type. Must implement the [`Encodes`](server_fn::Encodes) trait for references to the error type.\n    type Encoder: Encodes<Self> + Decodes<Self>;\n\n    /// Converts a [`ServerFnErrorErr`] into the application-specific custom error type.\n    fn from_server_fn_error(value: ServerFnErrorErr) -> Self;\n\n    /// Serializes the custom error type to bytes, according to the encoding given by `Self::Encoding`.\n    fn ser(&self) -> Bytes {\n        Self::Encoder::encode(self).unwrap_or_else(|e| {\n            Self::Encoder::encode(&Self::from_server_fn_error(\n                ServerFnErrorErr::Serialization(e.to_string()),\n            ))\n            .expect(\n                \"error serializing should success at least with the \\\n                 Serialization error\",\n            )\n        })\n    }\n\n    /// Deserializes the custom error type, according to the encoding given by `Self::Encoding`.\n    fn de(data: Bytes) -> Self {\n        Self::Encoder::decode(data).unwrap_or_else(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })\n    }\n}\n\n/// A helper trait for converting a [`ServerFnErrorErr`] into an application-specific custom error type that implements [`FromServerFnError`].\npub trait IntoAppError<E> {\n    /// Converts a [`ServerFnErrorErr`] into the application-specific custom error type.\n    fn into_app_error(self) -> E;\n}\n\nimpl<E> IntoAppError<E> for ServerFnErrorErr\nwhere\n    E: FromServerFnError,\n{\n    fn into_app_error(self) -> E {\n        E::from_server_fn_error(self)\n    }\n}\n\n#[doc(hidden)]\n#[rustversion::attr(\n    since(1.78),\n    diagnostic::on_unimplemented(\n        message = \"{Self} is not a `Result` or aliased `Result`. Server \\\n                   functions must return a `Result` or aliased `Result`.\",\n        label = \"Must return a `Result` or aliased `Result`.\",\n        note = \"If you are trying to return an alias of `Result`, you must \\\n                also implement `FromServerFnError` for the error type.\"\n    )\n)]\n/// A trait for extracting the error and ok types from a [`Result`]. This is used to allow alias types to be returned from server functions.\npub trait ServerFnMustReturnResult {\n    /// The error type of the [`Result`].\n    type Err;\n    /// The ok type of the [`Result`].\n    type Ok;\n}\n\n#[doc(hidden)]\nimpl<T, E> ServerFnMustReturnResult for Result<T, E> {\n    type Err = E;\n    type Ok = T;\n}\n\n#[test]\nfn assert_from_server_fn_error_impl() {\n    fn assert_impl<T: FromServerFnError>() {}\n\n    assert_impl::<ServerFnError>();\n}\n"
  },
  {
    "path": "server_fn/src/lib.rs",
    "content": "#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\n//! # Server Functions\n//!\n//! This package is based on a simple idea: sometimes it’s useful to write functions\n//! that will only run on the server, and call them from the client.\n//!\n//! If you’re creating anything beyond a toy app, you’ll need to do this all the time:\n//! reading from or writing to a database that only runs on the server, running expensive\n//! computations using libraries you don’t want to ship down to the client, accessing\n//! APIs that need to be called from the server rather than the client for CORS reasons\n//! or because you need a secret API key that’s stored on the server and definitely\n//! shouldn’t be shipped down to a user’s browser.\n//!\n//! Traditionally, this is done by separating your server and client code, and by setting\n//! up something like a REST API or GraphQL API to allow your client to fetch and mutate\n//! data on the server. This is fine, but it requires you to write and maintain your code\n//! in multiple separate places (client-side code for fetching, server-side functions to run),\n//! as well as creating a third thing to manage, which is the API contract between the two.\n//!\n//! This package provides two simple primitives that allow you instead to write co-located,\n//! isomorphic server functions. (*Co-located* means you can write them in your app code so\n//! that they are “located alongside” the client code that calls them, rather than separating\n//! the client and server sides. *Isomorphic* means you can call them from the client as if\n//! you were simply calling a function; the function call has the “same shape” on the client\n//! as it does on the server.)\n//!\n//! ### `#[server]`\n//!\n//! The [`#[server]`](../leptos/attr.server.html) macro allows you to annotate a function to\n//! indicate that it should only run on the server (i.e., when you have an `ssr` feature in your\n//! crate that is enabled).\n//!\n//! **Important**: Before calling a server function on a non-web platform, you must set the server URL by calling\n//! [`set_server_url`](crate::client::set_server_url).\n//!\n//! ```rust,ignore\n//! #[server]\n//! async fn read_posts(how_many: usize, query: String) -> Result<Vec<Posts>, ServerFnError> {\n//!   // do some server-only work here to access the database\n//!   let posts = ...;\n//!   Ok(posts)\n//! }\n//!\n//! // call the function\n//! # #[tokio::main]\n//! # async fn main() {\n//! async {\n//!   let posts = read_posts(3, \"my search\".to_string()).await;\n//!   log::debug!(\"posts = {posts:#?}\");\n//! }\n//! # }\n//! ```\n//!\n//! If you call this function from the client, it will serialize the function arguments and `POST`\n//! them to the server as if they were the URL-encoded inputs in `<form method=\"post\">`.\n//!\n//! Here’s what you need to remember:\n//! - **Server functions must be `async`.** Even if the work being done inside the function body\n//!   can run synchronously on the server, from the client’s perspective it involves an asynchronous\n//!   function call.\n//! - **Server functions must return `Result<T, ServerFnError>`.** Even if the work being done\n//!   inside the function body can’t fail, the processes of serialization/deserialization and the\n//!   network call are fallible. [`ServerFnError`] can receive generic errors.\n//! - **Server functions are part of the public API of your application.** A server function is an\n//!   ad hoc HTTP API endpoint, not a magic formula. Any server function can be accessed by any HTTP\n//!   client. You should take care to sanitize any data being returned from the function to ensure it\n//!   does not leak data that should exist only on the server.\n//! - **Server functions can’t be generic.** Because each server function creates a separate API endpoint,\n//!   it is difficult to monomorphize. As a result, server functions cannot be generic (for now?) If you need to use\n//!   a generic function, you can define a generic inner function called by multiple concrete server functions.\n//! - **Arguments and return types must be serializable.** We support a variety of different encodings,\n//!   but one way or another arguments need to be serialized to be sent to the server and deserialized\n//!   on the server, and the return type must be serialized on the server and deserialized on the client.\n//!   This means that the set of valid server function argument and return types is a subset of all\n//!   possible Rust argument and return types. (i.e., server functions are strictly more limited than\n//!   ordinary functions.)\n//!\n//! ## Server Function Encodings\n//!\n//! Server functions are designed to allow a flexible combination of input and output encodings, the set\n//! of which can be found in the [`codec`] module.\n//!\n//! Calling and handling server functions is done through the [`Protocol`] trait, which is implemented\n//! for the [`Http`] and [`Websocket`] protocols. Most server functions will use the [`Http`] protocol.\n//!\n//! When using the [`Http`] protocol, the serialization/deserialization process for server functions\n//! consists of a series of steps, each of which is represented by a different trait:\n//! 1. [`IntoReq`]: The client serializes the [`ServerFn`] argument type into an HTTP request.\n//! 2. The [`Client`] sends the request to the server.\n//! 3. [`FromReq`]: The server deserializes the HTTP request back into the [`ServerFn`] type.\n//! 4. The server calls calls [`ServerFn::run_body`] on the data.\n//! 5. [`IntoRes`]: The server serializes the [`ServerFn::Output`] type into an HTTP response.\n//! 6. The server integration applies any middleware from [`ServerFn::middlewares`] and responds to the request.\n//! 7. [`FromRes`]: The client deserializes the response back into the [`ServerFn::Output`] type.\n//!\n//! [server]: ../leptos/attr.server.html\n//! [`serde_qs`]: <https://docs.rs/serde_qs/latest/serde_qs/>\n//! [`cbor`]: <https://docs.rs/cbor/latest/cbor/>\n\n/// Implementations of the client side of the server function call.\npub mod client;\n\n/// Implementations of the server side of the server function call.\npub mod server;\n\n/// Encodings for arguments and results.\npub mod codec;\n\n#[macro_use]\n/// Error types and utilities.\npub mod error;\n/// Types to add server middleware to a server function.\npub mod middleware;\n/// Utilities to allow client-side redirects.\npub mod redirect;\n/// Types and traits for  for HTTP requests.\npub mod request;\n/// Types and traits for HTTP responses.\npub mod response;\n\n#[cfg(feature = \"actix-no-default\")]\n#[doc(hidden)]\npub use ::actix_web as actix_export;\n#[cfg(feature = \"axum-no-default\")]\n#[doc(hidden)]\npub use ::axum as axum_export;\n#[cfg(feature = \"generic\")]\n#[doc(hidden)]\npub use ::bytes as bytes_export;\n#[cfg(feature = \"generic\")]\n#[doc(hidden)]\npub use ::http as http_export;\nuse base64::{engine::general_purpose::STANDARD_NO_PAD, DecodeError, Engine};\n#[cfg(feature = \"bitcode\")]\npub use bitcode;\n// re-exported to make it possible to implement a custom Client without adding a separate\n// dependency on `bytes`\npub use bytes::Bytes;\nuse bytes::{BufMut, BytesMut};\nuse client::Client;\nuse codec::{Encoding, FromReq, FromRes, IntoReq, IntoRes};\n#[doc(hidden)]\npub use const_format;\n#[doc(hidden)]\npub use const_str;\npub use error::ServerFnError;\n#[cfg(feature = \"form-redirects\")]\nuse error::ServerFnUrlError;\nuse error::{FromServerFnError, ServerFnErrorErr};\nuse futures::{pin_mut, SinkExt, Stream, StreamExt};\nuse http::Method;\nuse middleware::{BoxedService, Layer, Service};\nuse redirect::call_redirect_hook;\nuse request::Req;\nuse response::{ClientRes, Res, TryRes};\n#[cfg(feature = \"rkyv\")]\npub use rkyv;\n#[doc(hidden)]\npub use serde;\n#[doc(hidden)]\n#[cfg(feature = \"serde-lite\")]\npub use serde_lite;\nuse server::Server;\nuse std::{\n    collections::HashMap,\n    fmt::{Debug, Display},\n    future::Future,\n    marker::PhantomData,\n    ops::{Deref, DerefMut},\n    pin::Pin,\n    sync::{Arc, LazyLock, RwLock},\n};\n#[doc(hidden)]\npub use xxhash_rust;\n\ntype ServerFnServerRequest<Fn> = <<Fn as ServerFn>::Server as crate::Server<\n    <Fn as ServerFn>::Error,\n    <Fn as ServerFn>::InputStreamError,\n    <Fn as ServerFn>::OutputStreamError,\n>>::Request;\ntype ServerFnServerResponse<Fn> = <<Fn as ServerFn>::Server as crate::Server<\n    <Fn as ServerFn>::Error,\n    <Fn as ServerFn>::InputStreamError,\n    <Fn as ServerFn>::OutputStreamError,\n>>::Response;\n\n/// Defines a function that runs only on the server, but can be called from the server or the client.\n///\n/// The type for which `ServerFn` is implemented is actually the type of the arguments to the function,\n/// while the function body itself is implemented in [`run_body`](ServerFn::run_body).\n///\n/// This means that `Self` here is usually a struct, in which each field is an argument to the function.\n/// In other words,\n/// ```rust,ignore\n/// #[server]\n/// pub async fn my_function(foo: String, bar: usize) -> Result<usize, ServerFnError> {\n///     Ok(foo.len() + bar)\n/// }\n/// ```\n/// should expand to\n/// ```rust,ignore\n/// #[derive(Serialize, Deserialize)]\n/// pub struct MyFunction {\n///     foo: String,\n///     bar: usize\n/// }\n///\n/// impl ServerFn for MyFunction {\n///     async fn run_body() -> Result<usize, ServerFnError> {\n///         Ok(foo.len() + bar)\n///     }\n///\n///     // etc.\n/// }\n/// ```\npub trait ServerFn: Send + Sized {\n    /// A unique path for the server function’s API endpoint, relative to the host, including its prefix.\n    const PATH: &'static str;\n\n    /// The type of the HTTP client that will send the request from the client side.\n    ///\n    /// For example, this might be `gloo-net` in the browser, or `reqwest` for a desktop app.\n    type Client: Client<\n        Self::Error,\n        Self::InputStreamError,\n        Self::OutputStreamError,\n    >;\n\n    /// The type of the HTTP server that will send the response from the server side.\n    ///\n    /// For example, this might be `axum` or `actix-web`.\n    type Server: Server<\n        Self::Error,\n        Self::InputStreamError,\n        Self::OutputStreamError,\n    >;\n\n    /// The protocol the server function uses to communicate with the client.\n    type Protocol: Protocol<\n        Self,\n        Self::Output,\n        Self::Client,\n        Self::Server,\n        Self::Error,\n        Self::InputStreamError,\n        Self::OutputStreamError,\n    >;\n\n    /// The return type of the server function.\n    ///\n    /// This needs to be converted into `ServerResponse` on the server side, and converted\n    /// *from* `ClientResponse` when received by the client.\n    type Output: Send;\n\n    /// The type of error in the server function return.\n    /// Typically [`ServerFnError`], but allowed to be any type that implements [`FromServerFnError`].\n    type Error: FromServerFnError + Send + Sync;\n    /// The type of error in the server function for stream items sent from the client to the server.\n    /// Typically [`ServerFnError`], but allowed to be any type that implements [`FromServerFnError`].\n    type InputStreamError: FromServerFnError + Send + Sync;\n    /// The type of error in the server function for stream items sent from the server to the client.\n    /// Typically [`ServerFnError`], but allowed to be any type that implements [`FromServerFnError`].\n    type OutputStreamError: FromServerFnError + Send + Sync;\n\n    /// Returns [`Self::PATH`].\n    fn url() -> &'static str {\n        Self::PATH\n    }\n\n    /// Middleware that should be applied to this server function.\n    fn middlewares() -> Vec<\n        Arc<\n            dyn Layer<\n                ServerFnServerRequest<Self>,\n                ServerFnServerResponse<Self>,\n            >,\n        >,\n    > {\n        Vec::new()\n    }\n\n    /// The body of the server function. This will only run on the server.\n    fn run_body(\n        self,\n    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send;\n\n    #[doc(hidden)]\n    fn run_on_server(\n        req: ServerFnServerRequest<Self>,\n    ) -> impl Future<Output = ServerFnServerResponse<Self>> + Send {\n        // Server functions can either be called by a real Client,\n        // or directly by an HTML <form>. If they're accessed by a <form>, default to\n        // redirecting back to the Referer.\n        #[cfg(feature = \"form-redirects\")]\n        let accepts_html = req\n            .accepts()\n            .map(|n| n.contains(\"text/html\"))\n            .unwrap_or(false);\n        #[cfg(feature = \"form-redirects\")]\n        let mut referer = req.referer().as_deref().map(ToOwned::to_owned);\n\n        async move {\n            #[allow(unused_variables, unused_mut)]\n            // used in form redirects feature\n            let (mut res, err) =\n                Self::Protocol::run_server(req, Self::run_body)\n                    .await\n                    .map(|res| (res, None))\n                    .unwrap_or_else(|e| {\n                        let mut response =\n                            <<Self as ServerFn>::Server as crate::Server<\n                                Self::Error,\n                                Self::InputStreamError,\n                                Self::OutputStreamError,\n                            >>::Response::error_response(\n                                Self::PATH, e.ser()\n                            );\n                        let content_type =\n                    <Self::Error as FromServerFnError>::Encoder::CONTENT_TYPE;\n                        response.content_type(content_type);\n                        (response, Some(e))\n                    });\n\n            // if it accepts HTML, we'll redirect to the Referer\n            #[cfg(feature = \"form-redirects\")]\n            if accepts_html {\n                // if it had an error, encode that error in the URL\n                if let Some(err) = err {\n                    if let Ok(url) = ServerFnUrlError::new(Self::PATH, err)\n                        .to_url(referer.as_deref().unwrap_or(\"/\"))\n                    {\n                        referer = Some(url.to_string());\n                    }\n                }\n                // otherwise, strip error info from referer URL, as that means it's from a previous\n                // call\n                else if let Some(referer) = referer.as_mut() {\n                    ServerFnUrlError::<Self::Error>::strip_error_info(referer)\n                }\n\n                // set the status code and Location header\n                res.redirect(referer.as_deref().unwrap_or(\"/\"));\n            }\n\n            res\n        }\n    }\n\n    #[doc(hidden)]\n    fn run_on_client(\n        self,\n    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send {\n        async move { Self::Protocol::run_client(Self::PATH, self).await }\n    }\n}\n\n/// The protocol that a server function uses to communicate with the client. This trait handles\n/// the server and client side of running a server function. It is implemented for the [`Http`] and\n/// [`Websocket`] protocols and can be used to implement custom protocols.\npub trait Protocol<\n    Input,\n    Output,\n    Client,\n    Server,\n    Error,\n    InputStreamError = Error,\n    OutputStreamError = Error,\n> where\n    Server: crate::Server<Error, InputStreamError, OutputStreamError>,\n    Client: crate::Client<Error, InputStreamError, OutputStreamError>,\n{\n    /// The HTTP method used for requests.\n    const METHOD: Method;\n\n    /// Run the server function on the server. The implementation should handle deserializing the\n    /// input, running the server function, and serializing the output.\n    fn run_server<F, Fut>(\n        request: Server::Request,\n        server_fn: F,\n    ) -> impl Future<Output = Result<Server::Response, Error>> + Send\n    where\n        F: Fn(Input) -> Fut + Send,\n        Fut: Future<Output = Result<Output, Error>> + Send;\n\n    /// Run the server function on the client. The implementation should handle serializing the\n    /// input, sending the request, and deserializing the output.\n    fn run_client(\n        path: &str,\n        input: Input,\n    ) -> impl Future<Output = Result<Output, Error>> + Send;\n}\n\n/// The http protocol with specific input and output encodings for the request and response. This is\n/// the default protocol server functions use if no override is set in the server function macro\n///\n/// The http protocol accepts two generic argument that define how the input and output for a server\n/// function are turned into HTTP requests and responses. For example, [`Http<GetUrl, Json>`] will\n/// accept a Url encoded Get request and return a JSON post response.\n///\n/// # Example\n///\n/// ```rust, no_run\n/// # use server_fn_macro_default::server;\n/// use serde::{Serialize, Deserialize};\n/// use server_fn::{Http, ServerFnError, codec::{Json, GetUrl}};\n///\n/// #[derive(Debug, Clone, Serialize, Deserialize)]\n/// pub struct Message {\n///     user: String,\n///     message: String,\n/// }\n///\n/// // The http protocol can be used on any server function that accepts and returns arguments that implement\n/// // the [`IntoReq`] and [`FromRes`] traits.\n/// //\n/// // In this case, the input and output encodings are [`GetUrl`] and [`Json`], respectively which requires\n/// // the items to implement [`IntoReq<GetUrl, ...>`] and [`FromRes<Json, ...>`]. Both of those implementations\n/// // require the items to implement [`Serialize`] and [`Deserialize`].\n/// # #[cfg(feature = \"browser\")] {\n/// #[server(protocol = Http<GetUrl, Json>)]\n/// async fn echo_http(\n///     input: Message,\n/// ) -> Result<Message, ServerFnError> {\n///     Ok(input)\n/// }\n/// # }\n/// ```\npub struct Http<InputProtocol, OutputProtocol>(\n    PhantomData<(InputProtocol, OutputProtocol)>,\n);\n\nimpl<InputProtocol, OutputProtocol, Input, Output, Client, Server, E>\n    Protocol<Input, Output, Client, Server, E>\n    for Http<InputProtocol, OutputProtocol>\nwhere\n    Input: IntoReq<InputProtocol, Client::Request, E>\n        + FromReq<InputProtocol, Server::Request, E>\n        + Send,\n    Output: IntoRes<OutputProtocol, Server::Response, E>\n        + FromRes<OutputProtocol, Client::Response, E>\n        + Send,\n    E: FromServerFnError,\n    InputProtocol: Encoding,\n    OutputProtocol: Encoding,\n    Client: crate::Client<E>,\n    Server: crate::Server<E>,\n{\n    const METHOD: Method = InputProtocol::METHOD;\n\n    async fn run_server<F, Fut>(\n        request: Server::Request,\n        server_fn: F,\n    ) -> Result<Server::Response, E>\n    where\n        F: Fn(Input) -> Fut + Send,\n        Fut: Future<Output = Result<Output, E>> + Send,\n    {\n        let input = Input::from_req(request).await?;\n\n        let output = server_fn(input).await?;\n\n        let response = Output::into_res(output).await?;\n\n        Ok(response)\n    }\n\n    async fn run_client(path: &str, input: Input) -> Result<Output, E>\n    where\n        Client: crate::Client<E>,\n    {\n        // create and send request on client\n        let req = input.into_req(path, OutputProtocol::CONTENT_TYPE)?;\n        let res = Client::send(req).await?;\n\n        let status = res.status();\n        let location = res.location();\n        let has_redirect_header = res.has_redirect();\n\n        // if it returns an error status, deserialize the error using the error's decoder.\n        let res = if (400..=599).contains(&status) {\n            Err(E::de(res.try_into_bytes().await?))\n        } else {\n            // otherwise, deserialize the body as is\n            let output = Output::from_res(res).await?;\n            Ok(output)\n        }?;\n\n        // if redirected, call the redirect hook (if that's been set)\n        if (300..=399).contains(&status) || has_redirect_header {\n            call_redirect_hook(&location);\n        }\n        Ok(res)\n    }\n}\n\n/// The websocket protocol that encodes the input and output streams using a websocket connection.\n///\n/// The websocket protocol accepts two generic argument that define the input and output serialization\n/// formats. For example, [`Websocket<CborEncoding, JsonEncoding>`] would accept a stream of Cbor-encoded messages\n/// and return a stream of JSON-encoded messages.\n///\n/// # Example\n///\n/// ```rust, no_run\n/// # use server_fn_macro_default::server;\n/// # #[cfg(feature = \"browser\")] {\n/// use server_fn::{ServerFnError, BoxedStream, Websocket, codec::JsonEncoding};\n/// use serde::{Serialize, Deserialize};\n///\n/// #[derive(Clone, Serialize, Deserialize)]\n/// pub struct Message {\n///     user: String,\n///     message: String,\n/// }\n/// // The websocket protocol can be used on any server function that accepts and returns a [`BoxedStream`]\n/// // with items that can be encoded by the input and output encoding generics.\n/// //\n/// // In this case, the input and output encodings are [`Json`] and [`Json`], respectively which requires\n/// // the items to implement [`Serialize`] and [`Deserialize`].\n/// #[server(protocol = Websocket<JsonEncoding, JsonEncoding>)]\n/// async fn echo_websocket(\n///     input: BoxedStream<Message, ServerFnError>,\n/// ) -> Result<BoxedStream<Message, ServerFnError>, ServerFnError> {\n///     Ok(input.into())\n/// }\n/// # }\n/// ```\npub struct Websocket<InputEncoding, OutputEncoding>(\n    PhantomData<(InputEncoding, OutputEncoding)>,\n);\n\n/// A boxed stream type that can be used with the websocket protocol.\n///\n/// You can easily convert any static type that implement [`futures::Stream`] into a [`BoxedStream`]\n/// with the [`From`] trait.\n///\n/// # Example\n///\n/// ```rust, no_run\n/// use futures::StreamExt;\n/// use server_fn::{BoxedStream, ServerFnError};\n///\n/// let stream: BoxedStream<_, ServerFnError> =\n///     futures::stream::iter(0..10).map(Result::Ok).into();\n/// ```\npub struct BoxedStream<T, E> {\n    stream: Pin<Box<dyn Stream<Item = Result<T, E>> + Send>>,\n}\n\nimpl<T, E> From<BoxedStream<T, E>>\n    for Pin<Box<dyn Stream<Item = Result<T, E>> + Send>>\n{\n    fn from(val: BoxedStream<T, E>) -> Self {\n        val.stream\n    }\n}\n\nimpl<T, E> Deref for BoxedStream<T, E> {\n    type Target = Pin<Box<dyn Stream<Item = Result<T, E>> + Send>>;\n    fn deref(&self) -> &Self::Target {\n        &self.stream\n    }\n}\n\nimpl<T, E> DerefMut for BoxedStream<T, E> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.stream\n    }\n}\n\nimpl<T, E> Debug for BoxedStream<T, E> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"BoxedStream\").finish()\n    }\n}\n\nimpl<T, E, S> From<S> for BoxedStream<T, E>\nwhere\n    S: Stream<Item = Result<T, E>> + Send + 'static,\n{\n    fn from(stream: S) -> Self {\n        BoxedStream {\n            stream: Box::pin(stream),\n        }\n    }\n}\n\nimpl<\n        Input,\n        InputItem,\n        OutputItem,\n        InputEncoding,\n        OutputEncoding,\n        Client,\n        Server,\n        Error,\n        InputStreamError,\n        OutputStreamError,\n    >\n    Protocol<\n        Input,\n        BoxedStream<OutputItem, OutputStreamError>,\n        Client,\n        Server,\n        Error,\n        InputStreamError,\n        OutputStreamError,\n    > for Websocket<InputEncoding, OutputEncoding>\nwhere\n    Input: Deref<Target = BoxedStream<InputItem, InputStreamError>>\n        + Into<BoxedStream<InputItem, InputStreamError>>\n        + From<BoxedStream<InputItem, InputStreamError>>,\n    InputEncoding: Encodes<InputItem> + Decodes<InputItem>,\n    OutputEncoding: Encodes<OutputItem> + Decodes<OutputItem>,\n    InputStreamError: FromServerFnError + Send,\n    OutputStreamError: FromServerFnError + Send,\n    Error: FromServerFnError + Send,\n    Server: crate::Server<Error, InputStreamError, OutputStreamError>,\n    Client: crate::Client<Error, InputStreamError, OutputStreamError>,\n    OutputItem: Send + 'static,\n    InputItem: Send + 'static,\n{\n    const METHOD: Method = Method::GET;\n\n    async fn run_server<F, Fut>(\n        request: Server::Request,\n        server_fn: F,\n    ) -> Result<Server::Response, Error>\n    where\n        F: Fn(Input) -> Fut + Send,\n        Fut: Future<\n                Output = Result<\n                    BoxedStream<OutputItem, OutputStreamError>,\n                    Error,\n                >,\n            > + Send,\n    {\n        let (request_bytes, response_stream, response) =\n            request.try_into_websocket().await?;\n        let input = request_bytes.map(|request_bytes| {\n            let request_bytes = request_bytes\n                .map(|bytes| deserialize_result::<InputStreamError>(bytes))\n                .unwrap_or_else(Err);\n            match request_bytes {\n                Ok(request_bytes) => InputEncoding::decode(request_bytes)\n                    .map_err(|e| {\n                        InputStreamError::from_server_fn_error(\n                            ServerFnErrorErr::Deserialization(e.to_string()),\n                        )\n                    }),\n                Err(err) => Err(InputStreamError::de(err)),\n            }\n        });\n        let boxed = Box::pin(input)\n            as Pin<\n                Box<\n                    dyn Stream<Item = Result<InputItem, InputStreamError>>\n                        + Send,\n                >,\n            >;\n        let input = BoxedStream { stream: boxed };\n\n        let output = server_fn(input.into()).await?;\n\n        let output = output.stream.map(|output| {\n            let result = match output {\n                Ok(output) => OutputEncoding::encode(&output).map_err(|e| {\n                    OutputStreamError::from_server_fn_error(\n                        ServerFnErrorErr::Serialization(e.to_string()),\n                    )\n                    .ser()\n                }),\n                Err(err) => Err(err.ser()),\n            };\n            serialize_result(result)\n        });\n\n        Server::spawn(async move {\n            pin_mut!(response_stream);\n            pin_mut!(output);\n            while let Some(output) = output.next().await {\n                if response_stream.send(output).await.is_err() {\n                    break;\n                }\n            }\n        })?;\n\n        Ok(response)\n    }\n\n    fn run_client(\n        path: &str,\n        input: Input,\n    ) -> impl Future<\n        Output = Result<BoxedStream<OutputItem, OutputStreamError>, Error>,\n    > + Send {\n        let input = input.into();\n\n        async move {\n            let (stream, sink) = Client::open_websocket(path).await?;\n\n            // Forward the input stream to the websocket\n            Client::spawn(async move {\n                pin_mut!(input);\n                pin_mut!(sink);\n                while let Some(input) = input.stream.next().await {\n                    let result = match input {\n                        Ok(input) => {\n                            InputEncoding::encode(&input).map_err(|e| {\n                                InputStreamError::from_server_fn_error(\n                                    ServerFnErrorErr::Serialization(\n                                        e.to_string(),\n                                    ),\n                                )\n                                .ser()\n                            })\n                        }\n                        Err(err) => Err(err.ser()),\n                    };\n                    let result = serialize_result(result);\n                    if sink.send(result).await.is_err() {\n                        break;\n                    }\n                }\n            });\n\n            // Return the output stream\n            let stream = stream.map(|request_bytes| {\n                let request_bytes = request_bytes\n                    .map(|bytes| deserialize_result::<OutputStreamError>(bytes))\n                    .unwrap_or_else(Err);\n                match request_bytes {\n                    Ok(request_bytes) => OutputEncoding::decode(request_bytes)\n                        .map_err(|e| {\n                            OutputStreamError::from_server_fn_error(\n                                ServerFnErrorErr::Deserialization(\n                                    e.to_string(),\n                                ),\n                            )\n                        }),\n                    Err(err) => Err(OutputStreamError::de(err)),\n                }\n            });\n            let boxed = Box::pin(stream)\n                as Pin<\n                    Box<\n                        dyn Stream<Item = Result<OutputItem, OutputStreamError>>\n                            + Send,\n                    >,\n                >;\n            let output = BoxedStream { stream: boxed };\n            Ok(output)\n        }\n    }\n}\n\n// Serializes a Result<Bytes, Bytes> into a single Bytes instance.\n// Format: [tag: u8][content: Bytes]\n// - Tag 0: Ok variant\n// - Tag 1: Err variant\nfn serialize_result(result: Result<Bytes, Bytes>) -> Bytes {\n    match result {\n        Ok(bytes) => {\n            let mut buf = BytesMut::with_capacity(1 + bytes.len());\n            buf.put_u8(0); // Tag for Ok variant\n            buf.extend_from_slice(&bytes);\n            buf.freeze()\n        }\n        Err(bytes) => {\n            let mut buf = BytesMut::with_capacity(1 + bytes.len());\n            buf.put_u8(1); // Tag for Err variant\n            buf.extend_from_slice(&bytes);\n            buf.freeze()\n        }\n    }\n}\n\n// Deserializes a Bytes instance back into a Result<Bytes, Bytes>.\nfn deserialize_result<E: FromServerFnError>(\n    bytes: Bytes,\n) -> Result<Bytes, Bytes> {\n    if bytes.is_empty() {\n        return Err(E::from_server_fn_error(\n            ServerFnErrorErr::Deserialization(\"Data is empty\".into()),\n        )\n        .ser());\n    }\n\n    let tag = bytes[0];\n    let content = bytes.slice(1..);\n\n    match tag {\n        0 => Ok(content),\n        1 => Err(content),\n        _ => Err(E::from_server_fn_error(ServerFnErrorErr::Deserialization(\n            \"Invalid data tag\".into(),\n        ))\n        .ser()), // Invalid tag\n    }\n}\n\n/// Encode format type\npub enum Format {\n    /// Binary representation\n    Binary,\n    /// utf-8 compatible text representation\n    Text,\n}\n/// A trait for types with an associated content type.\npub trait ContentType {\n    /// The MIME type of the data.\n    const CONTENT_TYPE: &'static str;\n}\n\n/// Data format representation\npub trait FormatType {\n    /// The representation type\n    const FORMAT_TYPE: Format;\n\n    /// Encodes data into a string.\n    fn into_encoded_string(bytes: Bytes) -> String {\n        match Self::FORMAT_TYPE {\n            Format::Binary => STANDARD_NO_PAD.encode(bytes),\n            Format::Text => String::from_utf8(bytes.into())\n                .expect(\"Valid text format type with utf-8 comptabile string\"),\n        }\n    }\n\n    /// Decodes string to bytes\n    fn from_encoded_string(data: &str) -> Result<Bytes, DecodeError> {\n        match Self::FORMAT_TYPE {\n            Format::Binary => {\n                STANDARD_NO_PAD.decode(data).map(|data| data.into())\n            }\n            Format::Text => Ok(Bytes::copy_from_slice(data.as_bytes())),\n        }\n    }\n}\n\n/// A trait for types that can be encoded into a bytes for a request body.\npub trait Encodes<T>: ContentType + FormatType {\n    /// The error type that can be returned if the encoding fails.\n    type Error: Display + Debug;\n\n    /// Encodes the given value into a bytes.\n    fn encode(output: &T) -> Result<Bytes, Self::Error>;\n}\n\n/// A trait for types that can be decoded from a bytes for a response body.\npub trait Decodes<T> {\n    /// The error type that can be returned if the decoding fails.\n    type Error: Display;\n\n    /// Decodes the given bytes into a value.\n    fn decode(bytes: Bytes) -> Result<T, Self::Error>;\n}\n\n#[cfg(feature = \"ssr\")]\n#[doc(hidden)]\npub use inventory;\n\n/// Uses the `inventory` crate to initialize a map between paths and server functions.\n#[macro_export]\nmacro_rules! initialize_server_fn_map {\n    ($req:ty, $res:ty) => {\n        std::sync::LazyLock::new(|| {\n            std::sync::RwLock::new(\n                $crate::inventory::iter::<ServerFnTraitObj<$req, $res>>\n                    .into_iter()\n                    .map(|obj| {\n                        ((obj.path().to_string(), obj.method()), obj.clone())\n                    })\n                    .collect(),\n            )\n        })\n    };\n}\n\n/// A list of middlewares that can be applied to a server function.\npub type MiddlewareSet<Req, Res> = Vec<Arc<dyn Layer<Req, Res>>>;\n\n/// A trait object that allows multiple server functions that take the same\n/// request type and return the same response type to be gathered into a single\n/// collection.\npub struct ServerFnTraitObj<Req, Res> {\n    path: &'static str,\n    method: Method,\n    handler: fn(Req) -> Pin<Box<dyn Future<Output = Res> + Send>>,\n    middleware: fn() -> MiddlewareSet<Req, Res>,\n    ser: fn(ServerFnErrorErr) -> Bytes,\n}\n\nimpl<Req, Res> ServerFnTraitObj<Req, Res> {\n    /// Converts the relevant parts of a server function into a trait object.\n    pub const fn new<\n        S: ServerFn<\n            Server: crate::Server<\n                S::Error,\n                S::InputStreamError,\n                S::OutputStreamError,\n                Request = Req,\n                Response = Res,\n            >,\n        >,\n    >(\n        handler: fn(Req) -> Pin<Box<dyn Future<Output = Res> + Send>>,\n    ) -> Self\n    where\n        Req: crate::Req<\n                S::Error,\n                S::InputStreamError,\n                S::OutputStreamError,\n                WebsocketResponse = Res,\n            > + Send\n            + 'static,\n        Res: crate::TryRes<S::Error> + Send + 'static,\n    {\n        Self {\n            path: S::PATH,\n            method: S::Protocol::METHOD,\n            handler,\n            middleware: S::middlewares,\n            ser: |e| S::Error::from_server_fn_error(e).ser(),\n        }\n    }\n\n    /// The path of the server function.\n    pub fn path(&self) -> &'static str {\n        self.path\n    }\n\n    /// The HTTP method the server function expects.\n    pub fn method(&self) -> Method {\n        self.method.clone()\n    }\n\n    /// The handler for this server function.\n    pub fn handler(&self, req: Req) -> impl Future<Output = Res> + Send {\n        (self.handler)(req)\n    }\n\n    /// The set of middleware that should be applied to this function.\n    pub fn middleware(&self) -> MiddlewareSet<Req, Res> {\n        (self.middleware)()\n    }\n\n    /// Converts the server function into a boxed service.\n    pub fn boxed(self) -> BoxedService<Req, Res>\n    where\n        Self: Service<Req, Res>,\n        Req: 'static,\n        Res: 'static,\n    {\n        BoxedService::new(self.ser, self)\n    }\n}\n\nimpl<Req, Res> Service<Req, Res> for ServerFnTraitObj<Req, Res>\nwhere\n    Req: Send + 'static,\n    Res: 'static,\n{\n    fn run(\n        &mut self,\n        req: Req,\n        _ser: fn(ServerFnErrorErr) -> Bytes,\n    ) -> Pin<Box<dyn Future<Output = Res> + Send>> {\n        let handler = self.handler;\n        Box::pin(async move { handler(req).await })\n    }\n}\n\nimpl<Req, Res> Clone for ServerFnTraitObj<Req, Res> {\n    fn clone(&self) -> Self {\n        Self {\n            path: self.path,\n            method: self.method.clone(),\n            handler: self.handler,\n            middleware: self.middleware,\n            ser: self.ser,\n        }\n    }\n}\n\n#[allow(unused)] // used by server integrations\ntype LazyServerFnMap<Req, Res> =\n    LazyLock<RwLock<HashMap<(String, Method), ServerFnTraitObj<Req, Res>>>>;\n\n#[cfg(feature = \"ssr\")]\nimpl<Req: 'static, Res: 'static> inventory::Collect\n    for ServerFnTraitObj<Req, Res>\n{\n    #[inline]\n    fn registry() -> &'static inventory::Registry {\n        static REGISTRY: inventory::Registry = inventory::Registry::new();\n        &REGISTRY\n    }\n}\n\n/// Axum integration.\n#[cfg(feature = \"axum-no-default\")]\npub mod axum {\n    use crate::{\n        error::FromServerFnError, middleware::BoxedService, LazyServerFnMap,\n        Protocol, Server, ServerFn, ServerFnTraitObj,\n    };\n    use axum::body::Body;\n    use http::{Method, Request, Response, StatusCode};\n    use or_poisoned::OrPoisoned;\n    use std::future::Future;\n\n    static REGISTERED_SERVER_FUNCTIONS: LazyServerFnMap<\n        Request<Body>,\n        Response<Body>,\n    > = initialize_server_fn_map!(Request<Body>, Response<Body>);\n\n    /// The axum server function backend\n    pub struct AxumServerFnBackend;\n\n    impl<Error, InputStreamError, OutputStreamError>\n        Server<Error, InputStreamError, OutputStreamError>\n        for AxumServerFnBackend\n    where\n        Error: FromServerFnError + Send + Sync,\n        InputStreamError: FromServerFnError + Send + Sync,\n        OutputStreamError: FromServerFnError + Send + Sync,\n    {\n        type Request = Request<Body>;\n        type Response = Response<Body>;\n\n        #[allow(unused_variables)]\n        fn spawn(\n            future: impl Future<Output = ()> + Send + 'static,\n        ) -> Result<(), Error> {\n            #[cfg(feature = \"axum\")]\n            {\n                tokio::spawn(future);\n                Ok(())\n            }\n            #[cfg(not(feature = \"axum\"))]\n            {\n                Err(Error::from_server_fn_error(\n                    crate::error::ServerFnErrorErr::Request(\n                        \"No async runtime available. You need to either \\\n                         enable the full axum feature to pull in tokio, or \\\n                         implement the `Server` trait for your async runtime \\\n                         manually.\"\n                            .into(),\n                    ),\n                ))\n            }\n        }\n    }\n\n    /// Explicitly register a server function. This is only necessary if you are\n    /// running the server in a WASM environment (or a rare environment that the\n    /// `inventory` crate won't work in.).\n    pub fn register_explicit<T>()\n    where\n        T: ServerFn<\n                Server: crate::Server<\n                    T::Error,\n                    T::InputStreamError,\n                    T::OutputStreamError,\n                    Request = Request<Body>,\n                    Response = Response<Body>,\n                >,\n            > + 'static,\n    {\n        REGISTERED_SERVER_FUNCTIONS.write().or_poisoned().insert(\n            (T::PATH.into(), T::Protocol::METHOD),\n            ServerFnTraitObj::new::<T>(|req| Box::pin(T::run_on_server(req))),\n        );\n    }\n\n    /// The set of all registered server function paths.\n    pub fn server_fn_paths() -> impl Iterator<Item = (&'static str, Method)> {\n        let paths: Vec<_> = REGISTERED_SERVER_FUNCTIONS\n            .read()\n            .unwrap()\n            .values()\n            .map(|item| (item.path(), item.method()))\n            .collect();\n\n        paths.into_iter()\n    }\n\n    /// An Axum handler that responds to a server function request.\n    pub async fn handle_server_fn(req: Request<Body>) -> Response<Body> {\n        let path = req.uri().path();\n\n        if let Some(mut service) =\n            get_server_fn_service(path, req.method().clone())\n        {\n            service.run(req).await\n        } else {\n            Response::builder()\n                .status(StatusCode::BAD_REQUEST)\n                .body(Body::from(format!(\n                    \"Could not find a server function at the route {path}. \\\n                     \\n\\nIt's likely that either\\n 1. The API prefix you \\\n                     specify in the `#[server]` macro doesn't match the \\\n                     prefix at which your server function handler is mounted, \\\n                     or \\n2. You are on a platform that doesn't support \\\n                     automatic server function registration and you need to \\\n                     call ServerFn::register_explicit() on the server \\\n                     function type, somewhere in your `main` function.\",\n                )))\n                .unwrap()\n        }\n    }\n\n    /// Returns the server function at the given path as a service that can be modified.\n    pub fn get_server_fn_service(\n        path: &str,\n        method: Method,\n    ) -> Option<BoxedService<Request<Body>, Response<Body>>> {\n        let key = (path.into(), method);\n        REGISTERED_SERVER_FUNCTIONS\n            .read()\n            .or_poisoned()\n            .get(&key)\n            .map(|server_fn| {\n                let middleware = (server_fn.middleware)();\n                let mut service = server_fn.clone().boxed();\n                for middleware in middleware {\n                    service = middleware.layer(service);\n                }\n                service\n            })\n    }\n}\n\n/// Actix integration.\n#[cfg(feature = \"actix-no-default\")]\npub mod actix {\n    use crate::{\n        error::FromServerFnError, middleware::BoxedService,\n        request::actix::ActixRequest, response::actix::ActixResponse,\n        server::Server, LazyServerFnMap, Protocol, ServerFn, ServerFnTraitObj,\n    };\n    use actix_web::{web::Payload, HttpRequest, HttpResponse};\n    use http::Method;\n    use or_poisoned::OrPoisoned;\n    #[doc(hidden)]\n    pub use send_wrapper::SendWrapper;\n    use std::future::Future;\n\n    static REGISTERED_SERVER_FUNCTIONS: LazyServerFnMap<\n        ActixRequest,\n        ActixResponse,\n    > = initialize_server_fn_map!(ActixRequest, ActixResponse);\n\n    /// The actix server function backend\n    pub struct ActixServerFnBackend;\n\n    impl<Error, InputStreamError, OutputStreamError>\n        Server<Error, InputStreamError, OutputStreamError>\n        for ActixServerFnBackend\n    where\n        Error: FromServerFnError + Send + Sync,\n        InputStreamError: FromServerFnError + Send + Sync,\n        OutputStreamError: FromServerFnError + Send + Sync,\n    {\n        type Request = ActixRequest;\n        type Response = ActixResponse;\n\n        fn spawn(\n            future: impl Future<Output = ()> + Send + 'static,\n        ) -> Result<(), Error> {\n            actix_web::rt::spawn(future);\n            Ok(())\n        }\n    }\n\n    /// Explicitly register a server function. This is only necessary if you are\n    /// running the server in a WASM environment (or a rare environment that the\n    /// `inventory` crate won't work in.).\n    pub fn register_explicit<T>()\n    where\n        T: ServerFn<\n                Server: crate::Server<\n                    T::Error,\n                    T::InputStreamError,\n                    T::OutputStreamError,\n                    Request = ActixRequest,\n                    Response = ActixResponse,\n                >,\n            > + 'static,\n    {\n        REGISTERED_SERVER_FUNCTIONS.write().or_poisoned().insert(\n            (T::PATH.into(), T::Protocol::METHOD),\n            ServerFnTraitObj::new::<T>(|req| Box::pin(T::run_on_server(req))),\n        );\n    }\n\n    /// The set of all registered server function paths.\n    pub fn server_fn_paths() -> impl Iterator<Item = (&'static str, Method)> {\n        let paths: Vec<_> = REGISTERED_SERVER_FUNCTIONS\n            .read()\n            .unwrap()\n            .values()\n            .map(|item| (item.path(), item.method()))\n            .collect();\n\n        paths.into_iter()\n    }\n\n    /// An Actix handler that responds to a server function request.\n    pub async fn handle_server_fn(\n        req: HttpRequest,\n        payload: Payload,\n    ) -> HttpResponse {\n        let path = req.uri().path();\n        let method = req.method();\n        if let Some(mut service) = get_server_fn_service(path, method) {\n            service\n                .run(ActixRequest::from((req, payload)))\n                .await\n                .0\n                .take()\n        } else {\n            HttpResponse::BadRequest().body(format!(\n                \"Could not find a server function at the route {path}. \\\n                 \\n\\nIt's likely that either\\n 1. The API prefix you specify \\\n                 in the `#[server]` macro doesn't match the prefix at which \\\n                 your server function handler is mounted, or \\n2. You are on \\\n                 a platform that doesn't support automatic server function \\\n                 registration and you need to call \\\n                 ServerFn::register_explicit() on the server function type, \\\n                 somewhere in your `main` function.\",\n            ))\n        }\n    }\n\n    /// Returns the server function at the given path as a service that can be modified.\n    pub fn get_server_fn_service(\n        path: &str,\n        method: &actix_web::http::Method,\n    ) -> Option<BoxedService<ActixRequest, ActixResponse>> {\n        use actix_web::http::Method as ActixMethod;\n\n        let method = match *method {\n            ActixMethod::GET => Method::GET,\n            ActixMethod::POST => Method::POST,\n            ActixMethod::PUT => Method::PUT,\n            ActixMethod::PATCH => Method::PATCH,\n            ActixMethod::DELETE => Method::DELETE,\n            ActixMethod::HEAD => Method::HEAD,\n            ActixMethod::TRACE => Method::TRACE,\n            ActixMethod::OPTIONS => Method::OPTIONS,\n            ActixMethod::CONNECT => Method::CONNECT,\n            _ => unreachable!(),\n        };\n        REGISTERED_SERVER_FUNCTIONS\n            .read()\n            .or_poisoned()\n            .get(&(path.into(), method))\n            .map(|server_fn| {\n                let middleware = (server_fn.middleware)();\n                let mut service = server_fn.clone().boxed();\n                for middleware in middleware {\n                    service = middleware.layer(service);\n                }\n                service\n            })\n    }\n}\n\n/// Mocks for the server function backend types when compiling for the client.\npub mod mock {\n    use std::future::Future;\n\n    /// A mocked server type that can be used in place of the actual server,\n    /// when compiling for the browser.\n    ///\n    /// ## Panics\n    /// This always panics if its methods are called. It is used solely to stub out the\n    /// server type when compiling for the client.\n    pub struct BrowserMockServer;\n\n    impl<Error, InputStreamError, OutputStreamError>\n        crate::server::Server<Error, InputStreamError, OutputStreamError>\n        for BrowserMockServer\n    where\n        Error: Send + 'static,\n        InputStreamError: Send + 'static,\n        OutputStreamError: Send + 'static,\n    {\n        type Request = crate::request::BrowserMockReq;\n        type Response = crate::response::BrowserMockRes;\n\n        fn spawn(\n            _: impl Future<Output = ()> + Send + 'static,\n        ) -> Result<(), Error> {\n            unreachable!()\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n\n    use super::*;\n    use crate::codec::JsonEncoding;\n    use serde::{Deserialize, Serialize};\n\n    #[derive(Debug, Serialize, Deserialize)]\n    enum TestError {\n        ServerFnError(ServerFnErrorErr),\n    }\n\n    impl FromServerFnError for TestError {\n        type Encoder = JsonEncoding;\n\n        fn from_server_fn_error(value: ServerFnErrorErr) -> Self {\n            Self::ServerFnError(value)\n        }\n    }\n    #[test]\n    fn test_result_serialization() {\n        // Test Ok variant\n        let ok_result: Result<Bytes, Bytes> =\n            Ok(Bytes::from_static(b\"success data\"));\n        let serialized = serialize_result(ok_result);\n        let deserialized = deserialize_result::<TestError>(serialized);\n        assert!(deserialized.is_ok());\n        assert_eq!(deserialized.unwrap(), Bytes::from_static(b\"success data\"));\n\n        // Test Err variant\n        let err_result: Result<Bytes, Bytes> =\n            Err(Bytes::from_static(b\"error details\"));\n        let serialized = serialize_result(err_result);\n        let deserialized = deserialize_result::<TestError>(serialized);\n        assert!(deserialized.is_err());\n        assert_eq!(\n            deserialized.unwrap_err(),\n            Bytes::from_static(b\"error details\")\n        );\n    }\n}\n"
  },
  {
    "path": "server_fn/src/middleware/mod.rs",
    "content": "use crate::error::ServerFnErrorErr;\nuse bytes::Bytes;\nuse std::{future::Future, pin::Pin};\n\n/// An abstraction over a middleware layer, which can be used to add additional\n/// middleware layer to a [`Service`].\npub trait Layer<Req, Res>: Send + Sync + 'static {\n    /// Adds this layer to the inner service.\n    fn layer(&self, inner: BoxedService<Req, Res>) -> BoxedService<Req, Res>;\n}\n\n/// A type-erased service, which takes an HTTP request and returns a response.\npub struct BoxedService<Req, Res> {\n    /// A function that converts a [`ServerFnErrorErr`] into a string.\n    pub ser: fn(ServerFnErrorErr) -> Bytes,\n    /// The inner service.\n    pub service: Box<dyn Service<Req, Res> + Send>,\n}\n\nimpl<Req, Res> BoxedService<Req, Res> {\n    /// Constructs a type-erased service from this service.\n    pub fn new(\n        ser: fn(ServerFnErrorErr) -> Bytes,\n        service: impl Service<Req, Res> + Send + 'static,\n    ) -> Self {\n        Self {\n            ser,\n            service: Box::new(service),\n        }\n    }\n\n    /// Converts a request into a response by running the inner service.\n    pub fn run(\n        &mut self,\n        req: Req,\n    ) -> Pin<Box<dyn Future<Output = Res> + Send>> {\n        self.service.run(req, self.ser)\n    }\n}\n\n/// A service converts an HTTP request into a response.\npub trait Service<Request, Response> {\n    /// Converts a request into a response.\n    fn run(\n        &mut self,\n        req: Request,\n        ser: fn(ServerFnErrorErr) -> Bytes,\n    ) -> Pin<Box<dyn Future<Output = Response> + Send>>;\n}\n\n#[cfg(feature = \"axum-no-default\")]\nmod axum {\n    use super::{BoxedService, Service};\n    use crate::{error::ServerFnErrorErr, response::Res, ServerFnError};\n    use axum::body::Body;\n    use bytes::Bytes;\n    use http::{Request, Response};\n    use std::{future::Future, pin::Pin};\n\n    impl<S> super::Service<Request<Body>, Response<Body>> for S\n    where\n        S: tower::Service<Request<Body>, Response = Response<Body>>,\n        S::Future: Send + 'static,\n        S::Error: std::fmt::Display + Send + 'static,\n    {\n        fn run(\n            &mut self,\n            req: Request<Body>,\n            ser: fn(ServerFnErrorErr) -> Bytes,\n        ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send>> {\n            let path = req.uri().path().to_string();\n            let inner = self.call(req);\n            Box::pin(async move {\n                inner.await.unwrap_or_else(|e| {\n                    // TODO: This does not set the Content-Type on the response. Doing so will\n                    //  require a breaking change in order to get the correct encoding from the\n                    //  error's `FromServerFnError::Encoder::CONTENT_TYPE` impl.\n                    //  Note: This only applies to middleware errors.\n                    let err =\n                        ser(ServerFnErrorErr::MiddlewareError(e.to_string()));\n                    Response::<Body>::error_response(&path, err)\n                })\n            })\n        }\n    }\n\n    impl tower::Service<Request<Body>>\n        for BoxedService<Request<Body>, Response<Body>>\n    {\n        type Response = Response<Body>;\n        type Error = ServerFnError;\n        type Future = Pin<\n            Box<\n                dyn std::future::Future<\n                        Output = Result<Self::Response, Self::Error>,\n                    > + Send,\n            >,\n        >;\n\n        fn poll_ready(\n            &mut self,\n            _cx: &mut std::task::Context<'_>,\n        ) -> std::task::Poll<Result<(), Self::Error>> {\n            Ok(()).into()\n        }\n\n        fn call(&mut self, req: Request<Body>) -> Self::Future {\n            let inner = self.service.run(req, self.ser);\n            Box::pin(async move { Ok(inner.await) })\n        }\n    }\n\n    impl<L> super::Layer<Request<Body>, Response<Body>> for L\n    where\n        L: tower_layer::Layer<BoxedService<Request<Body>, Response<Body>>>\n            + Sync\n            + Send\n            + 'static,\n        L::Service: Service<Request<Body>, Response<Body>> + Send + 'static,\n    {\n        fn layer(\n            &self,\n            inner: BoxedService<Request<Body>, Response<Body>>,\n        ) -> BoxedService<Request<Body>, Response<Body>> {\n            BoxedService::new(inner.ser, self.layer(inner))\n        }\n    }\n}\n\n#[cfg(feature = \"actix-no-default\")]\nmod actix {\n    use crate::{\n        error::ServerFnErrorErr,\n        request::actix::ActixRequest,\n        response::{actix::ActixResponse, Res},\n    };\n    use actix_web::{HttpRequest, HttpResponse};\n    use bytes::Bytes;\n    use std::{future::Future, pin::Pin};\n\n    impl<S> super::Service<HttpRequest, HttpResponse> for S\n    where\n        S: actix_web::dev::Service<HttpRequest, Response = HttpResponse>,\n        S::Future: Send + 'static,\n        S::Error: std::fmt::Display + Send + 'static,\n    {\n        fn run(\n            &mut self,\n            req: HttpRequest,\n            ser: fn(ServerFnErrorErr) -> Bytes,\n        ) -> Pin<Box<dyn Future<Output = HttpResponse> + Send>> {\n            let path = req.uri().path().to_string();\n            let inner = self.call(req);\n            Box::pin(async move {\n                inner.await.unwrap_or_else(|e| {\n                    // TODO: This does not set the Content-Type on the response. Doing so will\n                    //  require a breaking change in order to get the correct encoding from the\n                    //  error's `FromServerFnError::Encoder::CONTENT_TYPE` impl.\n                    //  Note: This only applies to middleware errors.\n                    let err =\n                        ser(ServerFnErrorErr::MiddlewareError(e.to_string()));\n                    ActixResponse::error_response(&path, err).take()\n                })\n            })\n        }\n    }\n\n    impl<S> super::Service<ActixRequest, ActixResponse> for S\n    where\n        S: actix_web::dev::Service<HttpRequest, Response = HttpResponse>,\n        S::Future: Send + 'static,\n        S::Error: std::fmt::Display + Send + 'static,\n    {\n        fn run(\n            &mut self,\n            req: ActixRequest,\n            ser: fn(ServerFnErrorErr) -> Bytes,\n        ) -> Pin<Box<dyn Future<Output = ActixResponse> + Send>> {\n            let path = req.0 .0.uri().path().to_string();\n            let inner = self.call(req.0.take().0);\n            Box::pin(async move {\n                ActixResponse::from(inner.await.unwrap_or_else(|e| {\n                    let err =\n                        ser(ServerFnErrorErr::MiddlewareError(e.to_string()));\n                    ActixResponse::error_response(&path, err).take()\n                }))\n            })\n        }\n    }\n}\n"
  },
  {
    "path": "server_fn/src/redirect.rs",
    "content": "use std::sync::OnceLock;\n\n/// A custom header that can be set with any value to indicate\n/// that the server function client should redirect to a new route.\n///\n/// This is useful because it allows returning a value from the request,\n/// while also indicating that a redirect should follow. This cannot be\n/// done with an HTTP `3xx` status code, because the browser will follow\n/// that redirect rather than returning the desired data.\npub const REDIRECT_HEADER: &str = \"serverfnredirect\";\n\n/// A function that will be called if a server function returns a `3xx` status\n/// or the [`REDIRECT_HEADER`].\npub type RedirectHook = Box<dyn Fn(&str) + Send + Sync>;\n\n// allowed: not in a public API, and pretty straightforward\n#[allow(clippy::type_complexity)]\npub(crate) static REDIRECT_HOOK: OnceLock<RedirectHook> = OnceLock::new();\n\n/// Sets a function that will be called if a server function returns a `3xx` status\n/// or the [`REDIRECT_HEADER`]. Returns `Err(_)` if the hook has already been set.\npub fn set_redirect_hook(\n    hook: impl Fn(&str) + Send + Sync + 'static,\n) -> Result<(), RedirectHook> {\n    REDIRECT_HOOK.set(Box::new(hook))\n}\n\n/// Calls the hook that has been set by [`set_redirect_hook`] to redirect to `loc`.\npub fn call_redirect_hook(loc: &str) {\n    if let Some(hook) = REDIRECT_HOOK.get() {\n        hook(loc)\n    }\n}\n"
  },
  {
    "path": "server_fn/src/request/actix.rs",
    "content": "use crate::{\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    request::Req,\n    response::actix::ActixResponse,\n};\nuse actix_web::{web::Payload, HttpRequest};\nuse actix_ws::Message;\nuse bytes::Bytes;\nuse futures::{FutureExt, Stream, StreamExt};\nuse send_wrapper::SendWrapper;\nuse std::{borrow::Cow, future::Future};\n\n/// A wrapped Actix request.\n///\n/// This uses a [`SendWrapper`] that allows the Actix `HttpRequest` type to be `Send`, but panics\n/// if it it is ever sent to another thread. Actix pins request handling to a single thread, so this\n/// is necessary to be compatible with traits that require `Send` but should never panic in actual use.\npub struct ActixRequest(pub(crate) SendWrapper<(HttpRequest, Payload)>);\n\nimpl ActixRequest {\n    /// Returns the raw Actix request, and its body.\n    pub fn take(self) -> (HttpRequest, Payload) {\n        self.0.take()\n    }\n\n    fn header(&self, name: &str) -> Option<Cow<'_, str>> {\n        self.0\n             .0\n            .headers()\n            .get(name)\n            .map(|h| String::from_utf8_lossy(h.as_bytes()))\n    }\n}\n\nimpl From<(HttpRequest, Payload)> for ActixRequest {\n    fn from(value: (HttpRequest, Payload)) -> Self {\n        ActixRequest(SendWrapper::new(value))\n    }\n}\n\nimpl<Error, InputStreamError, OutputStreamError>\n    Req<Error, InputStreamError, OutputStreamError> for ActixRequest\nwhere\n    Error: FromServerFnError + Send,\n    InputStreamError: FromServerFnError + Send,\n    OutputStreamError: FromServerFnError + Send,\n{\n    type WebsocketResponse = ActixResponse;\n\n    fn as_query(&self) -> Option<&str> {\n        self.0 .0.uri().query()\n    }\n\n    fn to_content_type(&self) -> Option<Cow<'_, str>> {\n        self.header(\"Content-Type\")\n    }\n\n    fn accepts(&self) -> Option<Cow<'_, str>> {\n        self.header(\"Accept\")\n    }\n\n    fn referer(&self) -> Option<Cow<'_, str>> {\n        self.header(\"Referer\")\n    }\n\n    fn try_into_bytes(\n        self,\n    ) -> impl Future<Output = Result<Bytes, Error>> + Send {\n        // Actix is going to keep this on a single thread anyway so it's fine to wrap it\n        // with SendWrapper, which makes it `Send` but will panic if it moves to another thread\n        SendWrapper::new(async move {\n            let payload = self.0.take().1;\n            payload.to_bytes().await.map_err(|e| {\n                ServerFnErrorErr::Deserialization(e.to_string())\n                    .into_app_error()\n            })\n        })\n    }\n\n    fn try_into_string(\n        self,\n    ) -> impl Future<Output = Result<String, Error>> + Send {\n        // Actix is going to keep this on a single thread anyway so it's fine to wrap it\n        // with SendWrapper, which makes it `Send` but will panic if it moves to another thread\n        SendWrapper::new(async move {\n            let payload = self.0.take().1;\n            let bytes = payload.to_bytes().await.map_err(|e| {\n                Error::from_server_fn_error(ServerFnErrorErr::Deserialization(\n                    e.to_string(),\n                ))\n            })?;\n            String::from_utf8(bytes.into()).map_err(|e| {\n                Error::from_server_fn_error(ServerFnErrorErr::Deserialization(\n                    e.to_string(),\n                ))\n            })\n        })\n    }\n\n    fn try_into_stream(\n        self,\n    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send, Error> {\n        let payload = self.0.take().1;\n        let stream = payload.map(|res| {\n            res.map_err(|e| {\n                Error::from_server_fn_error(ServerFnErrorErr::Deserialization(\n                    e.to_string(),\n                ))\n                .ser()\n            })\n        });\n        Ok(SendWrapper::new(stream))\n    }\n\n    async fn try_into_websocket(\n        self,\n    ) -> Result<\n        (\n            impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n            impl futures::Sink<Bytes> + Send + 'static,\n            Self::WebsocketResponse,\n        ),\n        Error,\n    > {\n        let (request, payload) = self.0.take();\n        let (response, mut session, mut msg_stream) =\n            actix_ws::handle(&request, payload).map_err(|e| {\n                Error::from_server_fn_error(ServerFnErrorErr::Request(\n                    e.to_string(),\n                ))\n            })?;\n\n        let (mut response_stream_tx, response_stream_rx) =\n            futures::channel::mpsc::channel(2048);\n        let (response_sink_tx, mut response_sink_rx) =\n            futures::channel::mpsc::channel::<Bytes>(2048);\n\n        actix_web::rt::spawn(async move {\n            loop {\n                futures::select! {\n                    incoming = response_sink_rx.next() => {\n                        let Some(incoming) = incoming else {\n                            break;\n                        };\n                                if let Err(err) = session.binary(incoming).await {\n                                    _ = response_stream_tx.start_send(Err(InputStreamError::from_server_fn_error(ServerFnErrorErr::Request(err.to_string())).ser()));\n                                }\n                    },\n                    outgoing = msg_stream.next().fuse() => {\n                        let Some(outgoing) = outgoing else {\n                            break;\n                        };\n                        match outgoing {\n                            Ok(Message::Ping(bytes)) => {\n                                if session.pong(&bytes).await.is_err() {\n                                    break;\n                                }\n                            }\n                            Ok(Message::Binary(bytes)) => {\n                                _ = response_stream_tx\n                                    .start_send(\n                                        Ok(bytes),\n                                    );\n                            }\n                            Ok(Message::Text(text)) => {\n                                _ = response_stream_tx.start_send(Ok(text.into_bytes()));\n                            }\n                            Ok(Message::Close(_)) => {\n                                break;\n                            }\n                            Ok(_other) => {\n                            }\n                            Err(e) => {\n                                _ = response_stream_tx.start_send(Err(InputStreamError::from_server_fn_error(ServerFnErrorErr::Response(e.to_string())).ser()));\n                            }\n                        }\n                    }\n                }\n            }\n            let _ = session.close(None).await;\n        });\n\n        Ok((\n            response_stream_rx,\n            response_sink_tx,\n            ActixResponse::from(response),\n        ))\n    }\n}\n"
  },
  {
    "path": "server_fn/src/request/axum.rs",
    "content": "use crate::{\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    request::Req,\n};\nuse axum::{\n    body::{Body, Bytes},\n    response::Response,\n};\nuse futures::{Sink, Stream, StreamExt};\nuse http::{\n    header::{ACCEPT, CONTENT_TYPE, REFERER},\n    Request,\n};\nuse http_body_util::BodyExt;\nuse std::borrow::Cow;\n\nimpl<Error, InputStreamError, OutputStreamError>\n    Req<Error, InputStreamError, OutputStreamError> for Request<Body>\nwhere\n    Error: FromServerFnError + Send,\n    InputStreamError: FromServerFnError + Send,\n    OutputStreamError: FromServerFnError + Send,\n{\n    type WebsocketResponse = Response;\n\n    fn as_query(&self) -> Option<&str> {\n        self.uri().query()\n    }\n\n    fn to_content_type(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(CONTENT_TYPE)\n            .map(|h| String::from_utf8_lossy(h.as_bytes()))\n    }\n\n    fn accepts(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(ACCEPT)\n            .map(|h| String::from_utf8_lossy(h.as_bytes()))\n    }\n\n    fn referer(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(REFERER)\n            .map(|h| String::from_utf8_lossy(h.as_bytes()))\n    }\n\n    async fn try_into_bytes(self) -> Result<Bytes, Error> {\n        let (_parts, body) = self.into_parts();\n\n        body.collect().await.map(|c| c.to_bytes()).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })\n    }\n\n    async fn try_into_string(self) -> Result<String, Error> {\n        let bytes = Req::<Error>::try_into_bytes(self).await?;\n        String::from_utf8(bytes.to_vec()).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })\n    }\n\n    fn try_into_stream(\n        self,\n    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static, Error>\n    {\n        Ok(self.into_body().into_data_stream().map(|chunk| {\n            chunk.map_err(|e| {\n                Error::from_server_fn_error(ServerFnErrorErr::Deserialization(\n                    e.to_string(),\n                ))\n                .ser()\n            })\n        }))\n    }\n\n    async fn try_into_websocket(\n        self,\n    ) -> Result<\n        (\n            impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n            impl Sink<Bytes> + Send + 'static,\n            Self::WebsocketResponse,\n        ),\n        Error,\n    > {\n        #[cfg(not(feature = \"axum\"))]\n        {\n            Err::<\n                (\n                    futures::stream::Once<\n                        std::future::Ready<Result<Bytes, Bytes>>,\n                    >,\n                    futures::sink::Drain<Bytes>,\n                    Self::WebsocketResponse,\n                ),\n                Error,\n            >(Error::from_server_fn_error(\n                crate::ServerFnErrorErr::Response(\n                    \"Websocket connections not supported for Axum when the \\\n                     `axum` feature is not enabled on the `server_fn` crate.\"\n                        .to_string(),\n                ),\n            ))\n        }\n        #[cfg(feature = \"axum\")]\n        {\n            use axum::extract::{ws::Message, FromRequest};\n            use futures::FutureExt;\n\n            let upgrade =\n                axum::extract::ws::WebSocketUpgrade::from_request(self, &())\n                    .await\n                    .map_err(|err| {\n                        Error::from_server_fn_error(ServerFnErrorErr::Request(\n                            err.to_string(),\n                        ))\n                    })?;\n            let (mut outgoing_tx, outgoing_rx) =\n                futures::channel::mpsc::channel::<Result<Bytes, Bytes>>(2048);\n            let (incoming_tx, mut incoming_rx) =\n                futures::channel::mpsc::channel::<Bytes>(2048);\n            let response = upgrade\n        .on_failed_upgrade({\n            let mut outgoing_tx = outgoing_tx.clone();\n            move |err: axum::Error| {\n                _ = outgoing_tx.start_send(Err(InputStreamError::from_server_fn_error(ServerFnErrorErr::Response(err.to_string())).ser()));\n            }\n        })\n        .on_upgrade(|mut session| async move {\n            loop {\n                futures::select! {\n                    incoming = incoming_rx.next() => {\n                        let Some(incoming) = incoming else {\n                            break;\n                        };\n                        if let Err(err) = session.send(Message::Binary(incoming)).await {\n                            _ = outgoing_tx.start_send(Err(InputStreamError::from_server_fn_error(ServerFnErrorErr::Request(err.to_string())).ser()));\n                        }\n                    },\n                        outgoing = session.recv().fuse() => {\n                        let Some(outgoing) = outgoing else {\n                            break;\n                        };\n                        match outgoing {\n                            Ok(Message::Binary(bytes)) => {\n                                _ = outgoing_tx\n                                    .start_send(\n                                        Ok(bytes),\n                                    );\n                            }\n                            Ok(Message::Text(text)) => {\n                                _ = outgoing_tx.start_send(Ok(Bytes::from(text)));\n                            }\n                            Ok(Message::Ping(bytes)) => {\n                                if session.send(Message::Pong(bytes)).await.is_err() {\n                                    break;\n                                }\n                            }\n                            Ok(_other) => {}\n                            Err(e) => {\n                                _ = outgoing_tx.start_send(Err(InputStreamError::from_server_fn_error(ServerFnErrorErr::Response(e.to_string())).ser()));\n                            }\n                        }\n                    }\n                }\n            }\n            _ = session.send(Message::Close(None)).await;\n        });\n\n            Ok((outgoing_rx, incoming_tx, response))\n        }\n    }\n}\n"
  },
  {
    "path": "server_fn/src/request/browser.rs",
    "content": "use super::ClientReq;\nuse crate::{\n    client::get_server_url,\n    error::{FromServerFnError, ServerFnErrorErr},\n};\nuse bytes::Bytes;\nuse futures::{Stream, StreamExt};\npub use gloo_net::http::Request;\nuse http::Method;\nuse js_sys::{Reflect, Uint8Array};\nuse send_wrapper::SendWrapper;\nuse std::ops::{Deref, DerefMut};\nuse wasm_bindgen::JsValue;\nuse wasm_streams::ReadableStream;\nuse web_sys::{\n    AbortController, AbortSignal, FormData, Headers, RequestInit,\n    UrlSearchParams,\n};\n\n/// A `fetch` request made in the browser.\n#[derive(Debug)]\npub struct BrowserRequest(pub(crate) SendWrapper<RequestInner>);\n\n#[derive(Debug)]\npub(crate) struct RequestInner {\n    pub(crate) request: Request,\n    pub(crate) abort_ctrl: Option<AbortOnDrop>,\n}\n\n#[derive(Debug)]\npub(crate) struct AbortOnDrop(Option<AbortController>);\n\nimpl AbortOnDrop {\n    /// Prevents the request from being aborted on drop.\n    pub fn prevent_cancellation(&mut self) {\n        self.0.take();\n    }\n}\n\nimpl Drop for AbortOnDrop {\n    fn drop(&mut self) {\n        if let Some(inner) = self.0.take() {\n            inner.abort();\n        }\n    }\n}\n\nimpl From<BrowserRequest> for Request {\n    fn from(value: BrowserRequest) -> Self {\n        value.0.take().request\n    }\n}\n\nimpl From<BrowserRequest> for web_sys::Request {\n    fn from(value: BrowserRequest) -> Self {\n        value.0.take().request.into()\n    }\n}\n\nimpl Deref for BrowserRequest {\n    type Target = Request;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0.deref().request\n    }\n}\n\nimpl DerefMut for BrowserRequest {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0.deref_mut().request\n    }\n}\n\n/// The `FormData` type available in the browser.\n#[derive(Debug)]\npub struct BrowserFormData(pub(crate) SendWrapper<FormData>);\n\nimpl BrowserFormData {\n    /// Returns the raw `web_sys::FormData` struct.\n    pub fn take(self) -> FormData {\n        self.0.take()\n    }\n}\n\nimpl From<FormData> for BrowserFormData {\n    fn from(value: FormData) -> Self {\n        Self(SendWrapper::new(value))\n    }\n}\n\nfn abort_signal() -> (Option<AbortOnDrop>, Option<AbortSignal>) {\n    let ctrl = AbortController::new().ok();\n    let signal = ctrl.as_ref().map(|ctrl| ctrl.signal());\n    (ctrl.map(|ctrl| AbortOnDrop(Some(ctrl))), signal)\n}\n\nimpl<E> ClientReq<E> for BrowserRequest\nwhere\n    E: FromServerFnError,\n{\n    type FormData = BrowserFormData;\n\n    fn try_new_req_query(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        query: &str,\n        method: http::Method,\n    ) -> Result<Self, E> {\n        let (abort_ctrl, abort_signal) = abort_signal();\n        let server_url = get_server_url();\n        let mut url = String::with_capacity(\n            server_url.len() + path.len() + 1 + query.len(),\n        );\n        url.push_str(server_url);\n        url.push_str(path);\n        url.push('?');\n        url.push_str(query);\n        Ok(Self(SendWrapper::new(RequestInner {\n            request: match method {\n                Method::GET => Request::get(&url),\n                Method::DELETE => Request::delete(&url),\n                Method::POST => Request::post(&url),\n                Method::PUT => Request::put(&url),\n                Method::PATCH => Request::patch(&url),\n                m => {\n                    return Err(E::from_server_fn_error(\n                        ServerFnErrorErr::UnsupportedRequestMethod(\n                            m.to_string(),\n                        ),\n                    ))\n                }\n            }\n            .header(\"Content-Type\", content_type)\n            .header(\"Accept\", accepts)\n            .abort_signal(abort_signal.as_ref())\n            .build()\n            .map_err(|e| {\n                E::from_server_fn_error(ServerFnErrorErr::Request(\n                    e.to_string(),\n                ))\n            })?,\n            abort_ctrl,\n        })))\n    }\n\n    fn try_new_req_text(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: String,\n        method: Method,\n    ) -> Result<Self, E> {\n        let (abort_ctrl, abort_signal) = abort_signal();\n        let server_url = get_server_url();\n        let mut url = String::with_capacity(server_url.len() + path.len());\n        url.push_str(server_url);\n        url.push_str(path);\n        Ok(Self(SendWrapper::new(RequestInner {\n            request: match method {\n                Method::POST => Request::post(&url),\n                Method::PATCH => Request::patch(&url),\n                Method::PUT => Request::put(&url),\n                m => {\n                    return Err(E::from_server_fn_error(\n                        ServerFnErrorErr::UnsupportedRequestMethod(\n                            m.to_string(),\n                        ),\n                    ))\n                }\n            }\n            .header(\"Content-Type\", content_type)\n            .header(\"Accept\", accepts)\n            .abort_signal(abort_signal.as_ref())\n            .body(body)\n            .map_err(|e| {\n                E::from_server_fn_error(ServerFnErrorErr::Request(\n                    e.to_string(),\n                ))\n            })?,\n            abort_ctrl,\n        })))\n    }\n\n    fn try_new_req_bytes(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: Bytes,\n        method: Method,\n    ) -> Result<Self, E> {\n        let (abort_ctrl, abort_signal) = abort_signal();\n        let server_url = get_server_url();\n        let mut url = String::with_capacity(server_url.len() + path.len());\n        url.push_str(server_url);\n        url.push_str(path);\n        let body: &[u8] = &body;\n        let body = Uint8Array::from(body).buffer();\n        Ok(Self(SendWrapper::new(RequestInner {\n            request: match method {\n                Method::POST => Request::post(&url),\n                Method::PATCH => Request::patch(&url),\n                Method::PUT => Request::put(&url),\n                m => {\n                    return Err(E::from_server_fn_error(\n                        ServerFnErrorErr::UnsupportedRequestMethod(\n                            m.to_string(),\n                        ),\n                    ))\n                }\n            }\n            .header(\"Content-Type\", content_type)\n            .header(\"Accept\", accepts)\n            .abort_signal(abort_signal.as_ref())\n            .body(body)\n            .map_err(|e| {\n                E::from_server_fn_error(ServerFnErrorErr::Request(\n                    e.to_string(),\n                ))\n            })?,\n            abort_ctrl,\n        })))\n    }\n\n    fn try_new_req_multipart(\n        path: &str,\n        accepts: &str,\n        body: Self::FormData,\n        method: Method,\n    ) -> Result<Self, E> {\n        let (abort_ctrl, abort_signal) = abort_signal();\n        let server_url = get_server_url();\n        let mut url = String::with_capacity(server_url.len() + path.len());\n        url.push_str(server_url);\n        url.push_str(path);\n        Ok(Self(SendWrapper::new(RequestInner {\n            request: match method {\n                Method::POST => Request::post(&url),\n                Method::PATCH => Request::patch(&url),\n                Method::PUT => Request::put(&url),\n                m => {\n                    return Err(E::from_server_fn_error(\n                        ServerFnErrorErr::UnsupportedRequestMethod(\n                            m.to_string(),\n                        ),\n                    ))\n                }\n            }\n            .header(\"Accept\", accepts)\n            .abort_signal(abort_signal.as_ref())\n            .body(body.0.take())\n            .map_err(|e| {\n                E::from_server_fn_error(ServerFnErrorErr::Request(\n                    e.to_string(),\n                ))\n            })?,\n            abort_ctrl,\n        })))\n    }\n\n    fn try_new_req_form_data(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: Self::FormData,\n        method: Method,\n    ) -> Result<Self, E> {\n        let (abort_ctrl, abort_signal) = abort_signal();\n        let form_data = body.0.take();\n        let url_params =\n            UrlSearchParams::new_with_str_sequence_sequence(&form_data)\n                .map_err(|e| {\n                    E::from_server_fn_error(ServerFnErrorErr::Serialization(\n                        e.as_string().unwrap_or_else(|| {\n                            \"Could not serialize FormData to URLSearchParams\"\n                                .to_string()\n                        }),\n                    ))\n                })?;\n        Ok(Self(SendWrapper::new(RequestInner {\n            request: match method {\n                Method::POST => Request::post(path),\n                Method::PUT => Request::put(path),\n                Method::PATCH => Request::patch(path),\n                m => {\n                    return Err(E::from_server_fn_error(\n                        ServerFnErrorErr::UnsupportedRequestMethod(\n                            m.to_string(),\n                        ),\n                    ))\n                }\n            }\n            .header(\"Content-Type\", content_type)\n            .header(\"Accept\", accepts)\n            .abort_signal(abort_signal.as_ref())\n            .body(url_params)\n            .map_err(|e| {\n                E::from_server_fn_error(ServerFnErrorErr::Request(\n                    e.to_string(),\n                ))\n            })?,\n            abort_ctrl,\n        })))\n    }\n\n    fn try_new_req_streaming(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: impl Stream<Item = Bytes> + 'static,\n        method: Method,\n    ) -> Result<Self, E> {\n        // Only allow for methods with bodies\n        match method {\n            Method::POST | Method::PATCH | Method::PUT => {}\n            m => {\n                return Err(E::from_server_fn_error(\n                    ServerFnErrorErr::UnsupportedRequestMethod(m.to_string()),\n                ))\n            }\n        }\n        // TODO abort signal\n        let (request, abort_ctrl) =\n            streaming_request(path, accepts, content_type, body, method)\n                .map_err(|e| {\n                    E::from_server_fn_error(ServerFnErrorErr::Request(format!(\n                        \"{e:?}\"\n                    )))\n                })?;\n        Ok(Self(SendWrapper::new(RequestInner {\n            request,\n            abort_ctrl,\n        })))\n    }\n}\n\nfn streaming_request(\n    path: &str,\n    accepts: &str,\n    content_type: &str,\n    body: impl Stream<Item = Bytes> + 'static,\n    method: Method,\n) -> Result<(Request, Option<AbortOnDrop>), JsValue> {\n    let (abort_ctrl, abort_signal) = abort_signal();\n    let stream = ReadableStream::from_stream(body.map(|bytes| {\n        let data = Uint8Array::from(bytes.as_ref());\n        let data = JsValue::from(data);\n        Ok(data) as Result<JsValue, JsValue>\n    }))\n    .into_raw();\n\n    let headers = Headers::new()?;\n    headers.append(\"Content-Type\", content_type)?;\n    headers.append(\"Accept\", accepts)?;\n\n    let init = RequestInit::new();\n    init.set_headers(&headers);\n    init.set_method(method.as_str());\n    init.set_signal(abort_signal.as_ref());\n    init.set_body(&stream);\n\n    // Chrome requires setting `duplex: \"half\"` on streaming requests\n    Reflect::set(\n        &init,\n        &JsValue::from_str(\"duplex\"),\n        &JsValue::from_str(\"half\"),\n    )?;\n    let req = web_sys::Request::new_with_str_and_init(path, &init)?;\n    Ok((Request::from(req), abort_ctrl))\n}\n"
  },
  {
    "path": "server_fn/src/request/generic.rs",
    "content": "//! This module uses platform-agnostic abstractions\n//! allowing users to run server functions on a wide range of\n//! platforms.\n//!\n//! The crates in use in this crate are:\n//!\n//! * `bytes`: platform-agnostic manipulation of bytes.\n//! * `http`: low-dependency HTTP abstractions' *front-end*.\n//!\n//! # Users\n//!\n//! * `wasm32-wasip*` integration crate `leptos_wasi` is using this\n//!   crate under the hood.\n\nuse crate::{\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    request::Req,\n};\nuse bytes::Bytes;\nuse futures::{\n    stream::{self, Stream},\n    Sink, StreamExt,\n};\nuse http::{Request, Response};\nuse std::borrow::Cow;\n\nimpl<Error, InputStreamError, OutputStreamError>\n    Req<Error, InputStreamError, OutputStreamError> for Request<Bytes>\nwhere\n    Error: FromServerFnError + Send,\n    InputStreamError: FromServerFnError + Send,\n    OutputStreamError: FromServerFnError + Send,\n{\n    type WebsocketResponse = Response<Bytes>;\n\n    async fn try_into_bytes(self) -> Result<Bytes, Error> {\n        Ok(self.into_body())\n    }\n\n    async fn try_into_string(self) -> Result<String, Error> {\n        String::from_utf8(self.into_body().into()).map_err(|err| {\n            ServerFnErrorErr::Deserialization(err.to_string()).into_app_error()\n        })\n    }\n\n    fn try_into_stream(\n        self,\n    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static, Error>\n    {\n        Ok(stream::iter(self.into_body())\n            .ready_chunks(16)\n            .map(|chunk| Ok(Bytes::from(chunk))))\n    }\n\n    fn to_content_type(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(http::header::CONTENT_TYPE)\n            .map(|val| String::from_utf8_lossy(val.as_bytes()))\n    }\n\n    fn accepts(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(http::header::ACCEPT)\n            .map(|val| String::from_utf8_lossy(val.as_bytes()))\n    }\n\n    fn referer(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(http::header::REFERER)\n            .map(|val| String::from_utf8_lossy(val.as_bytes()))\n    }\n\n    fn as_query(&self) -> Option<&str> {\n        self.uri().query()\n    }\n\n    async fn try_into_websocket(\n        self,\n    ) -> Result<\n        (\n            impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n            impl Sink<Bytes> + Send + 'static,\n            Self::WebsocketResponse,\n        ),\n        Error,\n    > {\n        Err::<\n            (\n                futures::stream::Once<std::future::Ready<Result<Bytes, Bytes>>>,\n                futures::sink::Drain<Bytes>,\n                Self::WebsocketResponse,\n            ),\n            _,\n        >(Error::from_server_fn_error(\n            crate::ServerFnErrorErr::Response(\n                \"Websockets are not supported on this platform.\".to_string(),\n            ),\n        ))\n    }\n}\n"
  },
  {
    "path": "server_fn/src/request/mod.rs",
    "content": "use bytes::Bytes;\nuse futures::{Sink, Stream};\nuse http::Method;\nuse std::{borrow::Cow, future::Future};\n\n/// Request types for Actix.\n#[cfg(feature = \"actix-no-default\")]\npub mod actix;\n/// Request types for Axum.\n#[cfg(feature = \"axum-no-default\")]\npub mod axum;\n/// Request types for the browser.\n#[cfg(feature = \"browser\")]\npub mod browser;\n#[cfg(feature = \"generic\")]\npub mod generic;\n/// Request types for [`reqwest`].\n#[cfg(feature = \"reqwest\")]\npub mod reqwest;\n\n/// Represents a request as made by the client.\npub trait ClientReq<E>\nwhere\n    Self: Sized,\n{\n    /// The type used for URL-encoded form data in this client.\n    type FormData;\n\n    /// Attempts to construct a new request with query parameters.\n    fn try_new_req_query(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        query: &str,\n        method: Method,\n    ) -> Result<Self, E>;\n\n    /// Attempts to construct a new request with a text body.\n    fn try_new_req_text(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: String,\n        method: Method,\n    ) -> Result<Self, E>;\n\n    /// Attempts to construct a new request with a binary body.\n    fn try_new_req_bytes(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: Bytes,\n        method: Method,\n    ) -> Result<Self, E>;\n\n    /// Attempts to construct a new request with form data as the body.\n    fn try_new_req_form_data(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: Self::FormData,\n        method: Method,\n    ) -> Result<Self, E>;\n\n    /// Attempts to construct a new request with a multipart body.\n    fn try_new_req_multipart(\n        path: &str,\n        accepts: &str,\n        body: Self::FormData,\n        method: Method,\n    ) -> Result<Self, E>;\n\n    /// Attempts to construct a new request with a streaming body.\n    fn try_new_req_streaming(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: impl Stream<Item = Bytes> + Send + 'static,\n        method: Method,\n    ) -> Result<Self, E>;\n\n    /// Attempts to construct a new `GET` request.\n    fn try_new_get(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        query: &str,\n    ) -> Result<Self, E> {\n        Self::try_new_req_query(path, content_type, accepts, query, Method::GET)\n    }\n\n    /// Attempts to construct a new `DELETE` request.\n    /// **Note**: Browser support for `DELETE` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_delete(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        query: &str,\n    ) -> Result<Self, E> {\n        Self::try_new_req_query(\n            path,\n            content_type,\n            accepts,\n            query,\n            Method::DELETE,\n        )\n    }\n\n    /// Attempts to construct a new `POST` request with a text body.\n    fn try_new_post(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: String,\n    ) -> Result<Self, E> {\n        Self::try_new_req_text(path, content_type, accepts, body, Method::POST)\n    }\n\n    /// Attempts to construct a new `PATCH` request with a text body.\n    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_patch(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: String,\n    ) -> Result<Self, E> {\n        Self::try_new_req_text(path, content_type, accepts, body, Method::PATCH)\n    }\n\n    /// Attempts to construct a new `PUT` request with a text body.\n    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_put(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: String,\n    ) -> Result<Self, E> {\n        Self::try_new_req_text(path, content_type, accepts, body, Method::PUT)\n    }\n\n    /// Attempts to construct a new `POST` request with a binary body.\n    fn try_new_post_bytes(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: Bytes,\n    ) -> Result<Self, E> {\n        Self::try_new_req_bytes(path, content_type, accepts, body, Method::POST)\n    }\n\n    /// Attempts to construct a new `PATCH` request with a binary body.\n    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_patch_bytes(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: Bytes,\n    ) -> Result<Self, E> {\n        Self::try_new_req_bytes(\n            path,\n            content_type,\n            accepts,\n            body,\n            Method::PATCH,\n        )\n    }\n\n    /// Attempts to construct a new `PUT` request with a binary body.\n    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_put_bytes(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: Bytes,\n    ) -> Result<Self, E> {\n        Self::try_new_req_bytes(path, content_type, accepts, body, Method::PUT)\n    }\n\n    /// Attempts to construct a new `POST` request with form data as the body.\n    fn try_new_post_form_data(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: Self::FormData,\n    ) -> Result<Self, E> {\n        Self::try_new_req_form_data(\n            path,\n            accepts,\n            content_type,\n            body,\n            Method::POST,\n        )\n    }\n\n    /// Attempts to construct a new `PATCH` request with form data as the body.\n    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_patch_form_data(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: Self::FormData,\n    ) -> Result<Self, E> {\n        Self::try_new_req_form_data(\n            path,\n            accepts,\n            content_type,\n            body,\n            Method::PATCH,\n        )\n    }\n\n    /// Attempts to construct a new `PUT` request with form data as the body.\n    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_put_form_data(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: Self::FormData,\n    ) -> Result<Self, E> {\n        Self::try_new_req_form_data(\n            path,\n            accepts,\n            content_type,\n            body,\n            Method::PUT,\n        )\n    }\n\n    /// Attempts to construct a new `POST` request with a multipart body.\n    fn try_new_post_multipart(\n        path: &str,\n        accepts: &str,\n        body: Self::FormData,\n    ) -> Result<Self, E> {\n        Self::try_new_req_multipart(path, accepts, body, Method::POST)\n    }\n\n    /// Attempts to construct a new `PATCH` request with a multipart body.\n    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_patch_multipart(\n        path: &str,\n        accepts: &str,\n        body: Self::FormData,\n    ) -> Result<Self, E> {\n        Self::try_new_req_multipart(path, accepts, body, Method::PATCH)\n    }\n\n    /// Attempts to construct a new `PUT` request with a multipart body.\n    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_put_multipart(\n        path: &str,\n        accepts: &str,\n        body: Self::FormData,\n    ) -> Result<Self, E> {\n        Self::try_new_req_multipart(path, accepts, body, Method::PUT)\n    }\n\n    /// Attempts to construct a new `POST` request with a streaming body.\n    fn try_new_post_streaming(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: impl Stream<Item = Bytes> + Send + 'static,\n    ) -> Result<Self, E> {\n        Self::try_new_req_streaming(\n            path,\n            accepts,\n            content_type,\n            body,\n            Method::POST,\n        )\n    }\n\n    /// Attempts to construct a new `PATCH` request with a streaming body.\n    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_patch_streaming(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: impl Stream<Item = Bytes> + Send + 'static,\n    ) -> Result<Self, E> {\n        Self::try_new_req_streaming(\n            path,\n            accepts,\n            content_type,\n            body,\n            Method::PATCH,\n        )\n    }\n\n    /// Attempts to construct a new `PUT` request with a streaming body.\n    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.\n    /// Consider using a `POST` request if functionality without JS/WASM is required.\n    fn try_new_put_streaming(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: impl Stream<Item = Bytes> + Send + 'static,\n    ) -> Result<Self, E> {\n        Self::try_new_req_streaming(\n            path,\n            accepts,\n            content_type,\n            body,\n            Method::PUT,\n        )\n    }\n}\n\n/// Represents the request as received by the server.\npub trait Req<Error, InputStreamError = Error, OutputStreamError = Error>\nwhere\n    Self: Sized,\n{\n    /// The response type for websockets.\n    type WebsocketResponse: Send;\n\n    /// Returns the query string of the request’s URL, starting after the `?`.\n    fn as_query(&self) -> Option<&str>;\n\n    /// Returns the `Content-Type` header, if any.\n    fn to_content_type(&self) -> Option<Cow<'_, str>>;\n\n    /// Returns the `Accepts` header, if any.\n    fn accepts(&self) -> Option<Cow<'_, str>>;\n\n    /// Returns the `Referer` header, if any.\n    fn referer(&self) -> Option<Cow<'_, str>>;\n\n    /// Attempts to extract the body of the request into [`Bytes`].\n    fn try_into_bytes(\n        self,\n    ) -> impl Future<Output = Result<Bytes, Error>> + Send;\n\n    /// Attempts to convert the body of the request into a string.\n    fn try_into_string(\n        self,\n    ) -> impl Future<Output = Result<String, Error>> + Send;\n\n    /// Attempts to convert the body of the request into a stream of bytes.\n    fn try_into_stream(\n        self,\n    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static, Error>;\n\n    /// Attempts to convert the body of the request into a websocket handle.\n    #[allow(clippy::type_complexity)]\n    fn try_into_websocket(\n        self,\n    ) -> impl Future<\n        Output = Result<\n            (\n                impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n                impl Sink<Bytes> + Send + 'static,\n                Self::WebsocketResponse,\n            ),\n            Error,\n        >,\n    > + Send;\n}\n\n/// A mocked request type that can be used in place of the actual server request,\n/// when compiling for the browser.\npub struct BrowserMockReq;\n\nimpl<Error, InputStreamError, OutputStreamError>\n    Req<Error, InputStreamError, OutputStreamError> for BrowserMockReq\nwhere\n    Error: Send + 'static,\n    InputStreamError: Send + 'static,\n    OutputStreamError: Send + 'static,\n{\n    type WebsocketResponse = crate::response::BrowserMockRes;\n\n    fn as_query(&self) -> Option<&str> {\n        unreachable!()\n    }\n\n    fn to_content_type(&self) -> Option<Cow<'_, str>> {\n        unreachable!()\n    }\n\n    fn accepts(&self) -> Option<Cow<'_, str>> {\n        unreachable!()\n    }\n\n    fn referer(&self) -> Option<Cow<'_, str>> {\n        unreachable!()\n    }\n    async fn try_into_bytes(self) -> Result<Bytes, Error> {\n        unreachable!()\n    }\n\n    async fn try_into_string(self) -> Result<String, Error> {\n        unreachable!()\n    }\n\n    fn try_into_stream(\n        self,\n    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send, Error> {\n        Ok(futures::stream::once(async { unreachable!() }))\n    }\n\n    async fn try_into_websocket(\n        self,\n    ) -> Result<\n        (\n            impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n            impl Sink<Bytes> + Send + 'static,\n            Self::WebsocketResponse,\n        ),\n        Error,\n    > {\n        #[allow(unreachable_code)]\n        Err::<\n            (\n                futures::stream::Once<std::future::Ready<Result<Bytes, Bytes>>>,\n                futures::sink::Drain<Bytes>,\n                Self::WebsocketResponse,\n            ),\n            _,\n        >(unreachable!())\n    }\n}\n"
  },
  {
    "path": "server_fn/src/request/reqwest.rs",
    "content": "use super::ClientReq;\nuse crate::{\n    client::get_server_url,\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n};\nuse bytes::Bytes;\nuse futures::{Stream, StreamExt};\nuse reqwest::{\n    header::{ACCEPT, CONTENT_TYPE},\n    Body,\n};\npub use reqwest::{multipart::Form, Client, Method, Request, Url};\nuse std::sync::LazyLock;\n\npub(crate) static CLIENT: LazyLock<Client> = LazyLock::new(Client::new);\n\nimpl<E> ClientReq<E> for Request\nwhere\n    E: FromServerFnError,\n{\n    type FormData = Form;\n\n    fn try_new_req_query(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        query: &str,\n        method: Method,\n    ) -> Result<Self, E> {\n        let url = format!(\"{}{}\", get_server_url(), path);\n        let mut url = Url::try_from(url.as_str()).map_err(|e| {\n            E::from_server_fn_error(ServerFnErrorErr::Request(e.to_string()))\n        })?;\n        url.set_query(Some(query));\n        let req = match method {\n            Method::GET => CLIENT.get(url),\n            Method::DELETE => CLIENT.delete(url),\n            Method::HEAD => CLIENT.head(url),\n            Method::POST => CLIENT.post(url),\n            Method::PATCH => CLIENT.patch(url),\n            Method::PUT => CLIENT.put(url),\n            m => {\n                return Err(E::from_server_fn_error(\n                    ServerFnErrorErr::UnsupportedRequestMethod(m.to_string()),\n                ))\n            }\n        }\n        .header(CONTENT_TYPE, content_type)\n        .header(ACCEPT, accepts)\n        .build()\n        .map_err(|e| {\n            E::from_server_fn_error(ServerFnErrorErr::Request(e.to_string()))\n        })?;\n        Ok(req)\n    }\n\n    fn try_new_req_text(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: String,\n        method: Method,\n    ) -> Result<Self, E> {\n        let url = format!(\"{}{}\", get_server_url(), path);\n        match method {\n            Method::POST => CLIENT.post(url),\n            Method::PUT => CLIENT.put(url),\n            Method::PATCH => CLIENT.patch(url),\n            m => {\n                return Err(E::from_server_fn_error(\n                    ServerFnErrorErr::UnsupportedRequestMethod(m.to_string()),\n                ))\n            }\n        }\n        .header(CONTENT_TYPE, content_type)\n        .header(ACCEPT, accepts)\n        .body(body)\n        .build()\n        .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into_app_error())\n    }\n\n    fn try_new_req_bytes(\n        path: &str,\n        content_type: &str,\n        accepts: &str,\n        body: Bytes,\n        method: Method,\n    ) -> Result<Self, E> {\n        let url = format!(\"{}{}\", get_server_url(), path);\n        match method {\n            Method::POST => CLIENT.post(url),\n            Method::PATCH => CLIENT.patch(url),\n            Method::PUT => CLIENT.put(url),\n            m => {\n                return Err(E::from_server_fn_error(\n                    ServerFnErrorErr::UnsupportedRequestMethod(m.to_string()),\n                ))\n            }\n        }\n        .header(CONTENT_TYPE, content_type)\n        .header(ACCEPT, accepts)\n        .body(body)\n        .build()\n        .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into_app_error())\n    }\n\n    fn try_new_req_multipart(\n        path: &str,\n        accepts: &str,\n        body: Self::FormData,\n        method: Method,\n    ) -> Result<Self, E> {\n        match method {\n            Method::POST => CLIENT.post(path),\n            Method::PUT => CLIENT.put(path),\n            Method::PATCH => CLIENT.patch(path),\n            m => {\n                return Err(E::from_server_fn_error(\n                    ServerFnErrorErr::UnsupportedRequestMethod(m.to_string()),\n                ))\n            }\n        }\n        .header(ACCEPT, accepts)\n        .multipart(body)\n        .build()\n        .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into_app_error())\n    }\n\n    fn try_new_req_form_data(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: Self::FormData,\n        method: Method,\n    ) -> Result<Self, E> {\n        match method {\n            Method::POST => CLIENT.post(path),\n            Method::PATCH => CLIENT.patch(path),\n            Method::PUT => CLIENT.put(path),\n            m => {\n                return Err(E::from_server_fn_error(\n                    ServerFnErrorErr::UnsupportedRequestMethod(m.to_string()),\n                ))\n            }\n        }\n        .header(CONTENT_TYPE, content_type)\n        .header(ACCEPT, accepts)\n        .multipart(body)\n        .build()\n        .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into_app_error())\n    }\n\n    fn try_new_req_streaming(\n        path: &str,\n        accepts: &str,\n        content_type: &str,\n        body: impl Stream<Item = Bytes> + Send + 'static,\n        method: Method,\n    ) -> Result<Self, E> {\n        let url = format!(\"{}{}\", get_server_url(), path);\n        let body = Body::wrap_stream(\n            body.map(|chunk| Ok(chunk) as Result<Bytes, ServerFnErrorErr>),\n        );\n        match method {\n            Method::POST => CLIENT.post(url),\n            Method::PUT => CLIENT.put(url),\n            Method::PATCH => CLIENT.patch(url),\n            m => {\n                return Err(E::from_server_fn_error(\n                    ServerFnErrorErr::UnsupportedRequestMethod(m.to_string()),\n                ))\n            }\n        }\n        .header(CONTENT_TYPE, content_type)\n        .header(ACCEPT, accepts)\n        .body(body)\n        .build()\n        .map_err(|e| ServerFnErrorErr::Request(e.to_string()).into_app_error())\n    }\n}\n"
  },
  {
    "path": "server_fn/src/request/spin.rs",
    "content": "use crate::{error::ServerFnError, request::Req};\nuse axum::body::{Body, Bytes};\nuse futures::{Stream, StreamExt};\nuse http::{\n    header::{ACCEPT, CONTENT_TYPE, REFERER},\n    Request,\n};\nuse http_body_util::BodyExt;\nuse std::borrow::Cow;\n\nimpl<E> Req<E> for IncomingRequest\nwhere\n    CustErr: 'static,\n{\n    fn as_query(&self) -> Option<&str> {\n        self.uri().query()\n    }\n\n    fn to_content_type(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(CONTENT_TYPE)\n            .map(|h| String::from_utf8_lossy(h.as_bytes()))\n    }\n\n    fn accepts(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(ACCEPT)\n            .map(|h| String::from_utf8_lossy(h.as_bytes()))\n    }\n\n    fn referer(&self) -> Option<Cow<'_, str>> {\n        self.headers()\n            .get(REFERER)\n            .map(|h| String::from_utf8_lossy(h.as_bytes()))\n    }\n\n    async fn try_into_bytes(self) -> Result<Bytes, E> {\n        let (_parts, body) = self.into_parts();\n\n        body.collect().await.map(|c| c.to_bytes()).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into()\n        })\n    }\n\n    async fn try_into_string(self) -> Result<String, E> {\n        let bytes = self.try_into_bytes().await?;\n        String::from_utf8(bytes.to_vec()).map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into()\n        })\n    }\n\n    fn try_into_stream(\n        self,\n    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static, E>\n    {\n        Ok(self.into_body().into_data_stream().map(|chunk| {\n            chunk.map_err(|e| {\n                E::from_server_fn_error(ServerFnErrorErr::Deserialization(\n                    e.to_string(),\n                ))\n                .ser()\n            })\n        }))\n    }\n}\n"
  },
  {
    "path": "server_fn/src/response/actix.rs",
    "content": "use super::{Res, TryRes};\nuse crate::error::{\n    FromServerFnError, ServerFnErrorWrapper, SERVER_FN_ERROR_HEADER,\n};\nuse actix_web::{\n    http::{\n        header,\n        header::{HeaderValue, CONTENT_TYPE, LOCATION},\n        StatusCode,\n    },\n    HttpResponse,\n};\nuse bytes::Bytes;\nuse futures::{Stream, StreamExt};\nuse send_wrapper::SendWrapper;\n\n/// A wrapped Actix response.\n///\n/// This uses a [`SendWrapper`] that allows the Actix `HttpResponse` type to be `Send`, but panics\n/// if it it is ever sent to another thread. Actix pins request handling to a single thread, so this\n/// is necessary to be compatible with traits that require `Send` but should never panic in actual use.\npub struct ActixResponse(pub(crate) SendWrapper<HttpResponse>);\n\nimpl ActixResponse {\n    /// Returns the raw Actix response.\n    pub fn take(self) -> HttpResponse {\n        self.0.take()\n    }\n}\n\nimpl From<HttpResponse> for ActixResponse {\n    fn from(value: HttpResponse) -> Self {\n        Self(SendWrapper::new(value))\n    }\n}\n\nimpl<E> TryRes<E> for ActixResponse\nwhere\n    E: FromServerFnError,\n{\n    fn try_from_string(content_type: &str, data: String) -> Result<Self, E> {\n        let mut builder = HttpResponse::build(StatusCode::OK);\n        Ok(ActixResponse(SendWrapper::new(\n            builder\n                .insert_header((header::CONTENT_TYPE, content_type))\n                .body(data),\n        )))\n    }\n\n    fn try_from_bytes(content_type: &str, data: Bytes) -> Result<Self, E> {\n        let mut builder = HttpResponse::build(StatusCode::OK);\n        Ok(ActixResponse(SendWrapper::new(\n            builder\n                .insert_header((header::CONTENT_TYPE, content_type))\n                .body(data),\n        )))\n    }\n\n    fn try_from_stream(\n        content_type: &str,\n        data: impl Stream<Item = Result<Bytes, Bytes>> + 'static,\n    ) -> Result<Self, E> {\n        let mut builder = HttpResponse::build(StatusCode::OK);\n        Ok(ActixResponse(SendWrapper::new(\n            builder\n                .insert_header((header::CONTENT_TYPE, content_type))\n                .streaming(data.map(|data| {\n                    data.map_err(|e| ServerFnErrorWrapper(E::de(e)))\n                })),\n        )))\n    }\n}\n\nimpl Res for ActixResponse {\n    fn error_response(path: &str, err: Bytes) -> Self {\n        ActixResponse(SendWrapper::new(\n            HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR)\n                .append_header((SERVER_FN_ERROR_HEADER, path))\n                .body(err),\n        ))\n    }\n\n    fn content_type(&mut self, content_type: &str) {\n        if let Ok(content_type) = HeaderValue::from_str(content_type) {\n            self.0.headers_mut().insert(CONTENT_TYPE, content_type);\n        }\n    }\n\n    fn redirect(&mut self, path: &str) {\n        if let Ok(path) = HeaderValue::from_str(path) {\n            *self.0.status_mut() = StatusCode::FOUND;\n            self.0.headers_mut().insert(LOCATION, path);\n        }\n    }\n}\n"
  },
  {
    "path": "server_fn/src/response/browser.rs",
    "content": "use super::ClientRes;\nuse crate::{\n    error::{FromServerFnError, IntoAppError, ServerFnErrorErr},\n    redirect::REDIRECT_HEADER,\n};\nuse bytes::Bytes;\nuse futures::{Stream, StreamExt};\npub use gloo_net::http::Response;\nuse http::{HeaderMap, HeaderName, HeaderValue};\nuse js_sys::Uint8Array;\nuse send_wrapper::SendWrapper;\nuse std::{future::Future, str::FromStr};\nuse wasm_bindgen::JsCast;\nuse wasm_streams::ReadableStream;\n\n/// The response to a `fetch` request made in the browser.\npub struct BrowserResponse(pub(crate) SendWrapper<Response>);\n\nimpl BrowserResponse {\n    /// Generate the headers from the internal [`Response`] object.\n    /// This is a workaround for the fact that the `Response` object does not\n    /// have a [`HeaderMap`] directly. This function will iterate over the\n    /// headers and convert them to a [`HeaderMap`].\n    pub fn generate_headers(&self) -> HeaderMap {\n        self.0\n            .headers()\n            .entries()\n            .filter_map(|(key, value)| {\n                let key = HeaderName::from_str(&key).ok()?;\n                let value = HeaderValue::from_str(&value).ok()?;\n                Some((key, value))\n            })\n            .collect()\n    }\n}\n\nimpl<E: FromServerFnError> ClientRes<E> for BrowserResponse {\n    fn try_into_string(self) -> impl Future<Output = Result<String, E>> + Send {\n        // the browser won't send this async work between threads (because it's single-threaded)\n        // so we can safely wrap this\n        SendWrapper::new(async move {\n            self.0.text().await.map_err(|e| {\n                ServerFnErrorErr::Deserialization(e.to_string())\n                    .into_app_error()\n            })\n        })\n    }\n\n    fn try_into_bytes(self) -> impl Future<Output = Result<Bytes, E>> + Send {\n        // the browser won't send this async work between threads (because it's single-threaded)\n        // so we can safely wrap this\n        SendWrapper::new(async move {\n            self.0.binary().await.map(Bytes::from).map_err(|e| {\n                ServerFnErrorErr::Deserialization(e.to_string())\n                    .into_app_error()\n            })\n        })\n    }\n\n    fn try_into_stream(\n        self,\n    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static, E>\n    {\n        let stream = ReadableStream::from_raw(self.0.body().unwrap())\n            .into_stream()\n            .map(|data| match data {\n                Err(e) => {\n                    web_sys::console::error_1(&e);\n                    Err(E::from_server_fn_error(ServerFnErrorErr::Request(\n                        format!(\"{e:?}\"),\n                    ))\n                    .ser())\n                }\n                Ok(data) => {\n                    let data = data.unchecked_into::<Uint8Array>();\n                    let mut buf = Vec::new();\n                    let length = data.length();\n                    buf.resize(length as usize, 0);\n                    data.copy_to(&mut buf);\n                    Ok(Bytes::from(buf))\n                }\n            });\n        Ok(SendWrapper::new(stream))\n    }\n\n    fn status(&self) -> u16 {\n        self.0.status()\n    }\n\n    fn status_text(&self) -> String {\n        self.0.status_text()\n    }\n\n    fn location(&self) -> String {\n        self.0\n            .headers()\n            .get(\"Location\")\n            .unwrap_or_else(|| self.0.url())\n    }\n\n    fn has_redirect(&self) -> bool {\n        self.0.headers().get(REDIRECT_HEADER).is_some()\n    }\n}\n"
  },
  {
    "path": "server_fn/src/response/generic.rs",
    "content": "//! This module uses platform-agnostic abstractions\n//! allowing users to run server functions on a wide range of\n//! platforms.\n//!\n//! The crates in use in this crate are:\n//!\n//! * `bytes`: platform-agnostic manipulation of bytes.\n//! * `http`: low-dependency HTTP abstractions' *front-end*.\n//!\n//! # Users\n//!\n//! * `wasm32-wasip*` integration crate `leptos_wasi` is using this\n//!   crate under the hood.\n\nuse super::{Res, TryRes};\nuse crate::error::{\n    FromServerFnError, IntoAppError, ServerFnErrorErr, ServerFnErrorWrapper,\n    SERVER_FN_ERROR_HEADER,\n};\nuse bytes::Bytes;\nuse futures::{Stream, TryStreamExt};\nuse http::{header, HeaderValue, Response, StatusCode};\nuse std::pin::Pin;\nuse throw_error::Error;\n\n/// The Body of a Response whose *execution model* can be\n/// customised using the variants.\npub enum Body {\n    /// The response body will be written synchronously.\n    Sync(Bytes),\n\n    /// The response body will be written asynchronously,\n    /// this execution model is also known as\n    /// \"streaming\".\n    Async(Pin<Box<dyn Stream<Item = Result<Bytes, Error>> + Send + 'static>>),\n}\n\nimpl From<String> for Body {\n    fn from(value: String) -> Self {\n        Body::Sync(Bytes::from(value))\n    }\n}\n\nimpl From<Bytes> for Body {\n    fn from(value: Bytes) -> Self {\n        Body::Sync(value)\n    }\n}\n\nimpl<E> TryRes<E> for Response<Body>\nwhere\n    E: Send + Sync + FromServerFnError,\n{\n    fn try_from_string(content_type: &str, data: String) -> Result<Self, E> {\n        let builder = http::Response::builder();\n        builder\n            .status(200)\n            .header(http::header::CONTENT_TYPE, content_type)\n            .body(data.into())\n            .map_err(|e| {\n                ServerFnErrorErr::Response(e.to_string()).into_app_error()\n            })\n    }\n\n    fn try_from_bytes(content_type: &str, data: Bytes) -> Result<Self, E> {\n        let builder = http::Response::builder();\n        builder\n            .status(200)\n            .header(http::header::CONTENT_TYPE, content_type)\n            .body(Body::Sync(data))\n            .map_err(|e| {\n                ServerFnErrorErr::Response(e.to_string()).into_app_error()\n            })\n    }\n\n    fn try_from_stream(\n        content_type: &str,\n        data: impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n    ) -> Result<Self, E> {\n        let builder = http::Response::builder();\n        builder\n            .status(200)\n            .header(http::header::CONTENT_TYPE, content_type)\n            .body(Body::Async(Box::pin(\n                data.map_err(|e| ServerFnErrorWrapper(E::de(e)))\n                    .map_err(Error::from),\n            )))\n            .map_err(|e| {\n                ServerFnErrorErr::Response(e.to_string()).into_app_error()\n            })\n    }\n}\n\nimpl Res for Response<Body> {\n    fn error_response(path: &str, err: Bytes) -> Self {\n        Response::builder()\n            .status(http::StatusCode::INTERNAL_SERVER_ERROR)\n            .header(SERVER_FN_ERROR_HEADER, path)\n            .body(err.into())\n            .unwrap()\n    }\n\n    fn content_type(&mut self, content_type: &str) {\n        if let Ok(content_type) = HeaderValue::from_str(content_type) {\n            self.headers_mut()\n                .insert(header::CONTENT_TYPE, content_type);\n        }\n    }\n\n    fn redirect(&mut self, path: &str) {\n        if let Ok(path) = HeaderValue::from_str(path) {\n            self.headers_mut().insert(header::LOCATION, path);\n            *self.status_mut() = StatusCode::FOUND;\n        }\n    }\n}\n"
  },
  {
    "path": "server_fn/src/response/http.rs",
    "content": "use super::{Res, TryRes};\nuse crate::error::{\n    FromServerFnError, IntoAppError, ServerFnErrorErr, ServerFnErrorWrapper,\n    SERVER_FN_ERROR_HEADER,\n};\nuse axum::body::Body;\nuse bytes::Bytes;\nuse futures::{Stream, TryStreamExt};\nuse http::{header, HeaderValue, Response, StatusCode};\n\nimpl<E> TryRes<E> for Response<Body>\nwhere\n    E: Send + Sync + FromServerFnError,\n{\n    fn try_from_string(content_type: &str, data: String) -> Result<Self, E> {\n        let builder = http::Response::builder();\n        builder\n            .status(200)\n            .header(http::header::CONTENT_TYPE, content_type)\n            .body(Body::from(data))\n            .map_err(|e| {\n                ServerFnErrorErr::Response(e.to_string()).into_app_error()\n            })\n    }\n\n    fn try_from_bytes(content_type: &str, data: Bytes) -> Result<Self, E> {\n        let builder = http::Response::builder();\n        builder\n            .status(200)\n            .header(http::header::CONTENT_TYPE, content_type)\n            .body(Body::from(data))\n            .map_err(|e| {\n                ServerFnErrorErr::Response(e.to_string()).into_app_error()\n            })\n    }\n\n    fn try_from_stream(\n        content_type: &str,\n        data: impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n    ) -> Result<Self, E> {\n        let body =\n            Body::from_stream(data.map_err(|e| ServerFnErrorWrapper(E::de(e))));\n        let builder = http::Response::builder();\n        builder\n            .status(200)\n            .header(http::header::CONTENT_TYPE, content_type)\n            .body(body)\n            .map_err(|e| {\n                ServerFnErrorErr::Response(e.to_string()).into_app_error()\n            })\n    }\n}\n\nimpl Res for Response<Body> {\n    fn error_response(path: &str, err: Bytes) -> Self {\n        Response::builder()\n            .status(http::StatusCode::INTERNAL_SERVER_ERROR)\n            .header(SERVER_FN_ERROR_HEADER, path)\n            .body(err.into())\n            .unwrap()\n    }\n\n    fn content_type(&mut self, content_type: &str) {\n        if let Ok(content_type) = HeaderValue::from_str(content_type) {\n            self.headers_mut()\n                .insert(header::CONTENT_TYPE, content_type);\n        }\n    }\n\n    fn redirect(&mut self, path: &str) {\n        if let Ok(path) = HeaderValue::from_str(path) {\n            self.headers_mut().insert(header::LOCATION, path);\n            *self.status_mut() = StatusCode::FOUND;\n        }\n    }\n}\n"
  },
  {
    "path": "server_fn/src/response/mod.rs",
    "content": "/// Response types for Actix.\n#[cfg(feature = \"actix-no-default\")]\npub mod actix;\n/// Response types for the browser.\n#[cfg(feature = \"browser\")]\npub mod browser;\n#[cfg(feature = \"generic\")]\npub mod generic;\n/// Response types for Axum.\n#[cfg(feature = \"axum-no-default\")]\npub mod http;\n/// Response types for [`reqwest`].\n#[cfg(feature = \"reqwest\")]\npub mod reqwest;\n\nuse bytes::Bytes;\nuse futures::Stream;\nuse std::future::Future;\n\n/// Represents the response as created by the server;\npub trait TryRes<E>\nwhere\n    Self: Sized,\n{\n    /// Attempts to convert a UTF-8 string into an HTTP response.\n    fn try_from_string(content_type: &str, data: String) -> Result<Self, E>;\n\n    /// Attempts to convert a binary blob represented as bytes into an HTTP response.\n    fn try_from_bytes(content_type: &str, data: Bytes) -> Result<Self, E>;\n\n    /// Attempts to convert a stream of bytes into an HTTP response.\n    fn try_from_stream(\n        content_type: &str,\n        data: impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,\n    ) -> Result<Self, E>;\n}\n\n/// Represents the response as created by the server;\npub trait Res {\n    /// Converts an error into a response, with a `500` status code and the error as its body.\n    fn error_response(path: &str, err: Bytes) -> Self;\n    /// Set the `Content-Type` header for the response.\n    fn content_type(&mut self, #[allow(unused_variables)] content_type: &str) {\n        // TODO 0.9: remove this method and default implementation. It is only included here\n        //  to allow setting the `Content-Type` header for error responses without requiring a\n        //  semver-incompatible change.\n    }\n    /// Redirect the response by setting a 302 code and Location header.\n    fn redirect(&mut self, path: &str);\n}\n\n/// Represents the response as received by the client.\npub trait ClientRes<E> {\n    /// Attempts to extract a UTF-8 string from an HTTP response.\n    fn try_into_string(self) -> impl Future<Output = Result<String, E>> + Send;\n\n    /// Attempts to extract a binary blob from an HTTP response.\n    fn try_into_bytes(self) -> impl Future<Output = Result<Bytes, E>> + Send;\n\n    /// Attempts to extract a binary stream from an HTTP response.\n    fn try_into_stream(\n        self,\n    ) -> Result<\n        impl Stream<Item = Result<Bytes, Bytes>> + Send + Sync + 'static,\n        E,\n    >;\n\n    /// HTTP status code of the response.\n    fn status(&self) -> u16;\n\n    /// Status text for the status code.\n    fn status_text(&self) -> String;\n\n    /// The `Location` header or (if none is set), the URL of the response.\n    fn location(&self) -> String;\n\n    /// Whether the response has the [`REDIRECT_HEADER`](crate::redirect::REDIRECT_HEADER) set.\n    fn has_redirect(&self) -> bool;\n}\n\n/// A mocked response type that can be used in place of the actual server response,\n/// when compiling for the browser.\n///\n/// ## Panics\n/// This always panics if its methods are called. It is used solely to stub out the\n/// server response type when compiling for the client.\npub struct BrowserMockRes;\n\nimpl<E> TryRes<E> for BrowserMockRes {\n    fn try_from_string(_content_type: &str, _data: String) -> Result<Self, E> {\n        unreachable!()\n    }\n\n    fn try_from_bytes(_content_type: &str, _data: Bytes) -> Result<Self, E> {\n        unreachable!()\n    }\n\n    fn try_from_stream(\n        _content_type: &str,\n        _data: impl Stream<Item = Result<Bytes, Bytes>>,\n    ) -> Result<Self, E> {\n        unreachable!()\n    }\n}\n\nimpl Res for BrowserMockRes {\n    fn error_response(_path: &str, _err: Bytes) -> Self {\n        unreachable!()\n    }\n\n    fn content_type(&mut self, _content_type: &str) {\n        unreachable!()\n    }\n\n    fn redirect(&mut self, _path: &str) {\n        unreachable!()\n    }\n}\n"
  },
  {
    "path": "server_fn/src/response/reqwest.rs",
    "content": "use super::ClientRes;\nuse crate::error::{FromServerFnError, IntoAppError, ServerFnErrorErr};\nuse bytes::Bytes;\nuse futures::{Stream, TryStreamExt};\nuse reqwest::Response;\n\nimpl<E: FromServerFnError> ClientRes<E> for Response {\n    async fn try_into_string(self) -> Result<String, E> {\n        self.text().await.map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })\n    }\n\n    async fn try_into_bytes(self) -> Result<Bytes, E> {\n        self.bytes().await.map_err(|e| {\n            ServerFnErrorErr::Deserialization(e.to_string()).into_app_error()\n        })\n    }\n\n    fn try_into_stream(\n        self,\n    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static, E>\n    {\n        Ok(self.bytes_stream().map_err(|e| {\n            E::from_server_fn_error(ServerFnErrorErr::Response(e.to_string()))\n                .ser()\n        }))\n    }\n\n    fn status(&self) -> u16 {\n        self.status().as_u16()\n    }\n\n    fn status_text(&self) -> String {\n        self.status().to_string()\n    }\n\n    fn location(&self) -> String {\n        self.headers()\n            .get(\"Location\")\n            .map(|value| String::from_utf8_lossy(value.as_bytes()).to_string())\n            .unwrap_or_else(|| self.url().to_string())\n    }\n\n    fn has_redirect(&self) -> bool {\n        self.headers().get(\"Location\").is_some()\n    }\n}\n"
  },
  {
    "path": "server_fn/src/server.rs",
    "content": "use crate::{\n    request::Req,\n    response::{Res, TryRes},\n};\nuse std::future::Future;\n\n/// A server defines a pair of request/response types and the logic to spawn\n/// an async task.\n///\n/// This trait is implemented for any server backend for server functions including\n/// `axum` and `actix-web`. It should almost never be necessary to implement it\n/// yourself, unless you’re trying to use an alternative HTTP server.\npub trait Server<Error, InputStreamError = Error, OutputStreamError = Error> {\n    /// The type of the HTTP request when received by the server function on the server side.\n    type Request: Req<\n            Error,\n            InputStreamError,\n            OutputStreamError,\n            WebsocketResponse = Self::Response,\n        > + Send\n        + 'static;\n\n    /// The type of the HTTP response returned by the server function on the server side.\n    type Response: Res + TryRes<Error> + Send + 'static;\n\n    /// Spawn an async task on the server.\n    fn spawn(\n        future: impl Future<Output = ()> + Send + 'static,\n    ) -> Result<(), Error>;\n}\n"
  },
  {
    "path": "server_fn/tests/invalid/aliased_return_full.rs",
    "content": "use server_fn_macro_default::server;\n\n#[derive(Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize)]\npub enum InvalidError {\n    #[error(\"error a\")]\n    A,\n}\n\ntype FullAlias = Result<String, InvalidError>;\n\n#[server]\npub async fn full_alias_result() -> FullAlias {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/invalid/aliased_return_full.stderr",
    "content": "error[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_full.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_full.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\n   = note: required for `BrowserClient` to implement `Client<InvalidError>`\nnote: required by a bound in `server_fn::ServerFn::Client`\n  --> src/lib.rs\n   |\n   |       type Client: Client<\n   |  __________________^\n   | |         Self::Error,\n   | |         Self::InputStreamError,\n   | |         Self::OutputStreamError,\n   | |     >;\n   | |_____^ required by this bound in `ServerFn::Client`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_full.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_full.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\n   = note: required for `BrowserClient` to implement `Client<InvalidError>`\n   = note: required for `Http<PostUrl, Post<JsonEncoding>>` to implement `Protocol<FullAliasResult, String, BrowserClient, BrowserMockServer, InvalidError>`\nnote: required by a bound in `server_fn::ServerFn::Protocol`\n  --> src/lib.rs\n   |\n   |       type Protocol: Protocol<\n   |  ____________________^\n   | |         Self,\n   | |         Self::Output,\n   | |         Self::Client,\n...  |\n   | |         Self::OutputStreamError,\n   | |     >;\n   | |_____^ required by this bound in `ServerFn::Protocol`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_full.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_full.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::Error`\n  --> src/lib.rs\n   |\n   |     type Error: FromServerFnError + Send + Sync;\n   |                 ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::Error`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_full.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_full.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::InputStreamError`\n  --> src/lib.rs\n   |\n   |     type InputStreamError: FromServerFnError + Send + Sync;\n   |                            ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::InputStreamError`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_full.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_full.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::OutputStreamError`\n  --> src/lib.rs\n   |\n   |     type OutputStreamError: FromServerFnError + Send + Sync;\n   |                             ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::OutputStreamError`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "server_fn/tests/invalid/aliased_return_none.rs",
    "content": "use server_fn_macro_default::server;\n\n#[derive(Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize)]\npub enum InvalidError {\n    #[error(\"error a\")]\n    A,\n}\n\n#[server]\npub async fn no_alias_result() -> Result<String, InvalidError> {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/invalid/aliased_return_none.stderr",
    "content": "error[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n --> tests/invalid/aliased_return_none.rs:9:1\n  |\n9 | #[server]\n  | ^^^^^^^^^ unsatisfied trait bound\n  |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n --> tests/invalid/aliased_return_none.rs:4:1\n  |\n4 | pub enum InvalidError {\n  | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n --> src/error.rs\n  |\n  | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n  | | where\n  | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n  | |___________________________________________________________^\n  = note: required for `BrowserClient` to implement `Client<InvalidError>`\nnote: required by a bound in `server_fn::ServerFn::Client`\n --> src/lib.rs\n  |\n  |       type Client: Client<\n  |  __________________^\n  | |         Self::Error,\n  | |         Self::InputStreamError,\n  | |         Self::OutputStreamError,\n  | |     >;\n  | |_____^ required by this bound in `ServerFn::Client`\n  = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n --> tests/invalid/aliased_return_none.rs:9:1\n  |\n9 | #[server]\n  | ^^^^^^^^^ unsatisfied trait bound\n  |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n --> tests/invalid/aliased_return_none.rs:4:1\n  |\n4 | pub enum InvalidError {\n  | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n --> src/error.rs\n  |\n  | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n  | | where\n  | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n  | |___________________________________________________________^\n  = note: required for `BrowserClient` to implement `Client<InvalidError>`\n  = note: required for `Http<PostUrl, Post<JsonEncoding>>` to implement `Protocol<NoAliasResult, String, BrowserClient, BrowserMockServer, InvalidError>`\nnote: required by a bound in `server_fn::ServerFn::Protocol`\n --> src/lib.rs\n  |\n  |       type Protocol: Protocol<\n  |  ____________________^\n  | |         Self,\n  | |         Self::Output,\n  | |         Self::Client,\n... |\n  | |         Self::OutputStreamError,\n  | |     >;\n  | |_____^ required by this bound in `ServerFn::Protocol`\n  = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_none.rs:10:50\n   |\n10 | pub async fn no_alias_result() -> Result<String, InvalidError> {\n   |                                                  ^^^^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_none.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::Error`\n  --> src/lib.rs\n   |\n   |     type Error: FromServerFnError + Send + Sync;\n   |                 ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::Error`\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_none.rs:10:50\n   |\n10 | pub async fn no_alias_result() -> Result<String, InvalidError> {\n   |                                                  ^^^^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_none.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::InputStreamError`\n  --> src/lib.rs\n   |\n   |     type InputStreamError: FromServerFnError + Send + Sync;\n   |                            ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::InputStreamError`\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_none.rs:10:50\n   |\n10 | pub async fn no_alias_result() -> Result<String, InvalidError> {\n   |                                                  ^^^^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_none.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::OutputStreamError`\n  --> src/lib.rs\n   |\n   |     type OutputStreamError: FromServerFnError + Send + Sync;\n   |                             ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::OutputStreamError`\n"
  },
  {
    "path": "server_fn/tests/invalid/aliased_return_part.rs",
    "content": "use server_fn_macro_default::server;\n\n#[derive(Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize)]\npub enum InvalidError {\n    #[error(\"error a\")]\n    A,\n}\n\ntype PartAlias<T> = Result<T, InvalidError>;\n\n#[server]\npub async fn part_alias_result() -> PartAlias<String> {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/invalid/aliased_return_part.stderr",
    "content": "error[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_part.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_part.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\n   = note: required for `BrowserClient` to implement `Client<InvalidError>`\nnote: required by a bound in `server_fn::ServerFn::Client`\n  --> src/lib.rs\n   |\n   |       type Client: Client<\n   |  __________________^\n   | |         Self::Error,\n   | |         Self::InputStreamError,\n   | |         Self::OutputStreamError,\n   | |     >;\n   | |_____^ required by this bound in `ServerFn::Client`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_part.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_part.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\n   = note: required for `BrowserClient` to implement `Client<InvalidError>`\n   = note: required for `Http<PostUrl, Post<JsonEncoding>>` to implement `Protocol<PartAliasResult, String, BrowserClient, BrowserMockServer, InvalidError>`\nnote: required by a bound in `server_fn::ServerFn::Protocol`\n  --> src/lib.rs\n   |\n   |       type Protocol: Protocol<\n   |  ____________________^\n   | |         Self,\n   | |         Self::Output,\n   | |         Self::Client,\n...  |\n   | |         Self::OutputStreamError,\n   | |     >;\n   | |_____^ required by this bound in `ServerFn::Protocol`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_part.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_part.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::Error`\n  --> src/lib.rs\n   |\n   |     type Error: FromServerFnError + Send + Sync;\n   |                 ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::Error`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_part.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_part.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::InputStreamError`\n  --> src/lib.rs\n   |\n   |     type InputStreamError: FromServerFnError + Send + Sync;\n   |                            ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::InputStreamError`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `InvalidError: FromServerFnError` is not satisfied\n  --> tests/invalid/aliased_return_part.rs:11:1\n   |\n11 | #[server]\n   | ^^^^^^^^^ unsatisfied trait bound\n   |\nhelp: the trait `FromServerFnError` is not implemented for `InvalidError`\n  --> tests/invalid/aliased_return_part.rs:4:1\n   |\n 4 | pub enum InvalidError {\n   | ^^^^^^^^^^^^^^^^^^^^^\nhelp: the trait `FromServerFnError` is implemented for `ServerFnError<CustErr>`\n  --> src/error.rs\n   |\n   | / impl<CustErr> FromServerFnError for ServerFnError<CustErr>\n   | | where\n   | |     CustErr: std::fmt::Debug + Display + FromStr + 'static,\n   | |___________________________________________________________^\nnote: required by a bound in `server_fn::ServerFn::OutputStreamError`\n  --> src/lib.rs\n   |\n   |     type OutputStreamError: FromServerFnError + Send + Sync;\n   |                             ^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::OutputStreamError`\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "server_fn/tests/invalid/empty_return.rs",
    "content": "use server_fn_macro_default::server;\n\n#[server]\npub async fn empty_return() -> () {\n    ()\n}\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/invalid/empty_return.stderr",
    "content": "error[E0277]: () is not a `Result` or aliased `Result`. Server functions must return a `Result` or aliased `Result`.\n --> tests/invalid/empty_return.rs:3:1\n  |\n3 | #[server]\n  | ^^^^^^^^^ Must return a `Result` or aliased `Result`.\n  |\n  = help: the trait `server_fn::error::ServerFnMustReturnResult` is not implemented for `()`\n  = note: If you are trying to return an alias of `Result`, you must also implement `FromServerFnError` for the error type.\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is implemented for `Result<T, E>`\n --> src/error.rs\n  |\n  | impl<T, E> ServerFnMustReturnResult for Result<T, E> {\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: () is not a `Result` or aliased `Result`. Server functions must return a `Result` or aliased `Result`.\n --> tests/invalid/empty_return.rs:4:32\n  |\n4 | pub async fn empty_return() -> () {\n  |                                ^^ Must return a `Result` or aliased `Result`.\n  |\n  = help: the trait `server_fn::error::ServerFnMustReturnResult` is not implemented for `()`\n  = note: If you are trying to return an alias of `Result`, you must also implement `FromServerFnError` for the error type.\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is implemented for `Result<T, E>`\n --> src/error.rs\n  |\n  | impl<T, E> ServerFnMustReturnResult for Result<T, E> {\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror[E0271]: expected `impl Future<Output = ()>` to be a future that resolves to `Result<_, _>`, but it resolves to `()`\n --> tests/invalid/empty_return.rs:4:32\n  |\n4 | pub async fn empty_return() -> () {\n  |                                ^^ expected `Result<_, _>`, found `()`\n  |\n  = note:   expected enum `Result<_, _>`\n          found unit type `()`\nnote: required by a bound in `ServerFn::run_body::{anon_assoc#0}`\n --> src/lib.rs\n  |\n  |     ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send;\n  |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::run_body::{anon_assoc#0}`\n\nerror[E0277]: () is not a `Result` or aliased `Result`. Server functions must return a `Result` or aliased `Result`.\n --> tests/invalid/empty_return.rs:3:1\n  |\n3 | #[server]\n  | ^^^^^^^^^ Must return a `Result` or aliased `Result`.\n  |\n  = help: the trait `server_fn::error::ServerFnMustReturnResult` is not implemented for `()`\n  = note: If you are trying to return an alias of `Result`, you must also implement `FromServerFnError` for the error type.\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is implemented for `Result<T, E>`\n --> src/error.rs\n  |\n  | impl<T, E> ServerFnMustReturnResult for Result<T, E> {\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror[E0308]: mismatched types\n --> tests/invalid/empty_return.rs:3:1\n  |\n3 | #[server]\n  | ^^^^^^^^^ expected `()`, found `Result<_, _>`\n  |\n  = note: expected unit type `()`\n                  found enum `Result<_, _>`\nhelp: consider using `Result::expect` to unwrap the `Result<_, _>` value, panicking if the value is a `Result::Err`\n  |\n3 | #[server].expect(\"REASON\")\n  |          +++++++++++++++++\n"
  },
  {
    "path": "server_fn/tests/invalid/no_return.rs",
    "content": "use server_fn_macro_default::server;\n\n#[server]\npub async fn no_return() {}\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/invalid/no_return.stderr",
    "content": "error: expected `->`\n --> tests/invalid/no_return.rs:4:26\n  |\n4 | pub async fn no_return() {}\n  |                          ^\n"
  },
  {
    "path": "server_fn/tests/invalid/not_async.rs",
    "content": "use server_fn_macro_default::server;\nuse server_fn::error::ServerFnError;\n\n#[server]\npub fn not_async() -> Result<String, ServerFnError> {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/invalid/not_async.stderr",
    "content": "error: expected `async`\n --> tests/invalid/not_async.rs:5:5\n  |\n5 | pub fn not_async() -> Result<String, ServerFnError> {\n  |     ^^\n\nwarning: unused import: `server_fn::error::ServerFnError`\n --> tests/invalid/not_async.rs:2:5\n  |\n2 | use server_fn::error::ServerFnError;\n  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  |\n  = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n"
  },
  {
    "path": "server_fn/tests/invalid/not_result.rs",
    "content": "use server_fn::{\n    codec::JsonEncoding,\n    error::{FromServerFnError, ServerFnErrorErr},\n};\nuse server_fn_macro_default::server;\n\n#[derive(\n    Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize,\n)]\npub enum CustomError {\n    #[error(\"error a\")]\n    A,\n    #[error(\"error b\")]\n    B,\n}\n\nimpl FromServerFnError for CustomError {\n    type Encoder = JsonEncoding;\n\n    fn from_server_fn_error(_: ServerFnErrorErr) -> Self {\n        Self::A\n    }\n}\n\n#[server]\npub async fn full_alias_result() -> CustomError {\n    CustomError::A\n}\n\nfn main() {}\n"
  },
  {
    "path": "server_fn/tests/invalid/not_result.stderr",
    "content": "error[E0277]: CustomError is not a `Result` or aliased `Result`. Server functions must return a `Result` or aliased `Result`.\n  --> tests/invalid/not_result.rs:25:1\n   |\n25 | #[server]\n   | ^^^^^^^^^ Must return a `Result` or aliased `Result`.\n   |\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is not implemented for `CustomError`\n  --> tests/invalid/not_result.rs:10:1\n   |\n10 | pub enum CustomError {\n   | ^^^^^^^^^^^^^^^^^^^^\n   = note: If you are trying to return an alias of `Result`, you must also implement `FromServerFnError` for the error type.\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is implemented for `Result<T, E>`\n  --> src/error.rs\n   |\n   | impl<T, E> ServerFnMustReturnResult for Result<T, E> {\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n   = note: this error originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: CustomError is not a `Result` or aliased `Result`. Server functions must return a `Result` or aliased `Result`.\n  --> tests/invalid/not_result.rs:26:37\n   |\n26 | pub async fn full_alias_result() -> CustomError {\n   |                                     ^^^^^^^^^^^ Must return a `Result` or aliased `Result`.\n   |\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is not implemented for `CustomError`\n  --> tests/invalid/not_result.rs:10:1\n   |\n10 | pub enum CustomError {\n   | ^^^^^^^^^^^^^^^^^^^^\n   = note: If you are trying to return an alias of `Result`, you must also implement `FromServerFnError` for the error type.\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is implemented for `Result<T, E>`\n  --> src/error.rs\n   |\n   | impl<T, E> ServerFnMustReturnResult for Result<T, E> {\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror[E0271]: expected `impl Future<Output = CustomError>` to be a future that resolves to `Result<_, _>`, but it resolves to `CustomError`\n  --> tests/invalid/not_result.rs:26:37\n   |\n26 | pub async fn full_alias_result() -> CustomError {\n   |                                     ^^^^^^^^^^^ expected `Result<_, _>`, found `CustomError`\n   |\n   = note: expected enum `Result<_, _>`\n              found enum `CustomError`\nnote: required by a bound in `ServerFn::run_body::{anon_assoc#0}`\n  --> src/lib.rs\n   |\n   |     ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send;\n   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ServerFn::run_body::{anon_assoc#0}`\n\nerror[E0277]: CustomError is not a `Result` or aliased `Result`. Server functions must return a `Result` or aliased `Result`.\n  --> tests/invalid/not_result.rs:25:1\n   |\n25 | #[server]\n   | ^^^^^^^^^ Must return a `Result` or aliased `Result`.\n   |\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is not implemented for `CustomError`\n  --> tests/invalid/not_result.rs:10:1\n   |\n10 | pub enum CustomError {\n   | ^^^^^^^^^^^^^^^^^^^^\n   = note: If you are trying to return an alias of `Result`, you must also implement `FromServerFnError` for the error type.\nhelp: the trait `server_fn::error::ServerFnMustReturnResult` is implemented for `Result<T, E>`\n  --> src/error.rs\n   |\n   | impl<T, E> ServerFnMustReturnResult for Result<T, E> {\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror[E0308]: mismatched types\n  --> tests/invalid/not_result.rs:25:1\n   |\n25 | #[server]\n   | ^^^^^^^^^ expected `CustomError`, found `Result<_, _>`\n   |\n   = note: expected enum `CustomError`\n              found enum `Result<_, _>`\nhelp: consider using `Result::expect` to unwrap the `Result<_, _>` value, panicking if the value is a `Result::Err`\n   |\n25 | #[server].expect(\"REASON\")\n   |          +++++++++++++++++\n"
  },
  {
    "path": "server_fn/tests/server_macro.rs",
    "content": "// The trybuild output has slightly different error message output for\n// different combinations of features. Since tests are run with `test-all-features`\n// multiple combinations of features are tested. This ensures this file is only\n// run when **only** the browser feature is enabled.\n#![cfg(all(\n    rustc_nightly,\n    feature = \"browser\",\n    not(any(\n        feature = \"postcard\",\n        feature = \"multipart\",\n        feature = \"serde-lite\",\n        feature = \"cbor\",\n        feature = \"msgpack\",\n        feature = \"bitcode\",\n        feature = \"bitcode-serde\",\n    ))\n))]\n\n#[test]\nfn aliased_results() {\n    let t = trybuild::TestCases::new();\n    t.pass(\"tests/valid/*.rs\");\n    t.compile_fail(\"tests/invalid/*.rs\")\n}\n"
  },
  {
    "path": "server_fn/tests/valid/aliased_return_full.rs",
    "content": "use server_fn_macro_default::server;\nuse server_fn::error::ServerFnError;\n\ntype FullAlias = Result<String, ServerFnError>;\n\n#[server]\npub async fn full_alias_result() -> FullAlias {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/valid/aliased_return_none.rs",
    "content": "use server_fn_macro_default::server;\nuse server_fn::error::ServerFnError;\n\n#[server]\npub async fn no_alias_result() -> Result<String, ServerFnError> {\n    Ok(\"hello\".to_string())\n}\n\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/valid/aliased_return_part.rs",
    "content": "use server_fn_macro_default::server;\nuse server_fn::error::ServerFnError;\n\ntype PartAlias<T> = Result<T, ServerFnError>;\n\n#[server]\npub async fn part_alias_result() -> PartAlias<String> {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}"
  },
  {
    "path": "server_fn/tests/valid/custom_error_aliased_return_full.rs",
    "content": "use server_fn::{\n    codec::JsonEncoding,\n    error::{FromServerFnError, ServerFnErrorErr},\n};\nuse server_fn_macro_default::server;\n\n#[derive(\n    Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize,\n)]\npub enum CustomError {\n    #[error(\"error a\")]\n    ErrorA,\n    #[error(\"error b\")]\n    ErrorB,\n}\n\nimpl FromServerFnError for CustomError {\n    type Encoder = JsonEncoding;\n\n    fn from_server_fn_error(_: ServerFnErrorErr) -> Self {\n        Self::ErrorA\n    }\n}\n\ntype FullAlias = Result<String, CustomError>;\n\n#[server]\npub async fn full_alias_result() -> FullAlias {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}\n"
  },
  {
    "path": "server_fn/tests/valid/custom_error_aliased_return_none.rs",
    "content": "use server_fn::{\n    codec::JsonEncoding,\n    error::{FromServerFnError, ServerFnErrorErr},\n};\nuse server_fn_macro_default::server;\n\n#[derive(\n    Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize,\n)]\npub enum CustomError {\n    #[error(\"error a\")]\n    ErrorA,\n    #[error(\"error b\")]\n    ErrorB,\n}\n\nimpl FromServerFnError for CustomError {\n    type Encoder = JsonEncoding;\n\n    fn from_server_fn_error(_: ServerFnErrorErr) -> Self {\n        Self::ErrorA\n    }\n}\n\n#[server]\npub async fn no_alias_result() -> Result<String, CustomError> {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}\n"
  },
  {
    "path": "server_fn/tests/valid/custom_error_aliased_return_part.rs",
    "content": "use server_fn::{\n    codec::JsonEncoding,\n    error::{FromServerFnError, ServerFnErrorErr},\n};\nuse server_fn_macro_default::server;\n\n#[derive(\n    Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize,\n)]\npub enum CustomError {\n    #[error(\"error a\")]\n    ErrorA,\n    #[error(\"error b\")]\n    ErrorB,\n}\n\nimpl FromServerFnError for CustomError {\n    type Encoder = JsonEncoding;\n\n    fn from_server_fn_error(_: ServerFnErrorErr) -> Self {\n        Self::ErrorA\n    }\n}\n\ntype PartAlias<T> = Result<T, CustomError>;\n\n#[server]\npub async fn part_alias_result() -> PartAlias<String> {\n    Ok(\"hello\".to_string())\n}\n\nfn main() {}\n"
  },
  {
    "path": "server_fn_macro/Cargo.toml",
    "content": "[package]\nname = \"server_fn_macro\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"RPC for any web framework.\"\nreadme = \"../README.md\"\nversion = \"0.8.10\"\nedition.workspace = true\n\n[dependencies]\nquote = { workspace = true, default-features = true }\nsyn = { features = [\n  \"full\",\n  \"parsing\",\n  \"extra-traits\",\n], workspace = true, default-features = true }\nproc-macro2 = { workspace = true, default-features = true }\nxxhash-rust = { features = [\n  \"const_xxh64\",\n], workspace = true, default-features = true }\nconst_format = { workspace = true, default-features = true }\nconvert_case = { workspace = true, default-features = true }\n\n\n[build-dependencies]\nrustc_version = { workspace = true, default-features = true }\n\n\n[features]\nnightly = []\nssr = []\nactix = []\naxum = []\ngeneric = []\nreqwest = []\n\n[package.metadata.docs.rs]\nall-features = true\nrustdoc-args = [\"--generate-link-to-definition\"]\n\n[package.metadata.cargo-all-features]\nmax_combination_size = 2\nskip_feature_sets = [[\"nightly\"]]\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(rustc_nightly)'] }\n"
  },
  {
    "path": "server_fn_macro/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "server_fn_macro/build.rs",
    "content": "use rustc_version::{version_meta, Channel};\n\nfn main() {\n    // Set cfg flags depending on release channel\n    if matches!(version_meta().unwrap().channel, Channel::Nightly) {\n        println!(\"cargo:rustc-cfg=rustc_nightly\");\n    }\n}\n"
  },
  {
    "path": "server_fn_macro/src/lib.rs",
    "content": "#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(proc_macro_span))]\n#![forbid(unsafe_code)]\n#![deny(missing_docs)]\n\n//! Implementation of the `server_fn` macro.\n//!\n//! This crate contains the implementation of the `server_fn` macro. [`server_macro_impl`] can be used to implement custom versions of the macro for different frameworks that allow users to pass a custom context from the server to the server function.\n\nuse convert_case::{Case, Converter};\nuse proc_macro2::{Span, TokenStream as TokenStream2};\nuse quote::{format_ident, quote, quote_spanned, ToTokens};\nuse syn::{\n    parse::{Parse, ParseStream},\n    punctuated::Punctuated,\n    spanned::Spanned,\n    *,\n};\n\n/// A parsed server function call.\npub struct ServerFnCall {\n    args: ServerFnArgs,\n    body: ServerFnBody,\n    default_path: String,\n    server_fn_path: Option<Path>,\n    preset_server: Option<Type>,\n    default_protocol: Option<Type>,\n    default_input_encoding: Option<Type>,\n    default_output_encoding: Option<Type>,\n}\n\nimpl ServerFnCall {\n    /// Parse the arguments of a server function call.\n    ///\n    /// ```ignore\n    /// #[proc_macro_attribute]\n    /// pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n    ///     match ServerFnCall::parse(\n    ///         \"/api\",\n    ///         args.into(),\n    ///         s.into(),\n    ///     ) {\n    ///         Err(e) => e.to_compile_error().into(),\n    ///         Ok(s) => s.to_token_stream().into(),\n    ///     }\n    /// }\n    /// ```\n    pub fn parse(\n        default_path: &str,\n        args: TokenStream2,\n        body: TokenStream2,\n    ) -> Result<Self> {\n        let args = syn::parse2(args)?;\n        let body = syn::parse2(body)?;\n        let mut myself = ServerFnCall {\n            default_path: default_path.into(),\n            args,\n            body,\n            server_fn_path: None,\n            preset_server: None,\n            default_protocol: None,\n            default_input_encoding: None,\n            default_output_encoding: None,\n        };\n\n        // We need to make the server function body send if actix is enabled. To\n        // do that, we wrap the body in a SendWrapper, which is an async fn that\n        // asserts that the future is always polled from the same thread.\n        if cfg!(feature = \"actix\") {\n            let server_fn_path = myself.server_fn_path();\n            let block = myself.body.block.to_token_stream();\n            myself.body.block = quote! {\n                {\n                    #server_fn_path::actix::SendWrapper::new(async move {\n                        #block\n                    })\n                    .await\n                }\n            };\n        }\n\n        Ok(myself)\n    }\n\n    /// Get a reference to the server function arguments.\n    pub fn get_args(&self) -> &ServerFnArgs {\n        &self.args\n    }\n\n    /// Get a mutable reference to the server function arguments.\n    pub fn get_args_mut(&mut self) -> &mut ServerFnArgs {\n        &mut self.args\n    }\n\n    /// Get a reference to the server function body.\n    pub fn get_body(&self) -> &ServerFnBody {\n        &self.body\n    }\n\n    /// Get a mutable reference to the server function body.\n    pub fn get_body_mut(&mut self) -> &mut ServerFnBody {\n        &mut self.body\n    }\n\n    /// Set the path to the server function crate.\n    pub fn default_server_fn_path(mut self, path: Option<Path>) -> Self {\n        self.server_fn_path = path;\n        self\n    }\n\n    /// Set the default server implementation.\n    pub fn default_server_type(mut self, server: Option<Type>) -> Self {\n        self.preset_server = server;\n        self\n    }\n\n    /// Set the default protocol.\n    pub fn default_protocol(mut self, protocol: Option<Type>) -> Self {\n        self.default_protocol = protocol;\n        self\n    }\n\n    /// Set the default input http encoding. This will be used by [`Self::protocol`]\n    /// if no protocol or default protocol is set or if only the output encoding is set\n    ///\n    /// Defaults to `PostUrl`\n    pub fn default_input_encoding(mut self, protocol: Option<Type>) -> Self {\n        self.default_input_encoding = protocol;\n        self\n    }\n\n    /// Set the default output http encoding. This will be used by [`Self::protocol`]\n    /// if no protocol or default protocol is set or if only the input encoding is set\n    ///\n    /// Defaults to `Json`\n    pub fn default_output_encoding(mut self, protocol: Option<Type>) -> Self {\n        self.default_output_encoding = protocol;\n        self\n    }\n\n    /// Get the client type to use for the server function.\n    pub fn client_type(&self) -> Type {\n        let server_fn_path = self.server_fn_path();\n        if let Some(client) = self.args.client.clone() {\n            client\n        } else if cfg!(feature = \"reqwest\") {\n            parse_quote! {\n                #server_fn_path::client::reqwest::ReqwestClient\n            }\n        } else {\n            parse_quote! {\n                #server_fn_path::client::browser::BrowserClient\n            }\n        }\n    }\n\n    /// Get the server type to use for the server function.\n    pub fn server_type(&self) -> Type {\n        let server_fn_path = self.server_fn_path();\n        if !cfg!(feature = \"ssr\") {\n            parse_quote! {\n                #server_fn_path::mock::BrowserMockServer\n            }\n        } else if cfg!(feature = \"axum\") {\n            parse_quote! {\n                #server_fn_path::axum::AxumServerFnBackend\n            }\n        } else if cfg!(feature = \"actix\") {\n            parse_quote! {\n                #server_fn_path::actix::ActixServerFnBackend\n            }\n        } else if cfg!(feature = \"generic\") {\n            parse_quote! {\n                #server_fn_path::axum::AxumServerFnBackend\n            }\n        } else if let Some(server) = &self.args.server {\n            server.clone()\n        } else if let Some(server) = &self.preset_server {\n            server.clone()\n        } else {\n            // fall back to the browser version, to avoid erroring out\n            // in things like doctests\n            // in reality, one of the above needs to be set\n            parse_quote! {\n                #server_fn_path::mock::BrowserMockServer\n            }\n        }\n    }\n\n    /// Get the path to the server_fn crate.\n    pub fn server_fn_path(&self) -> Path {\n        self.server_fn_path\n            .clone()\n            .unwrap_or_else(|| parse_quote! { server_fn })\n    }\n\n    /// Get the input http encoding if no protocol is set\n    fn input_http_encoding(&self) -> Type {\n        let server_fn_path = self.server_fn_path();\n        self.args\n            .input\n            .as_ref()\n            .map(|n| {\n                if self.args.builtin_encoding {\n                    parse_quote! { #server_fn_path::codec::#n }\n                } else {\n                    n.clone()\n                }\n            })\n            .unwrap_or_else(|| {\n                self.default_input_encoding.clone().unwrap_or_else(\n                    || parse_quote!(#server_fn_path::codec::PostUrl),\n                )\n            })\n    }\n\n    /// Get the output http encoding if no protocol is set\n    fn output_http_encoding(&self) -> Type {\n        let server_fn_path = self.server_fn_path();\n        self.args\n            .output\n            .as_ref()\n            .map(|n| {\n                if self.args.builtin_encoding {\n                    parse_quote! { #server_fn_path::codec::#n }\n                } else {\n                    n.clone()\n                }\n            })\n            .unwrap_or_else(|| {\n                self.default_output_encoding.clone().unwrap_or_else(\n                    || parse_quote!(#server_fn_path::codec::Json),\n                )\n            })\n    }\n\n    /// Get the http input and output encodings for the server function\n    /// if no protocol is set\n    pub fn http_encodings(&self) -> Option<(Type, Type)> {\n        self.args\n            .protocol\n            .is_none()\n            .then(|| (self.input_http_encoding(), self.output_http_encoding()))\n    }\n\n    /// Get the protocol to use for the server function.\n    pub fn protocol(&self) -> Type {\n        let server_fn_path = self.server_fn_path();\n        let default_protocol = &self.default_protocol;\n        self.args.protocol.clone().unwrap_or_else(|| {\n            // If both the input and output encodings are none,\n            // use the default protocol\n            if self.args.input.is_none() && self.args.output.is_none() {\n                default_protocol.clone().unwrap_or_else(|| {\n                    parse_quote! {\n                        #server_fn_path::Http<#server_fn_path::codec::PostUrl, #server_fn_path::codec::Json>\n                    }\n                })\n            } else {\n                // Otherwise use the input and output encodings, filling in\n                // defaults if necessary\n                let input = self.input_http_encoding();\n                let output = self.output_http_encoding();\n                parse_quote! {\n                    #server_fn_path::Http<#input, #output>\n                }\n            }\n        })\n    }\n\n    fn input_ident(&self) -> Option<String> {\n        match &self.args.input {\n            Some(Type::Path(path)) => {\n                path.path.segments.last().map(|seg| seg.ident.to_string())\n            }\n            None => Some(\"PostUrl\".to_string()),\n            _ => None,\n        }\n    }\n\n    fn websocket_protocol(&self) -> bool {\n        if let Type::Path(path) = self.protocol() {\n            path.path\n                .segments\n                .iter()\n                .any(|segment| segment.ident == \"Websocket\")\n        } else {\n            false\n        }\n    }\n\n    fn serde_path(&self) -> String {\n        let path = self\n            .server_fn_path()\n            .segments\n            .iter()\n            .map(|segment| segment.ident.to_string())\n            .collect::<Vec<_>>();\n        let path = path.join(\"::\");\n        format!(\"{path}::serde\")\n    }\n\n    /// Get the docs for the server function.\n    pub fn docs(&self) -> TokenStream2 {\n        // pass through docs from the function body\n        self.body\n            .docs\n            .iter()\n            .map(|(doc, span)| quote_spanned!(*span=> #[doc = #doc]))\n            .collect::<TokenStream2>()\n    }\n\n    fn fn_name_as_str(&self) -> String {\n        self.body.ident.to_string()\n    }\n\n    fn struct_tokens(&self) -> TokenStream2 {\n        let server_fn_path = self.server_fn_path();\n        let fn_name_as_str = self.fn_name_as_str();\n        let link_to_server_fn = format!(\n            \"Serialized arguments for the [`{fn_name_as_str}`] server \\\n             function.\\n\\n\"\n        );\n        let args_docs = quote! {\n            #[doc = #link_to_server_fn]\n        };\n\n        let docs = self.docs();\n\n        let input_ident = self.input_ident();\n\n        enum PathInfo {\n            Serde,\n            Rkyv,\n            Bitcode,\n            None,\n        }\n\n        let (path, derives) = match input_ident.as_deref() {\n            Some(\"Rkyv\") => (\n                PathInfo::Rkyv,\n                quote! {\n                    Clone, #server_fn_path::rkyv::Archive, #server_fn_path::rkyv::Serialize, #server_fn_path::rkyv::Deserialize\n                },\n            ),\n            Some(\"Bitcode\") => (\n                PathInfo::Bitcode,\n                quote! {\n                    Clone, #server_fn_path::bitcode::Encode, #server_fn_path::bitcode::Decode\n                },\n            ),\n            Some(\"MultipartFormData\")\n            | Some(\"Streaming\")\n            | Some(\"StreamingText\") => (PathInfo::None, quote! {}),\n            Some(\"SerdeLite\") => (\n                PathInfo::Serde,\n                quote! {\n                    Clone, #server_fn_path::serde_lite::Serialize, #server_fn_path::serde_lite::Deserialize\n                },\n            ),\n            _ => match &self.args.input_derive {\n                Some(derives) => {\n                    let d = &derives.elems;\n                    (PathInfo::None, quote! { #d })\n                }\n                None => {\n                    if self.websocket_protocol() {\n                        (PathInfo::None, quote! {})\n                    } else {\n                        (\n                            PathInfo::Serde,\n                            quote! {\n                                Clone, #server_fn_path::serde::Serialize, #server_fn_path::serde::Deserialize\n                            },\n                        )\n                    }\n                }\n            },\n        };\n        let addl_path = match path {\n            PathInfo::Serde => {\n                let serde_path = self.serde_path();\n                quote! {\n                    #[serde(crate = #serde_path)]\n                }\n            }\n            PathInfo::Bitcode => quote! {},\n            PathInfo::Rkyv => quote! {},\n            PathInfo::None => quote! {},\n        };\n\n        let lint_attrs = &self.body.lint_attrs;\n\n        let vis = &self.body.vis;\n        let struct_name = self.struct_name();\n        let fields = self\n            .body\n            .inputs\n            .iter()\n            .map(|server_fn_arg| {\n                let mut typed_arg = server_fn_arg.arg.clone();\n                // strip `mut`, which is allowed in fn args but not in struct fields\n                if let Pat::Ident(ident) = &mut *typed_arg.pat {\n                    ident.mutability = None;\n                }\n                let attrs = &server_fn_arg.server_fn_attributes;\n                quote! { #(#attrs ) * #vis #typed_arg }\n            })\n            .collect::<Vec<_>>();\n\n        quote! {\n            #args_docs\n            #docs\n            #[derive(Debug, #derives)]\n            #addl_path\n            #(#lint_attrs)*\n            #vis struct #struct_name {\n                #(#fields),*\n            }\n        }\n    }\n\n    /// Get the name of the server function struct that will be submitted to inventory.\n    ///\n    /// This will either be the name specified in the macro arguments or the PascalCase\n    /// version of the function name.\n    pub fn struct_name(&self) -> Ident {\n        // default to PascalCase version of function name if no struct name given\n        self.args.struct_name.clone().unwrap_or_else(|| {\n            let upper_camel_case_name = Converter::new()\n                .from_case(Case::Snake)\n                .to_case(Case::UpperCamel)\n                .convert(self.body.ident.to_string());\n            Ident::new(&upper_camel_case_name, self.body.ident.span())\n        })\n    }\n\n    /// Wrap the struct name in any custom wrapper specified in the macro arguments\n    /// and return it as a type\n    fn wrapped_struct_name(&self) -> TokenStream2 {\n        let struct_name = self.struct_name();\n        if let Some(wrapper) = self.args.custom_wrapper.as_ref() {\n            quote! { #wrapper<#struct_name> }\n        } else {\n            quote! { #struct_name }\n        }\n    }\n\n    /// Wrap the struct name in any custom wrapper specified in the macro arguments\n    /// and return it as a type with turbofish\n    fn wrapped_struct_name_turbofish(&self) -> TokenStream2 {\n        let struct_name = self.struct_name();\n        if let Some(wrapper) = self.args.custom_wrapper.as_ref() {\n            quote! { #wrapper::<#struct_name> }\n        } else {\n            quote! { #struct_name }\n        }\n    }\n\n    /// Generate the code to submit the server function type to inventory.\n    pub fn submit_to_inventory(&self) -> TokenStream2 {\n        // auto-registration with inventory\n        if cfg!(feature = \"ssr\") {\n            let server_fn_path = self.server_fn_path();\n            let wrapped_struct_name = self.wrapped_struct_name();\n            let wrapped_struct_name_turbofish =\n                self.wrapped_struct_name_turbofish();\n            quote! {\n                #server_fn_path::inventory::submit! {{\n                    use #server_fn_path::{ServerFn, codec::Encoding};\n                    #server_fn_path::ServerFnTraitObj::new::<#wrapped_struct_name>(\n                        |req| Box::pin(#wrapped_struct_name_turbofish::run_on_server(req)),\n                    )\n                }}\n            }\n        } else {\n            quote! {}\n        }\n    }\n\n    /// Generate the server function's URL. This will be the prefix path, then by the\n    /// module path if `SERVER_FN_MOD_PATH` is set, then the function name, and finally\n    /// a hash of the function name and location in the source code.\n    pub fn server_fn_url(&self) -> TokenStream2 {\n        let default_path = &self.default_path;\n        let prefix =\n            self.args.prefix.clone().unwrap_or_else(|| {\n                LitStr::new(default_path, Span::call_site())\n            });\n        let server_fn_path = self.server_fn_path();\n        let fn_path = self.args.fn_path.clone().map(|fn_path| {\n            let fn_path = fn_path.value();\n            // Remove any leading slashes, then add one slash back\n            let fn_path = \"/\".to_string() + fn_path.trim_start_matches('/');\n            fn_path\n        });\n\n        let enable_server_fn_mod_path =\n            option_env!(\"SERVER_FN_MOD_PATH\").is_some();\n        let mod_path = if enable_server_fn_mod_path {\n            quote! {\n                #server_fn_path::const_format::concatcp!(\n                    #server_fn_path::const_str::replace!(module_path!(), \"::\", \"/\"),\n                    \"/\"\n                )\n            }\n        } else {\n            quote! { \"\" }\n        };\n\n        let enable_hash = option_env!(\"DISABLE_SERVER_FN_HASH\").is_none();\n        let key_env_var = match option_env!(\"SERVER_FN_OVERRIDE_KEY\") {\n            Some(_) => \"SERVER_FN_OVERRIDE_KEY\",\n            None => \"CARGO_MANIFEST_DIR\",\n        };\n        let hash = if enable_hash {\n            quote! {\n                #server_fn_path::xxhash_rust::const_xxh64::xxh64(\n                    concat!(env!(#key_env_var), \":\", module_path!()).as_bytes(),\n                    0\n                )\n            }\n        } else {\n            quote! { \"\" }\n        };\n\n        let fn_name_as_str = self.fn_name_as_str();\n        if let Some(fn_path) = fn_path {\n            quote! {\n                #server_fn_path::const_format::concatcp!(\n                    #prefix,\n                    #mod_path,\n                    #fn_path\n                )\n            }\n        } else {\n            quote! {\n                #server_fn_path::const_format::concatcp!(\n                    #prefix,\n                    \"/\",\n                    #mod_path,\n                    #fn_name_as_str,\n                    #hash\n                )\n            }\n        }\n    }\n\n    /// Get the names of the fields the server function takes as inputs.\n    fn field_names(&self) -> Vec<&std::boxed::Box<syn::Pat>> {\n        self.body\n            .inputs\n            .iter()\n            .map(|f| &f.arg.pat)\n            .collect::<Vec<_>>()\n    }\n\n    /// Generate the implementation for the server function trait.\n    fn server_fn_impl(&self) -> TokenStream2 {\n        let server_fn_path = self.server_fn_path();\n        let struct_name = self.struct_name();\n\n        let protocol = self.protocol();\n        let middlewares = &self.body.middlewares;\n        let return_ty = &self.body.return_ty;\n        let output_ty = self.body.output_ty\n            .as_ref()\n            .map_or_else(|| {\n                quote! {\n                    <#return_ty as #server_fn_path::error::ServerFnMustReturnResult>::Ok\n                }\n            },\n            ToTokens::to_token_stream,\n        );\n        let error_ty = &self.body.error_ty;\n        let error_ty = error_ty\n            .as_ref()\n            .map_or_else(|| {\n                quote! {\n                    <#return_ty as #server_fn_path::error::ServerFnMustReturnResult>::Err\n                }\n            },\n            ToTokens::to_token_stream,\n        );\n        let error_ws_in_ty = if self.websocket_protocol() {\n            self.body\n                .error_ws_in_ty\n                .as_ref()\n                .map(ToTokens::to_token_stream)\n                .unwrap_or(error_ty.clone())\n        } else {\n            error_ty.clone()\n        };\n        let error_ws_out_ty = if self.websocket_protocol() {\n            self.body\n                .error_ws_out_ty\n                .as_ref()\n                .map(ToTokens::to_token_stream)\n                .unwrap_or(error_ty.clone())\n        } else {\n            error_ty.clone()\n        };\n        let field_names = self.field_names();\n\n        // run_body in the trait implementation\n        let run_body = if cfg!(feature = \"ssr\") {\n            let destructure =\n                if let Some(wrapper) = self.args.custom_wrapper.as_ref() {\n                    quote! {\n                        let #wrapper(#struct_name { #(#field_names),* }) = self;\n                    }\n                } else {\n                    quote! {\n                        let #struct_name { #(#field_names),* } = self;\n                    }\n                };\n            let dummy_name = self.body.to_dummy_ident();\n\n            // using the impl Future syntax here is thanks to Actix\n            //\n            // if we use Actix types inside the function, here, it becomes !Send\n            // so we need to add SendWrapper, because Actix won't actually send it anywhere\n            // but if we used SendWrapper in an async fn, the types don't work out because it\n            // becomes impl Future<Output = SendWrapper<_>>\n            //\n            // however, SendWrapper<Future<Output = T>> impls Future<Output = T>\n            let body = quote! {\n                async move {\n                    #destructure\n                    #dummy_name(#(#field_names),*).await\n                }\n            };\n            quote! {\n                // we need this for Actix, for the SendWrapper to count as impl Future\n                // but non-Actix will have a clippy warning otherwise\n                #[allow(clippy::manual_async_fn)]\n                fn run_body(self) -> impl std::future::Future<Output = #return_ty> + Send {\n                    #body\n                }\n            }\n        } else {\n            quote! {\n                #[allow(unused_variables)]\n                async fn run_body(self) -> #return_ty {\n                    unreachable!()\n                }\n            }\n        };\n        let client = self.client_type();\n\n        let server = self.server_type();\n\n        // generate the url of the server function\n        let path = self.server_fn_url();\n\n        let middlewares = if cfg!(feature = \"ssr\") {\n            quote! {\n                vec![\n                    #(\n                        std::sync::Arc::new(#middlewares)\n                    ),*\n                ]\n            }\n        } else {\n            quote! { vec![] }\n        };\n        let wrapped_struct_name = self.wrapped_struct_name();\n\n        quote! {\n            impl #server_fn_path::ServerFn for #wrapped_struct_name {\n                const PATH: &'static str = #path;\n\n                type Client = #client;\n                type Server = #server;\n                type Protocol = #protocol;\n                type Output = #output_ty;\n                type Error = #error_ty;\n                type InputStreamError = #error_ws_in_ty;\n                type OutputStreamError = #error_ws_out_ty;\n\n                fn middlewares() -> Vec<std::sync::Arc<dyn #server_fn_path::middleware::Layer<<Self::Server as #server_fn_path::server::Server<Self::Error>>::Request, <Self::Server as #server_fn_path::server::Server<Self::Error>>::Response>>> {\n                    #middlewares\n                }\n\n                #run_body\n            }\n        }\n    }\n\n    /// Return the name and type of the first field if there is only one field.\n    fn single_field(&self) -> Option<(&Pat, &Type)> {\n        self.body\n            .inputs\n            .first()\n            .filter(|_| self.body.inputs.len() == 1)\n            .map(|field| (&*field.arg.pat, &*field.arg.ty))\n    }\n\n    fn deref_impl(&self) -> TokenStream2 {\n        let impl_deref = self\n            .args\n            .impl_deref\n            .as_ref()\n            .map(|v| v.value)\n            .unwrap_or(true)\n            || self.websocket_protocol();\n        if !impl_deref {\n            return quote! {};\n        }\n        // if there's exactly one field, impl Deref<T> for the struct\n        let Some(single_field) = self.single_field() else {\n            return quote! {};\n        };\n        let struct_name = self.struct_name();\n        let (name, ty) = single_field;\n        quote! {\n            impl std::ops::Deref for #struct_name {\n                type Target = #ty;\n                fn deref(&self) -> &Self::Target {\n                    &self.#name\n                }\n            }\n        }\n    }\n\n    fn impl_from(&self) -> TokenStream2 {\n        let impl_from = self\n            .args\n            .impl_from\n            .as_ref()\n            .map(|v| v.value)\n            .unwrap_or(true)\n            || self.websocket_protocol();\n        if !impl_from {\n            return quote! {};\n        }\n        // if there's exactly one field, impl From<T> for the struct\n        let Some(single_field) = self.single_field() else {\n            return quote! {};\n        };\n        let struct_name = self.struct_name();\n        let (name, ty) = single_field;\n        quote! {\n            impl From<#struct_name> for #ty {\n                fn from(value: #struct_name) -> Self {\n                    let #struct_name { #name } = value;\n                    #name\n                }\n            }\n\n            impl From<#ty> for #struct_name {\n                fn from(#name: #ty) -> Self {\n                    #struct_name { #name }\n                }\n            }\n        }\n    }\n\n    fn func_tokens(&self) -> TokenStream2 {\n        let body = &self.body;\n        // default values for args\n        let struct_name = self.struct_name();\n\n        // build struct for type\n        let fn_name = &body.ident;\n        let attrs = &body.attrs;\n        let lint_attrs = &body.lint_attrs;\n\n        let fn_args = body.inputs.iter().map(|f| &f.arg).collect::<Vec<_>>();\n\n        let field_names = self.field_names();\n\n        // check output type\n        let output_arrow = body.output_arrow;\n        let return_ty = &body.return_ty;\n        let vis = &body.vis;\n\n        // Forward the docs from the function\n        let docs = self.docs();\n\n        // the actual function definition\n        if cfg!(feature = \"ssr\") {\n            let dummy_name = body.to_dummy_ident();\n            quote! {\n                #docs\n                #(#attrs)*\n                #(#lint_attrs)*\n                #vis async fn #fn_name(#(#fn_args),*) #output_arrow #return_ty {\n                    #dummy_name(#(#field_names),*).await\n                }\n            }\n        } else {\n            let restructure = if let Some(custom_wrapper) =\n                self.args.custom_wrapper.as_ref()\n            {\n                quote! {\n                    let data = #custom_wrapper(#struct_name { #(#field_names),* });\n                }\n            } else {\n                quote! {\n                    let data = #struct_name { #(#field_names),* };\n                }\n            };\n            let server_fn_path = self.server_fn_path();\n            quote! {\n                #docs\n                #(#attrs)*\n                #[allow(unused_variables)]\n                #vis async fn #fn_name(#(#fn_args),*) #output_arrow #return_ty {\n                    use #server_fn_path::ServerFn;\n                    #restructure\n                    data.run_on_client().await\n                }\n            }\n        }\n    }\n}\n\nimpl ToTokens for ServerFnCall {\n    fn to_tokens(&self, tokens: &mut TokenStream2) {\n        let body = &self.body;\n\n        // only emit the dummy (unmodified server-only body) for the server build\n        let dummy = cfg!(feature = \"ssr\").then(|| body.to_dummy_output());\n\n        let impl_from = self.impl_from();\n\n        let deref_impl = self.deref_impl();\n\n        let inventory = self.submit_to_inventory();\n\n        let func = self.func_tokens();\n\n        let server_fn_impl = self.server_fn_impl();\n\n        let struct_tokens = self.struct_tokens();\n\n        tokens.extend(quote! {\n            #struct_tokens\n\n            #impl_from\n\n            #deref_impl\n\n            #server_fn_impl\n\n            #inventory\n\n            #func\n\n            #dummy\n        });\n    }\n}\n\n/// The implementation of the `server` macro.\n/// ```ignore\n/// #[proc_macro_attribute]\n/// pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {\n///     match server_macro_impl(\n///         args.into(),\n///         s.into(),\n///         Some(syn::parse_quote!(my_crate::exports::server_fn)),\n///     ) {\n///         Err(e) => e.to_compile_error().into(),\n///         Ok(s) => s.to_token_stream().into(),\n///     }\n/// }\n/// ```\npub fn server_macro_impl(\n    args: TokenStream2,\n    body: TokenStream2,\n    server_fn_path: Option<Path>,\n    default_path: &str,\n    preset_server: Option<Type>,\n    default_protocol: Option<Type>,\n) -> Result<TokenStream2> {\n    let body = ServerFnCall::parse(default_path, args, body)?\n        .default_server_fn_path(server_fn_path)\n        .default_server_type(preset_server)\n        .default_protocol(default_protocol);\n\n    Ok(body.to_token_stream())\n}\n\nfn type_from_ident(ident: Ident) -> Type {\n    let mut segments = Punctuated::new();\n    segments.push(PathSegment {\n        ident,\n        arguments: PathArguments::None,\n    });\n    Type::Path(TypePath {\n        qself: None,\n        path: Path {\n            leading_colon: None,\n            segments,\n        },\n    })\n}\n\n/// Middleware for a server function.\n#[derive(Debug, Clone)]\npub struct Middleware {\n    expr: syn::Expr,\n}\n\nimpl ToTokens for Middleware {\n    fn to_tokens(&self, tokens: &mut TokenStream2) {\n        let expr = &self.expr;\n        tokens.extend(quote::quote! {\n            #expr\n        });\n    }\n}\n\nimpl Parse for Middleware {\n    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {\n        let arg: syn::Expr = input.parse()?;\n        Ok(Middleware { expr: arg })\n    }\n}\n\nfn output_type(return_ty: &Type) -> Option<&Type> {\n    if let syn::Type::Path(pat) = &return_ty {\n        if pat.path.segments[0].ident == \"Result\" {\n            if pat.path.segments.is_empty() {\n                panic!(\"{:#?}\", pat.path);\n            } else if let PathArguments::AngleBracketed(args) =\n                &pat.path.segments[0].arguments\n            {\n                if let GenericArgument::Type(ty) = &args.args[0] {\n                    return Some(ty);\n                }\n            }\n        }\n    };\n\n    None\n}\n\nfn err_type(return_ty: &Type) -> Option<&Type> {\n    if let syn::Type::Path(pat) = &return_ty {\n        if pat.path.segments[0].ident == \"Result\" {\n            if let PathArguments::AngleBracketed(args) =\n                &pat.path.segments[0].arguments\n            {\n                // Result<T>\n                if args.args.len() == 1 {\n                    return None;\n                }\n                // Result<T, _>\n                else if let GenericArgument::Type(ty) = &args.args[1] {\n                    return Some(ty);\n                }\n            }\n        }\n    };\n\n    None\n}\n\nfn err_ws_in_type(\n    inputs: &Punctuated<ServerFnArg, syn::token::Comma>,\n) -> Option<Type> {\n    inputs.into_iter().find_map(|pat| {\n        if let syn::Type::Path(ref pat) = *pat.arg.ty {\n            if pat.path.segments[0].ident != \"BoxedStream\" {\n                return None;\n            }\n\n            if let PathArguments::AngleBracketed(args) =\n                &pat.path.segments[0].arguments\n            {\n                // BoxedStream<T>\n                if args.args.len() == 1 {\n                    return None;\n                }\n                // BoxedStream<T, E>\n                else if let GenericArgument::Type(ty) = &args.args[1] {\n                    return Some(ty.clone());\n                }\n            };\n        };\n\n        None\n    })\n}\n\nfn err_ws_out_type(output_ty: &Option<Type>) -> Result<Option<Type>> {\n    if let Some(syn::Type::Path(ref pat)) = output_ty {\n        if pat.path.segments[0].ident == \"BoxedStream\" {\n            if let PathArguments::AngleBracketed(args) =\n                &pat.path.segments[0].arguments\n            {\n                // BoxedStream<T>\n                if args.args.len() == 1 {\n                    return Ok(None);\n                }\n                // BoxedStream<T, E>\n                else if let GenericArgument::Type(ty) = &args.args[1] {\n                    return Ok(Some(ty.clone()));\n                }\n\n                return Err(syn::Error::new(\n                    output_ty.span(),\n                    \"websocket server functions should return \\\n                     BoxedStream<Result<T, E>> where E: FromServerFnError\",\n                ));\n            };\n        }\n    };\n\n    Ok(None)\n}\n\n/// The arguments to the `server` macro.\n#[derive(Debug)]\n#[non_exhaustive]\npub struct ServerFnArgs {\n    /// The name of the struct that will implement the server function trait\n    /// and be submitted to inventory.\n    pub struct_name: Option<Ident>,\n    /// The prefix to use for the server function URL.\n    pub prefix: Option<LitStr>,\n    /// The input http encoding to use for the server function.\n    pub input: Option<Type>,\n    /// Additional traits to derive on the input struct for the server function.\n    pub input_derive: Option<ExprTuple>,\n    /// The output http encoding to use for the server function.\n    pub output: Option<Type>,\n    /// The path to the server function crate.\n    pub fn_path: Option<LitStr>,\n    /// The server type to use for the server function.\n    pub server: Option<Type>,\n    /// The client type to use for the server function.\n    pub client: Option<Type>,\n    /// The custom wrapper to use for the server function struct.\n    pub custom_wrapper: Option<Path>,\n    /// If the generated input type should implement `From` the only field in the input\n    pub impl_from: Option<LitBool>,\n    /// If the generated input type should implement `Deref` to the only field in the input\n    pub impl_deref: Option<LitBool>,\n    /// The protocol to use for the server function implementation.\n    pub protocol: Option<Type>,\n    builtin_encoding: bool,\n}\n\nimpl Parse for ServerFnArgs {\n    fn parse(stream: ParseStream) -> syn::Result<Self> {\n        // legacy 4-part arguments\n        let mut struct_name: Option<Ident> = None;\n        let mut prefix: Option<LitStr> = None;\n        let mut encoding: Option<LitStr> = None;\n        let mut fn_path: Option<LitStr> = None;\n\n        // new arguments: can only be keyed by name\n        let mut input: Option<Type> = None;\n        let mut input_derive: Option<ExprTuple> = None;\n        let mut output: Option<Type> = None;\n        let mut server: Option<Type> = None;\n        let mut client: Option<Type> = None;\n        let mut custom_wrapper: Option<Path> = None;\n        let mut impl_from: Option<LitBool> = None;\n        let mut impl_deref: Option<LitBool> = None;\n        let mut protocol: Option<Type> = None;\n\n        let mut use_key_and_value = false;\n        let mut arg_pos = 0;\n\n        while !stream.is_empty() {\n            arg_pos += 1;\n            let lookahead = stream.lookahead1();\n            if lookahead.peek(Ident) {\n                let key_or_value: Ident = stream.parse()?;\n\n                let lookahead = stream.lookahead1();\n                if lookahead.peek(Token![=]) {\n                    stream.parse::<Token![=]>()?;\n                    let key = key_or_value;\n                    use_key_and_value = true;\n                    if key == \"name\" {\n                        if struct_name.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `name`\",\n                            ));\n                        }\n                        struct_name = Some(stream.parse()?);\n                    } else if key == \"prefix\" {\n                        if prefix.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `prefix`\",\n                            ));\n                        }\n                        prefix = Some(stream.parse()?);\n                    } else if key == \"encoding\" {\n                        if encoding.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `encoding`\",\n                            ));\n                        }\n                        encoding = Some(stream.parse()?);\n                    } else if key == \"endpoint\" {\n                        if fn_path.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `endpoint`\",\n                            ));\n                        }\n                        fn_path = Some(stream.parse()?);\n                    } else if key == \"input\" {\n                        if encoding.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"`encoding` and `input` should not both be \\\n                                 specified\",\n                            ));\n                        } else if input.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `input`\",\n                            ));\n                        }\n                        input = Some(stream.parse()?);\n                    } else if key == \"input_derive\" {\n                        if input_derive.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `input_derive`\",\n                            ));\n                        }\n                        input_derive = Some(stream.parse()?);\n                    } else if key == \"output\" {\n                        if encoding.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"`encoding` and `output` should not both be \\\n                                 specified\",\n                            ));\n                        } else if output.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `output`\",\n                            ));\n                        }\n                        output = Some(stream.parse()?);\n                    } else if key == \"server\" {\n                        if server.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `server`\",\n                            ));\n                        }\n                        server = Some(stream.parse()?);\n                    } else if key == \"client\" {\n                        if client.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `client`\",\n                            ));\n                        }\n                        client = Some(stream.parse()?);\n                    } else if key == \"custom\" {\n                        if custom_wrapper.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `custom`\",\n                            ));\n                        }\n                        custom_wrapper = Some(stream.parse()?);\n                    } else if key == \"impl_from\" {\n                        if impl_from.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `impl_from`\",\n                            ));\n                        }\n                        impl_from = Some(stream.parse()?);\n                    } else if key == \"impl_deref\" {\n                        if impl_deref.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `impl_deref`\",\n                            ));\n                        }\n                        impl_deref = Some(stream.parse()?);\n                    } else if key == \"protocol\" {\n                        if protocol.is_some() {\n                            return Err(syn::Error::new(\n                                key.span(),\n                                \"keyword argument repeated: `protocol`\",\n                            ));\n                        }\n                        protocol = Some(stream.parse()?);\n                    } else {\n                        return Err(lookahead.error());\n                    }\n                } else {\n                    let value = key_or_value;\n                    if use_key_and_value {\n                        return Err(syn::Error::new(\n                            value.span(),\n                            \"positional argument follows keyword argument\",\n                        ));\n                    }\n                    if arg_pos == 1 {\n                        struct_name = Some(value)\n                    } else {\n                        return Err(syn::Error::new(\n                            value.span(),\n                            \"expected string literal\",\n                        ));\n                    }\n                }\n            } else if lookahead.peek(LitStr) {\n                if use_key_and_value {\n                    return Err(syn::Error::new(\n                        stream.span(),\n                        \"If you use keyword arguments (e.g., `name` = \\\n                         Something), then you can no longer use arguments \\\n                         without a keyword.\",\n                    ));\n                }\n                match arg_pos {\n                    1 => return Err(lookahead.error()),\n                    2 => prefix = Some(stream.parse()?),\n                    3 => encoding = Some(stream.parse()?),\n                    4 => fn_path = Some(stream.parse()?),\n                    _ => {\n                        return Err(syn::Error::new(\n                            stream.span(),\n                            \"unexpected extra argument\",\n                        ))\n                    }\n                }\n            } else {\n                return Err(lookahead.error());\n            }\n\n            if !stream.is_empty() {\n                stream.parse::<Token![,]>()?;\n            }\n        }\n\n        // parse legacy encoding into input/output\n        let mut builtin_encoding = false;\n        if let Some(encoding) = encoding {\n            match encoding.value().to_lowercase().as_str() {\n                \"url\" => {\n                    input = Some(type_from_ident(syn::parse_quote!(Url)));\n                    output = Some(type_from_ident(syn::parse_quote!(Json)));\n                    builtin_encoding = true;\n                }\n                \"cbor\" => {\n                    input = Some(type_from_ident(syn::parse_quote!(Cbor)));\n                    output = Some(type_from_ident(syn::parse_quote!(Cbor)));\n                    builtin_encoding = true;\n                }\n                \"getcbor\" => {\n                    input = Some(type_from_ident(syn::parse_quote!(GetUrl)));\n                    output = Some(type_from_ident(syn::parse_quote!(Cbor)));\n                    builtin_encoding = true;\n                }\n                \"getjson\" => {\n                    input = Some(type_from_ident(syn::parse_quote!(GetUrl)));\n                    output = Some(syn::parse_quote!(Json));\n                    builtin_encoding = true;\n                }\n                _ => {\n                    return Err(syn::Error::new(\n                        encoding.span(),\n                        \"Encoding not found.\",\n                    ))\n                }\n            }\n        }\n\n        Ok(Self {\n            struct_name,\n            prefix,\n            input,\n            input_derive,\n            output,\n            fn_path,\n            builtin_encoding,\n            server,\n            client,\n            custom_wrapper,\n            impl_from,\n            impl_deref,\n            protocol,\n        })\n    }\n}\n\n/// An argument type in a server function.\n#[derive(Debug, Clone)]\npub struct ServerFnArg {\n    /// The attributes on the server function argument.\n    server_fn_attributes: Vec<Attribute>,\n    /// The type of the server function argument.\n    arg: syn::PatType,\n}\n\nimpl ToTokens for ServerFnArg {\n    fn to_tokens(&self, tokens: &mut TokenStream2) {\n        let ServerFnArg { arg, .. } = self;\n        tokens.extend(quote! {\n            #arg\n        });\n    }\n}\n\nimpl Parse for ServerFnArg {\n    fn parse(input: ParseStream) -> Result<Self> {\n        let arg: syn::FnArg = input.parse()?;\n        let mut arg = match arg {\n            FnArg::Receiver(_) => {\n                return Err(syn::Error::new(\n                    arg.span(),\n                    \"cannot use receiver types in server function macro\",\n                ))\n            }\n            FnArg::Typed(t) => t,\n        };\n\n        fn rename_path(path: Path, from_ident: Ident, to_ident: Ident) -> Path {\n            if path.is_ident(&from_ident) {\n                Path {\n                    leading_colon: None,\n                    segments: Punctuated::from_iter([PathSegment {\n                        ident: to_ident,\n                        arguments: PathArguments::None,\n                    }]),\n                }\n            } else {\n                path\n            }\n        }\n\n        let server_fn_attributes = arg\n            .attrs\n            .iter()\n            .cloned()\n            .map(|attr| {\n                if attr.path().is_ident(\"server\") {\n                    // Allow the following attributes:\n                    // - #[server(default)]\n                    // - #[server(rename = \"fieldName\")]\n\n                    // Rename `server` to `serde`\n                    let attr = Attribute {\n                        meta: match attr.meta {\n                            Meta::Path(path) => Meta::Path(rename_path(\n                                path,\n                                format_ident!(\"server\"),\n                                format_ident!(\"serde\"),\n                            )),\n                            Meta::List(mut list) => {\n                                list.path = rename_path(\n                                    list.path,\n                                    format_ident!(\"server\"),\n                                    format_ident!(\"serde\"),\n                                );\n                                Meta::List(list)\n                            }\n                            Meta::NameValue(mut name_value) => {\n                                name_value.path = rename_path(\n                                    name_value.path,\n                                    format_ident!(\"server\"),\n                                    format_ident!(\"serde\"),\n                                );\n                                Meta::NameValue(name_value)\n                            }\n                        },\n                        ..attr\n                    };\n\n                    let args = attr.parse_args::<Meta>()?;\n                    match args {\n                        // #[server(default)]\n                        Meta::Path(path) if path.is_ident(\"default\") => {\n                            Ok(attr.clone())\n                        }\n                        // #[server(flatten)]\n                        Meta::Path(path) if path.is_ident(\"flatten\") => {\n                            Ok(attr.clone())\n                        }\n                        // #[server(default = \"value\")]\n                        Meta::NameValue(name_value)\n                            if name_value.path.is_ident(\"default\") =>\n                        {\n                            Ok(attr.clone())\n                        }\n                        // #[server(skip)]\n                        Meta::Path(path) if path.is_ident(\"skip\") => {\n                            Ok(attr.clone())\n                        }\n                        // #[server(rename = \"value\")]\n                        Meta::NameValue(name_value)\n                            if name_value.path.is_ident(\"rename\") =>\n                        {\n                            Ok(attr.clone())\n                        }\n                        _ => Err(Error::new(\n                            attr.span(),\n                            \"Unrecognized #[server] attribute, expected \\\n                             #[server(default)] or #[server(rename = \\\n                             \\\"fieldName\\\")]\",\n                        )),\n                    }\n                } else if attr.path().is_ident(\"doc\") {\n                    // Allow #[doc = \"documentation\"]\n                    Ok(attr.clone())\n                } else if attr.path().is_ident(\"allow\") {\n                    // Allow #[allow(...)]\n                    Ok(attr.clone())\n                } else if attr.path().is_ident(\"deny\") {\n                    // Allow #[deny(...)]\n                    Ok(attr.clone())\n                } else if attr.path().is_ident(\"ignore\") {\n                    // Allow #[ignore]\n                    Ok(attr.clone())\n                } else {\n                    Err(Error::new(\n                        attr.span(),\n                        \"Unrecognized attribute, expected #[server(...)]\",\n                    ))\n                }\n            })\n            .collect::<Result<Vec<_>>>()?;\n        arg.attrs = vec![];\n        Ok(ServerFnArg {\n            arg,\n            server_fn_attributes,\n        })\n    }\n}\n\n/// The body of a server function.\n#[derive(Debug, Clone)]\n#[non_exhaustive]\npub struct ServerFnBody {\n    /// The attributes on the server function.\n    pub attrs: Vec<Attribute>,\n    /// The visibility of the server function.\n    pub vis: syn::Visibility,\n    async_token: Token![async],\n    fn_token: Token![fn],\n    /// The name of the server function.\n    pub ident: Ident,\n    /// The generics of the server function.\n    pub generics: Generics,\n    _paren_token: token::Paren,\n    /// The arguments to the server function.\n    pub inputs: Punctuated<ServerFnArg, Token![,]>,\n    output_arrow: Token![->],\n    /// The return type of the server function.\n    pub return_ty: syn::Type,\n    /// The Ok output type of the server function.\n    pub output_ty: Option<syn::Type>,\n    /// The error output type of the server function.\n    pub error_ty: Option<syn::Type>,\n    /// The error type of WebSocket client-sent error\n    pub error_ws_in_ty: Option<syn::Type>,\n    /// The error type of WebSocket server-sent error\n    pub error_ws_out_ty: Option<syn::Type>,\n    /// The body of the server function.\n    pub block: TokenStream2,\n    /// The documentation of the server function.\n    pub docs: Vec<(String, Span)>,\n    /// The lint attributes of the server function.\n    pub lint_attrs: Vec<Attribute>,\n    /// The middleware attributes applied to the server function.\n    pub middlewares: Vec<Middleware>,\n}\n\nimpl Parse for ServerFnBody {\n    fn parse(input: ParseStream) -> Result<Self> {\n        let mut attrs: Vec<Attribute> = input.call(Attribute::parse_outer)?;\n\n        let vis: Visibility = input.parse()?;\n\n        let async_token = input.parse()?;\n\n        let fn_token = input.parse()?;\n        let ident = input.parse()?;\n        let generics: Generics = input.parse()?;\n\n        let content;\n        let _paren_token = syn::parenthesized!(content in input);\n\n        let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?;\n\n        let output_arrow = input.parse()?;\n        let return_ty = input.parse()?;\n        let output_ty = output_type(&return_ty).cloned();\n        let error_ty = err_type(&return_ty).cloned();\n        let error_ws_in_ty = err_ws_in_type(&inputs);\n        let error_ws_out_ty = err_ws_out_type(&output_ty)?;\n\n        let block = input.parse()?;\n\n        let docs = attrs\n            .iter()\n            .filter_map(|attr| {\n                let Meta::NameValue(attr) = &attr.meta else {\n                    return None;\n                };\n                if !attr.path.is_ident(\"doc\") {\n                    return None;\n                }\n\n                let value = match &attr.value {\n                    syn::Expr::Lit(lit) => match &lit.lit {\n                        syn::Lit::Str(s) => Some(s.value()),\n                        _ => return None,\n                    },\n                    _ => return None,\n                };\n\n                Some((value.unwrap_or_default(), attr.path.span()))\n            })\n            .collect();\n        attrs.retain(|attr| {\n            let Meta::NameValue(attr) = &attr.meta else {\n                return true;\n            };\n            !attr.path.is_ident(\"doc\")\n        });\n        let (lint_attrs, mut attrs): (Vec<_>, Vec<_>) =\n            attrs.into_iter().partition(is_lint_attr);\n\n        // extract all #[middleware] attributes, removing them from signature of dummy\n        let mut middlewares: Vec<Middleware> = vec![];\n        attrs.retain(|attr| {\n            if attr.meta.path().is_ident(\"middleware\") {\n                if let Ok(middleware) = attr.parse_args() {\n                    middlewares.push(middleware);\n                    false\n                } else {\n                    true\n                }\n            } else {\n                // in ssr mode, remove the \"lazy\" macro\n                // the lazy macro doesn't do anything on the server anyway, but it can cause confusion for rust-analyzer\n                // when the lazy macro is applied to both the function and the dummy\n                !(cfg!(feature = \"ssr\") && matches!(attr.meta.path().segments.last(), Some(PathSegment { ident, .. }) if ident == \"lazy\") )\n            }\n        });\n\n        Ok(Self {\n            vis,\n            async_token,\n            fn_token,\n            ident,\n            generics,\n            _paren_token,\n            inputs,\n            output_arrow,\n            return_ty,\n            output_ty,\n            error_ty,\n            error_ws_in_ty,\n            error_ws_out_ty,\n            block,\n            attrs,\n            docs,\n            lint_attrs,\n            middlewares,\n        })\n    }\n}\n\nimpl ServerFnBody {\n    fn to_dummy_ident(&self) -> Ident {\n        Ident::new(&format!(\"__server_{}\", self.ident), self.ident.span())\n    }\n\n    fn to_dummy_output(&self) -> TokenStream2 {\n        let ident = self.to_dummy_ident();\n        let Self {\n            attrs,\n            vis,\n            async_token,\n            fn_token,\n            generics,\n            inputs,\n            output_arrow,\n            return_ty,\n            block,\n            lint_attrs,\n            ..\n        } = &self;\n        quote! {\n            #[doc(hidden)]\n            #(#attrs)*\n            #(#lint_attrs)*\n            #vis #async_token #fn_token #ident #generics ( #inputs ) #output_arrow #return_ty\n            #block\n        }\n    }\n}\n\nfn is_lint_attr(attr: &Attribute) -> bool {\n    let path = &attr.path();\n    path.is_ident(\"allow\")\n        || path.is_ident(\"warn\")\n        || path.is_ident(\"expect\")\n        || path.is_ident(\"deny\")\n        || path.is_ident(\"forbid\")\n}\n"
  },
  {
    "path": "tachys/Cargo.toml",
    "content": "[package]\nname = \"tachys\"\nversion = \"0.2.14\"\nauthors = [\"Greg Johnston\"]\nlicense = \"MIT\"\nreadme = \"../README.md\"\nrepository = \"https://github.com/leptos-rs/leptos\"\ndescription = \"Tools for building reactivity-agnostic, renderer-generic, statically-typed view trees for user interface libraries.\"\nrust-version.workspace = true\nedition.workspace = true\n\n[dependencies]\nthrow_error = { workspace = true }\nany_spawner = { workspace = true, optional = true }\nconst_str_slice_concat = { workspace = true }\neither_of = { workspace = true }\nnext_tuple = { workspace = true }\nor_poisoned = { workspace = true }\nreactive_graph = { workspace = true, optional = true }\nreactive_stores = { workspace = true, optional = true }\nslotmap = { optional = true, workspace = true, default-features = true }\noco_ref = { workspace = true, optional = true }\nasync-trait = { workspace = true, default-features = true }\npaste = { workspace = true, default-features = true }\nerased = { workspace = true, default-features = true }\nwasm-bindgen = { workspace = true, default-features = true }\nhtml-escape = { workspace = true, default-features = true }\njs-sys = { workspace = true, default-features = true }\nweb-sys = { features = [\n  \"Window\",\n  \"Document\",\n  \"HtmlElement\",\n  \"HtmlInputElement\",\n  \"Element\",\n  \"Event\",\n  \"console\",\n  \"Comment\",\n  \"Text\",\n  \"Node\",\n  \"HtmlTemplateElement\",\n  \"DocumentFragment\",\n  \"DomTokenList\",\n  \"CssStyleDeclaration\",\n  \"ShadowRoot\",\n  \"HtmlCollection\",\n  \"DomStringMap\",\n\n  # Events we cast to in leptos_macro -- added here so we don't force users to import them\n  \"AddEventListenerOptions\",\n  \"AnimationEvent\",\n  \"BeforeUnloadEvent\",\n  \"ClipboardEvent\",\n  \"CompositionEvent\",\n  \"CustomEvent\",\n  \"DeviceMotionEvent\",\n  \"DeviceOrientationEvent\",\n  \"DragEvent\",\n  \"ErrorEvent\",\n  \"Event\",\n  \"FocusEvent\",\n  \"GamepadEvent\",\n  \"HashChangeEvent\",\n  \"InputEvent\",\n  \"KeyboardEvent\",\n  \"MessageEvent\",\n  \"MouseEvent\",\n  \"PageTransitionEvent\",\n  \"PointerEvent\",\n  \"PopStateEvent\",\n  \"ProgressEvent\",\n  \"PromiseRejectionEvent\",\n  \"SecurityPolicyViolationEvent\",\n  \"StorageEvent\",\n  \"SubmitEvent\",\n  \"TouchEvent\",\n  \"TransitionEvent\",\n  \"UiEvent\",\n  \"WheelEvent\",\n\n  # HTML Element Types\n  \"HtmlHtmlElement\",\n  \"HtmlBaseElement\",\n  \"HtmlHeadElement\",\n  \"HtmlLinkElement\",\n  \"HtmlMetaElement\",\n  \"HtmlStyleElement\",\n  \"HtmlTitleElement\",\n  \"HtmlBodyElement\",\n  \"HtmlHeadingElement\",\n  \"HtmlQuoteElement\",\n  \"HtmlDivElement\",\n  \"HtmlDListElement\",\n  \"HtmlHrElement\",\n  \"HtmlLiElement\",\n  \"HtmlOListElement\",\n  \"HtmlParagraphElement\",\n  \"HtmlPreElement\",\n  \"HtmlUListElement\",\n  \"HtmlAnchorElement\",\n  \"HtmlBrElement\",\n  \"HtmlDataElement\",\n  \"HtmlQuoteElement\",\n  \"HtmlSpanElement\",\n  \"HtmlTimeElement\",\n  \"HtmlAreaElement\",\n  \"HtmlAudioElement\",\n  \"HtmlImageElement\",\n  \"HtmlMapElement\",\n  \"HtmlTrackElement\",\n  \"HtmlVideoElement\",\n  \"HtmlEmbedElement\",\n  \"HtmlIFrameElement\",\n  \"HtmlObjectElement\",\n  \"HtmlParamElement\",\n  \"HtmlPictureElement\",\n  \"HtmlSourceElement\",\n  \"SvgElement\",\n  \"HtmlCanvasElement\",\n  \"HtmlScriptElement\",\n  \"HtmlModElement\",\n  \"HtmlTableCaptionElement\",\n  \"HtmlTableColElement\",\n  \"HtmlTableColElement\",\n  \"HtmlTableElement\",\n  \"HtmlTableSectionElement\",\n  \"HtmlTableCellElement\",\n  \"HtmlTableSectionElement\",\n  \"HtmlTableCellElement\",\n  \"HtmlTableSectionElement\",\n  \"HtmlTableRowElement\",\n  \"HtmlButtonElement\",\n  \"HtmlDataListElement\",\n  \"HtmlFieldSetElement\",\n  \"HtmlFormElement\",\n  \"HtmlInputElement\",\n  \"HtmlLabelElement\",\n  \"HtmlLegendElement\",\n  \"HtmlMeterElement\",\n  \"HtmlOptGroupElement\",\n  \"HtmlOutputElement\",\n  \"HtmlProgressElement\",\n  \"HtmlSelectElement\",\n  \"HtmlTextAreaElement\",\n  \"HtmlDetailsElement\",\n  \"HtmlDialogElement\",\n  \"HtmlMenuElement\",\n  \"HtmlSlotElement\",\n  \"HtmlTemplateElement\",\n  \"HtmlOptionElement\",\n], workspace = true, default-features = true }\ndrain_filter_polyfill = { workspace = true, default-features = true }\nindexmap = { workspace = true, default-features = true }\nrustc-hash = { workspace = true, default-features = true }\nfutures = { workspace = true, default-features = true }\nitertools = { workspace = true, default-features = true }\nsend_wrapper = { workspace = true, default-features = true }\nsledgehammer_bindgen = { features = [\n  \"web\",\n], optional = true, workspace = true, default-features = true }\nsledgehammer_utils = { optional = true, workspace = true, default-features = true }\ntracing = { optional = true, workspace = true, default-features = true }\nserde = { optional = true, workspace = true, default-features = true }\nserde_json = { optional = true, workspace = true, default-features = true }\n\n[dev-dependencies]\ntokio-test = { workspace = true, default-features = true }\ntokio = { features = [\n  \"rt\",\n  \"macros\",\n], workspace = true, default-features = true }\n\n[build-dependencies]\nrustc_version = { workspace = true, default-features = true }\n\n[features]\ndefault = [\"testing\"]\ndelegation = []                                                       # enables event delegation\nerror-hook = []\nhydrate = []\nislands = [\"dep:serde\", \"dep:serde_json\"]\nssr = []\noco = [\"dep:oco_ref\"]\nnightly = [\"reactive_graph/nightly\"]\ntesting = [\"dep:slotmap\"]\nreactive_graph = [\"dep:reactive_graph\", \"dep:any_spawner\"]\nreactive_stores = [\"reactive_graph\", \"dep:reactive_stores\"]\nsledgehammer = [\"dep:sledgehammer_bindgen\", \"dep:sledgehammer_utils\"]\ntracing = [\"dep:tracing\"]\nmark_branches = []\n\n[package.metadata.cargo-all-features]\ndenylist = [\"tracing\", \"sledgehammer\"]\nskip_feature_sets = [\n  [\n    \"ssr\",\n    \"hydrate\",\n  ],\n  [\n    \"hydrate\",\n    \"islands\",\n  ],\n  [\n    \"ssr\",\n    \"delegation\",\n  ],\n  [\n    \"nightly\",\n  ],\n]\nmax_combination_size = 2\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = [\n  'cfg(leptos_debuginfo)',\n  'cfg(erase_components)',\n  'cfg(rustc_nightly)',\n] }\n"
  },
  {
    "path": "tachys/Makefile.toml",
    "content": "extend = { path = \"../cargo-make/main.toml\" }\n"
  },
  {
    "path": "tachys/build.rs",
    "content": "use rustc_version::{version_meta, Channel};\n\nfn main() {\n    // Set cfg flags depending on release channel\n    if matches!(version_meta().unwrap().channel, Channel::Nightly) {\n        println!(\"cargo:rustc-cfg=rustc_nightly\");\n    }\n}\n"
  },
  {
    "path": "tachys/src/dom.rs",
    "content": "use wasm_bindgen::JsCast;\nuse web_sys::{Document, HtmlElement, Window};\n\nthread_local! {\n    pub(crate) static WINDOW: web_sys::Window = web_sys::window().unwrap();\n\n    pub(crate) static DOCUMENT: web_sys::Document = web_sys::window().unwrap().document().unwrap();\n}\n\n/// Returns the [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window).\n///\n/// This is cached as a thread-local variable, so calling `window()` multiple times\n/// requires only one call out to JavaScript.\npub fn window() -> Window {\n    WINDOW.with(Clone::clone)\n}\n\n/// Returns the [`Document`](https://developer.mozilla.org/en-US/docs/Web/API/Document).\n///\n/// This is cached as a thread-local variable, so calling `document()` multiple times\n/// requires only one call out to JavaScript.\n///\n/// ## Panics\n/// Panics if called outside a browser environment.\npub fn document() -> Document {\n    DOCUMENT.with(Clone::clone)\n}\n\n/// The `<body>` element.\n///\n/// ## Panics\n/// Panics if there is no `<body>` in the current document, or if it is called outside a browser\n/// environment.\npub fn body() -> HtmlElement {\n    document().body().unwrap()\n}\n\n/// Helper function to extract [`Event.target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target)\n/// from any event.\npub fn event_target<T>(event: &web_sys::Event) -> T\nwhere\n    T: JsCast,\n{\n    event.target().unwrap().unchecked_into::<T>()\n}\n\n/// Helper function to extract `event.target.value` from an event.\n///\n/// This is useful in the `on:input` or `on:change` listeners for an `<input>` element.\npub fn event_target_value<T>(event: &T) -> String\nwhere\n    T: JsCast,\n{\n    event\n        .unchecked_ref::<web_sys::Event>()\n        .target()\n        .unwrap()\n        .unchecked_into::<web_sys::HtmlInputElement>()\n        .value()\n}\n\n/// Helper function to extract `event.target.checked` from an event.\n///\n/// This is useful in the `on:change` listeners for an `<input type=\"checkbox\">` element.\npub fn event_target_checked(ev: &web_sys::Event) -> bool {\n    ev.target()\n        .unwrap()\n        .unchecked_into::<web_sys::HtmlInputElement>()\n        .checked()\n}\n"
  },
  {
    "path": "tachys/src/erased.rs",
    "content": "use erased::ErasedBox;\n\n#[cfg(not(erase_components))]\nfn check(id_1: &std::any::TypeId, id_2: &std::any::TypeId) {\n    if id_1 != id_2 {\n        panic!(\"Erased: type mismatch\")\n    }\n}\n\nmacro_rules! erased {\n    ([$($new_t_params:tt)*], $name:ident) => {\n        /// A type-erased item. This is slightly more efficient than using `Box<dyn Any (+ Send)>`.\n        ///\n        /// With the caveat that T must always be correct upon retrieval.\n        /// In erased mode T retrieval is unchecked to minimise codegen, in other modes T will be verified and a panic otherwise.\n        pub struct $name {\n            #[cfg(not(erase_components))]\n            type_id: std::any::TypeId,\n            value: Option<ErasedBox>,\n            drop: fn(ErasedBox),\n        }\n\n\n        impl $name {\n            /// Create a new type-erased item.\n            pub fn new<T: $($new_t_params)*>(item: T) -> Self {\n                Self {\n                    #[cfg(not(erase_components))]\n                    type_id: std::any::TypeId::of::<T>(),\n                    value: Some(ErasedBox::new(Box::new(item))),\n                    drop: |value| {\n                        let _ = unsafe { value.into_inner::<T>() };\n                    },\n                }\n            }\n\n            /// Get a reference to the inner value.\n            pub fn get_ref<T: 'static>(&self) -> &T {\n                #[cfg(not(erase_components))]\n                check(&self.type_id, &std::any::TypeId::of::<T>());\n                unsafe { self.value.as_ref().unwrap().get_ref::<T>() }\n            }\n\n            /// Get a mutable reference to the inner value.\n            pub fn get_mut<T: 'static>(&mut self) -> &mut T {\n                #[cfg(not(erase_components))]\n                check(&self.type_id, &std::any::TypeId::of::<T>());\n                unsafe { self.value.as_mut().unwrap().get_mut::<T>() }\n            }\n\n            /// Consume the item and return the inner value.\n            pub fn into_inner<T: 'static>(mut self) -> T {\n                #[cfg(not(erase_components))]\n                check(&self.type_id, &std::any::TypeId::of::<T>());\n                *unsafe { self.value.take().unwrap().into_inner::<T>() }\n            }\n        }\n\n        /// If into_inner() wasn't called, the value would leak and destructors wouldn't run, this prevents that from happening.\n        impl Drop for $name {\n            fn drop(&mut self) {\n                if let Some(value) = self.value.take() {\n                    (self.drop)(value);\n                }\n            }\n        }\n    };\n\n}\n\nerased!([Send + 'static], Erased);\nerased!(['static], ErasedLocal);\n\n/// SAFETY: `Erased::new` ensures that `T` is `Send` and `'static`.\nunsafe impl Send for Erased {}\n"
  },
  {
    "path": "tachys/src/html/attribute/any_attribute.rs",
    "content": "use super::{Attribute, NextAttribute};\nuse crate::{\n    erased::{Erased, ErasedLocal},\n    html::attribute::NamedAttributeKey,\n    renderer::{dom::Element, Rndr},\n};\nuse std::{any::TypeId, fmt::Debug, mem};\n#[cfg(feature = \"ssr\")]\nuse std::{future::Future, pin::Pin};\n\n/// A type-erased container for any [`Attribute`].\npub struct AnyAttribute {\n    type_id: TypeId,\n    html_len: usize,\n    value: Erased,\n    clone: fn(&Erased) -> AnyAttribute,\n    #[cfg(feature = \"ssr\")]\n    to_html: fn(Erased, &mut String, &mut String, &mut String, &mut String),\n    build: fn(Erased, el: crate::renderer::types::Element) -> AnyAttributeState,\n    rebuild: fn(Erased, &mut AnyAttributeState),\n    #[cfg(feature = \"hydrate\")]\n    hydrate_from_server:\n        fn(Erased, crate::renderer::types::Element) -> AnyAttributeState,\n    #[cfg(feature = \"hydrate\")]\n    hydrate_from_template:\n        fn(Erased, crate::renderer::types::Element) -> AnyAttributeState,\n    #[cfg(feature = \"ssr\")]\n    #[allow(clippy::type_complexity)]\n    resolve: fn(Erased) -> Pin<Box<dyn Future<Output = AnyAttribute> + Send>>,\n    #[cfg(feature = \"ssr\")]\n    dry_resolve: fn(&mut Erased),\n    keys: fn(&Erased) -> Vec<NamedAttributeKey>,\n}\n\nimpl Clone for AnyAttribute {\n    fn clone(&self) -> Self {\n        (self.clone)(&self.value)\n    }\n}\n\nimpl Debug for AnyAttribute {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AnyAttribute\").finish_non_exhaustive()\n    }\n}\n\n/// View state for [`AnyAttribute`].\npub struct AnyAttributeState {\n    type_id: TypeId,\n    state: ErasedLocal,\n    el: crate::renderer::types::Element,\n    keys: Vec<NamedAttributeKey>,\n}\n\n/// Converts an [`Attribute`] into [`AnyAttribute`].\npub trait IntoAnyAttribute {\n    /// Wraps the given attribute.\n    fn into_any_attr(self) -> AnyAttribute;\n}\n\nimpl<T> IntoAnyAttribute for T\nwhere\n    Self: Send,\n    T: Attribute,\n    crate::renderer::types::Element: Clone,\n{\n    fn into_any_attr(self) -> AnyAttribute {\n        fn clone<T: Attribute + Clone + 'static>(\n            value: &Erased,\n        ) -> AnyAttribute {\n            value.get_ref::<T>().clone().into_any_attr()\n        }\n\n        #[cfg(feature = \"ssr\")]\n        fn to_html<T: Attribute + 'static>(\n            value: Erased,\n            buf: &mut String,\n            class: &mut String,\n            style: &mut String,\n            inner_html: &mut String,\n        ) {\n            value\n                .into_inner::<T>()\n                .to_html(buf, class, style, inner_html);\n        }\n\n        fn build<T: Attribute + 'static>(\n            value: Erased,\n            el: crate::renderer::types::Element,\n        ) -> AnyAttributeState {\n            AnyAttributeState {\n                type_id: TypeId::of::<T>(),\n                keys: value.get_ref::<T>().keys(),\n                state: ErasedLocal::new(value.into_inner::<T>().build(&el)),\n                el,\n            }\n        }\n\n        #[cfg(feature = \"hydrate\")]\n        fn hydrate_from_server<T: Attribute + 'static>(\n            value: Erased,\n            el: crate::renderer::types::Element,\n        ) -> AnyAttributeState {\n            AnyAttributeState {\n                type_id: TypeId::of::<T>(),\n                keys: value.get_ref::<T>().keys(),\n                state: ErasedLocal::new(\n                    value.into_inner::<T>().hydrate::<true>(&el),\n                ),\n                el,\n            }\n        }\n\n        #[cfg(feature = \"hydrate\")]\n        fn hydrate_from_template<T: Attribute + 'static>(\n            value: Erased,\n            el: crate::renderer::types::Element,\n        ) -> AnyAttributeState {\n            AnyAttributeState {\n                type_id: TypeId::of::<T>(),\n                keys: value.get_ref::<T>().keys(),\n                state: ErasedLocal::new(\n                    value.into_inner::<T>().hydrate::<true>(&el),\n                ),\n                el,\n            }\n        }\n\n        fn rebuild<T: Attribute + 'static>(\n            value: Erased,\n            state: &mut AnyAttributeState,\n        ) {\n            let value = value.into_inner::<T>();\n            let state = state.state.get_mut::<T::State>();\n            value.rebuild(state);\n        }\n\n        #[cfg(feature = \"ssr\")]\n        fn dry_resolve<T: Attribute + 'static>(value: &mut Erased) {\n            value.get_mut::<T>().dry_resolve();\n        }\n\n        #[cfg(feature = \"ssr\")]\n        fn resolve<T: Attribute + 'static>(\n            value: Erased,\n        ) -> Pin<Box<dyn Future<Output = AnyAttribute> + Send>> {\n            use futures::FutureExt;\n\n            async move {value.into_inner::<T>().resolve().await.into_any_attr()}.boxed()\n        }\n\n        fn keys<T: Attribute + 'static>(\n            value: &Erased,\n        ) -> Vec<NamedAttributeKey> {\n            value.get_ref::<T>().keys()\n        }\n\n        let value = self.into_cloneable_owned();\n        AnyAttribute {\n            type_id: TypeId::of::<T::CloneableOwned>(),\n            html_len: value.html_len(),\n            value: Erased::new(value),\n            clone: clone::<T::CloneableOwned>,\n            #[cfg(feature = \"ssr\")]\n            to_html: to_html::<T::CloneableOwned>,\n            build: build::<T::CloneableOwned>,\n            rebuild: rebuild::<T::CloneableOwned>,\n            #[cfg(feature = \"hydrate\")]\n            hydrate_from_server: hydrate_from_server::<T::CloneableOwned>,\n            #[cfg(feature = \"hydrate\")]\n            hydrate_from_template: hydrate_from_template::<T::CloneableOwned>,\n            #[cfg(feature = \"ssr\")]\n            resolve: resolve::<T::CloneableOwned>,\n            #[cfg(feature = \"ssr\")]\n            dry_resolve: dry_resolve::<T::CloneableOwned>,\n            keys: keys::<T::CloneableOwned>,\n        }\n    }\n}\n\nimpl NextAttribute for AnyAttribute {\n    type Output<NewAttr: Attribute> = Vec<AnyAttribute>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        vec![self, new_attr.into_any_attr()]\n    }\n}\n\nimpl Attribute for AnyAttribute {\n    const MIN_LENGTH: usize = 0;\n\n    type AsyncOutput = AnyAttribute;\n    type State = AnyAttributeState;\n    type Cloneable = AnyAttribute;\n    type CloneableOwned = AnyAttribute;\n\n    fn html_len(&self) -> usize {\n        self.html_len\n    }\n\n    #[allow(unused)] // they are used in SSR\n    fn to_html(\n        self,\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n    ) {\n        #[cfg(feature = \"ssr\")]\n        {\n            (self.to_html)(self.value, buf, class, style, inner_html);\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        panic!(\n            \"You are rendering AnyAttribute to HTML without the `ssr` feature \\\n             enabled.\"\n        );\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        #[cfg(feature = \"hydrate\")]\n        if FROM_SERVER {\n            (self.hydrate_from_server)(self.value, el.clone())\n        } else {\n            (self.hydrate_from_template)(self.value, el.clone())\n        }\n        #[cfg(not(feature = \"hydrate\"))]\n        {\n            _ = el;\n            panic!(\n                \"You are trying to hydrate AnyAttribute without the `hydrate` \\\n                 feature enabled.\"\n            );\n        }\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        (self.build)(self.value, el.clone())\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        if self.type_id == state.type_id {\n            (self.rebuild)(self.value, state)\n        } else {\n            let new = self.build(&state.el);\n            *state = new;\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {\n        #[cfg(feature = \"ssr\")]\n        {\n            (self.dry_resolve)(&mut self.value)\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        panic!(\n            \"You are rendering AnyAttribute to HTML without the `ssr` feature \\\n             enabled.\"\n        );\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        #[cfg(feature = \"ssr\")]\n        {\n            (self.resolve)(self.value).await\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        panic!(\n            \"You are rendering AnyAttribute to HTML without the `ssr` feature \\\n             enabled.\"\n        );\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        (self.keys)(&self.value)\n    }\n}\n\nimpl NextAttribute for Vec<AnyAttribute> {\n    type Output<NewAttr: Attribute> = Self;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        mut self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        self.push(new_attr.into_any_attr());\n        self\n    }\n}\n\nimpl Attribute for Vec<AnyAttribute> {\n    const MIN_LENGTH: usize = 0;\n\n    type AsyncOutput = Vec<AnyAttribute>;\n    type State = (Element, Vec<AnyAttributeState>);\n    type Cloneable = Vec<AnyAttribute>;\n    type CloneableOwned = Vec<AnyAttribute>;\n\n    fn html_len(&self) -> usize {\n        self.iter().map(|attr| attr.html_len()).sum()\n    }\n\n    #[allow(unused)] // they are used in SSR\n    fn to_html(\n        self,\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n    ) {\n        #[cfg(feature = \"ssr\")]\n        {\n            for mut attr in self {\n                attr.to_html(buf, class, style, inner_html)\n            }\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        panic!(\n            \"You are rendering AnyAttribute to HTML without the `ssr` feature \\\n             enabled.\"\n        );\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        #[cfg(feature = \"hydrate\")]\n        if FROM_SERVER {\n            (\n                el.clone(),\n                self.into_iter()\n                    .map(|attr| attr.hydrate::<true>(el))\n                    .collect(),\n            )\n        } else {\n            (\n                el.clone(),\n                self.into_iter()\n                    .map(|attr| attr.hydrate::<false>(el))\n                    .collect(),\n            )\n        }\n        #[cfg(not(feature = \"hydrate\"))]\n        {\n            _ = el;\n            panic!(\n                \"You are trying to hydrate AnyAttribute without the `hydrate` \\\n                 feature enabled.\"\n            );\n        }\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        (\n            el.clone(),\n            self.into_iter().map(|attr| attr.build(el)).collect(),\n        )\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, state) = state;\n        for old in mem::take(state) {\n            for key in old.keys {\n                match key {\n                    NamedAttributeKey::InnerHtml => {\n                        Rndr::set_inner_html(&old.el, \"\");\n                    }\n                    NamedAttributeKey::Property(prop_name) => {\n                        Rndr::set_property(\n                            &old.el,\n                            &prop_name,\n                            &wasm_bindgen::JsValue::UNDEFINED,\n                        );\n                    }\n                    NamedAttributeKey::Attribute(key) => {\n                        Rndr::remove_attribute(&old.el, &key);\n                    }\n                }\n            }\n        }\n        *state = self.into_iter().map(|s| s.build(el)).collect();\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {\n        #[cfg(feature = \"ssr\")]\n        {\n            for attr in self.iter_mut() {\n                attr.dry_resolve()\n            }\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        panic!(\n            \"You are rendering AnyAttribute to HTML without the `ssr` feature \\\n             enabled.\"\n        );\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        #[cfg(feature = \"ssr\")]\n        {\n            futures::future::join_all(\n                self.into_iter().map(|attr| attr.resolve()),\n            )\n            .await\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        panic!(\n            \"You are rendering AnyAttribute to HTML without the `ssr` feature \\\n             enabled.\"\n        );\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        self.iter().flat_map(|s| s.keys()).collect()\n    }\n}\n"
  },
  {
    "path": "tachys/src/html/attribute/aria.rs",
    "content": "use crate::{\n    html::{\n        attribute::{Attr, *},\n        element::{ElementType, HtmlElement},\n    },\n    renderer::Rndr,\n    view::{add_attr::AddAnyAttr, RenderHtml},\n};\n\n/// Applies ARIA attributes to an HTML element.\npub trait AriaAttributes<Rndr, V>\nwhere\n    Self: Sized + AddAnyAttr,\n    V: AttributeValue,\n{\n    /// Identifies the currently active descendant of a composite widget.\n    fn aria_activedescendant(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaActivedescendant, V>> {\n        self.add_any_attr(aria_activedescendant(value))\n    }\n\n    /// Indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the `aria-relevant` attribute.\n    fn aria_atomic(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaAtomic, V>> {\n        self.add_any_attr(aria_atomic(value))\n    }\n\n    /// Indicates whether user input completion suggestions are provided.\n    fn aria_autocomplete(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaAutocomplete, V>> {\n        self.add_any_attr(aria_autocomplete(value))\n    }\n\n    /// Indicates whether an element, and its subtree, are currently being updated.\n    fn aria_busy(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaBusy, V>> {\n        self.add_any_attr(aria_busy(value))\n    }\n\n    /// Indicates the current \"checked\" state of checkboxes, radio buttons, and other widgets.\n    fn aria_checked(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaChecked, V>> {\n        self.add_any_attr(aria_checked(value))\n    }\n\n    /// Defines the number of columns in a table, grid, or treegrid.\n    fn aria_colcount(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaColcount, V>> {\n        self.add_any_attr(aria_colcount(value))\n    }\n\n    /// Defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.\n    fn aria_colindex(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaColindex, V>> {\n        self.add_any_attr(aria_colindex(value))\n    }\n\n    /// Defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.\n    fn aria_colspan(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaColspan, V>> {\n        self.add_any_attr(aria_colspan(value))\n    }\n\n    /// Identifies the element (or elements) whose contents or presence are controlled by the current element.\n    fn aria_controls(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaControls, V>> {\n        self.add_any_attr(aria_controls(value))\n    }\n\n    /// Indicates the element that represents the current item within a container or set of related elements.\n    fn aria_current(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaCurrent, V>> {\n        self.add_any_attr(aria_current(value))\n    }\n\n    /// Identifies the element (or elements) that describes the object.\n    fn aria_describedby(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaDescribedby, V>> {\n        self.add_any_attr(aria_describedby(value))\n    }\n\n    /// Defines a string value that describes or annotates the current element.\n    fn aria_description(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaDescription, V>> {\n        self.add_any_attr(aria_description(value))\n    }\n\n    /// Identifies the element that provides additional information related to the object.\n    fn aria_details(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaDetails, V>> {\n        self.add_any_attr(aria_details(value))\n    }\n\n    /// Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.\n    fn aria_disabled(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaDisabled, V>> {\n        self.add_any_attr(aria_disabled(value))\n    }\n\n    /// Indicates what functions can be performed when a dragged object is released on the drop target.\n    fn aria_dropeffect(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaDropeffect, V>> {\n        self.add_any_attr(aria_dropeffect(value))\n    }\n\n    /// Defines the element that provides an error message related to the object.\n    fn aria_errormessage(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaErrormessage, V>> {\n        self.add_any_attr(aria_errormessage(value))\n    }\n\n    /// Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.\n    fn aria_expanded(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaExpanded, V>> {\n        self.add_any_attr(aria_expanded(value))\n    }\n\n    /// Identifies the next element (or elements) in an alternate reading order of content.\n    fn aria_flowto(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaFlowto, V>> {\n        self.add_any_attr(aria_flowto(value))\n    }\n\n    /// Indicates an element's \"grabbed\" state in a drag-and-drop operation.\n    fn aria_grabbed(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaGrabbed, V>> {\n        self.add_any_attr(aria_grabbed(value))\n    }\n\n    /// Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element.\n    fn aria_haspopup(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaHaspopup, V>> {\n        self.add_any_attr(aria_haspopup(value))\n    }\n\n    /// Indicates whether the element is exposed to an accessibility API.\n    fn aria_hidden(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaHidden, V>> {\n        self.add_any_attr(aria_hidden(value))\n    }\n\n    /// Indicates the entered value does not conform to the format expected by the application.\n    fn aria_invalid(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaInvalid, V>> {\n        self.add_any_attr(aria_invalid(value))\n    }\n\n    /// Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element.\n    fn aria_keyshortcuts(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaKeyshortcuts, V>> {\n        self.add_any_attr(aria_keyshortcuts(value))\n    }\n\n    /// Defines a string value that labels the current element.\n    fn aria_label(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaLabel, V>> {\n        self.add_any_attr(aria_label(value))\n    }\n\n    /// Identifies the element (or elements) that labels the current element.\n    fn aria_labelledby(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaLabelledby, V>> {\n        self.add_any_attr(aria_labelledby(value))\n    }\n\n    /// Indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region.\n    fn aria_live(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaLive, V>> {\n        self.add_any_attr(aria_live(value))\n    }\n\n    /// Indicates whether an element is modal when displayed.\n    fn aria_modal(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaModal, V>> {\n        self.add_any_attr(aria_modal(value))\n    }\n\n    /// Indicates whether a text box accepts multiple lines of input or only a single line.\n    fn aria_multiline(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaMultiline, V>> {\n        self.add_any_attr(aria_multiline(value))\n    }\n\n    /// Indicates that the user may select more than one item from the current selectable descendants.\n    fn aria_multiselectable(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaMultiselectable, V>> {\n        self.add_any_attr(aria_multiselectable(value))\n    }\n\n    /// Indicates whether the element's orientation is horizontal, vertical, or undefined.\n    fn aria_orientation(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaOrientation, V>> {\n        self.add_any_attr(aria_orientation(value))\n    }\n\n    /// Identifies an element (or elements) in order to define a visual, functional, or contextual parent/child relationship between DOM elements where the DOM hierarchy cannot be used to represent the relationship.\n    fn aria_owns(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaOwns, V>> {\n        self.add_any_attr(aria_owns(value))\n    }\n\n    /// Defines a short hint (a word or short phrase) intended to help the user with data entry when the control has no value.\n    fn aria_placeholder(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaPlaceholder, V>> {\n        self.add_any_attr(aria_placeholder(value))\n    }\n\n    /// Defines an element's number or position in the current set of listitems or treeitems.\n    fn aria_posinset(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaPosinset, V>> {\n        self.add_any_attr(aria_posinset(value))\n    }\n\n    /// Indicates the current \"pressed\" state of toggle buttons.\n    fn aria_pressed(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaPressed, V>> {\n        self.add_any_attr(aria_pressed(value))\n    }\n\n    /// Indicates that the element is not editable, but is otherwise operable.\n    fn aria_readonly(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaReadonly, V>> {\n        self.add_any_attr(aria_readonly(value))\n    }\n\n    /// Indicates what notifications the user agent will trigger when the accessibility tree within a live region is modified.\n    fn aria_relevant(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaRelevant, V>> {\n        self.add_any_attr(aria_relevant(value))\n    }\n\n    /// Indicates that user input is required on the element before a form may be submitted.\n    fn aria_required(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaRequired, V>> {\n        self.add_any_attr(aria_required(value))\n    }\n\n    /// Defines a human-readable, author-localized description for the role of an element.\n    fn aria_roledescription(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaRoledescription, V>> {\n        self.add_any_attr(aria_roledescription(value))\n    }\n\n    /// Defines the total number of rows in a table, grid, or treegrid.\n    fn aria_rowcount(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaRowcount, V>> {\n        self.add_any_attr(aria_rowcount(value))\n    }\n\n    /// Defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.\n    fn aria_rowindex(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaRowindex, V>> {\n        self.add_any_attr(aria_rowindex(value))\n    }\n\n    /// Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.\n    fn aria_rowspan(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaRowspan, V>> {\n        self.add_any_attr(aria_rowspan(value))\n    }\n\n    /// Indicates the current \"selected\" state of various widgets.\n    fn aria_selected(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaSelected, V>> {\n        self.add_any_attr(aria_selected(value))\n    }\n\n    /// Defines the number of items in the current set of listitems or treeitems.\n    fn aria_setsize(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaSetsize, V>> {\n        self.add_any_attr(aria_setsize(value))\n    }\n\n    /// Indicates if items in a table or grid are sorted in ascending or descending order.\n    fn aria_sort(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaSort, V>> {\n        self.add_any_attr(aria_sort(value))\n    }\n\n    /// Defines the maximum allowed value for a range widget.\n    fn aria_valuemax(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaValuemax, V>> {\n        self.add_any_attr(aria_valuemax(value))\n    }\n\n    /// Defines the minimum allowed value for a range widget.\n    fn aria_valuemin(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaValuemin, V>> {\n        self.add_any_attr(aria_valuemin(value))\n    }\n\n    /// Defines the current value for a range widget.\n    fn aria_valuenow(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaValuenow, V>> {\n        self.add_any_attr(aria_valuenow(value))\n    }\n\n    /// Defines the human-readable text alternative of `aria-valuenow` for a range widget.\n    fn aria_valuetext(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<AriaValuetext, V>> {\n        self.add_any_attr(aria_valuetext(value))\n    }\n}\n\nimpl<El, At, Ch, V> AriaAttributes<Rndr, V> for HtmlElement<El, At, Ch>\nwhere\n    El: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n    V: AttributeValue,\n{\n}\n"
  },
  {
    "path": "tachys/src/html/attribute/custom.rs",
    "content": "use super::{\n    maybe_next_attr_erasure_macros::next_attr_output_type, NextAttribute,\n};\nuse crate::{\n    html::attribute::{\n        maybe_next_attr_erasure_macros::next_attr_combine, Attribute,\n        AttributeValue, NamedAttributeKey,\n    },\n    view::{add_attr::AddAnyAttr, Position, ToTemplate},\n};\nuse std::{borrow::Cow, sync::Arc};\n\n/// Adds a custom attribute with any key-value combination.\n#[inline(always)]\npub fn custom_attribute<K, V>(key: K, value: V) -> CustomAttr<K, V>\nwhere\n    K: CustomAttributeKey,\n    V: AttributeValue,\n{\n    CustomAttr { key, value }\n}\n\n/// A custom attribute with any key-value combination.\n#[derive(Debug)]\npub struct CustomAttr<K, V>\nwhere\n    K: CustomAttributeKey,\n    V: AttributeValue,\n{\n    key: K,\n    value: V,\n}\n\nimpl<K, V> Clone for CustomAttr<K, V>\nwhere\n    K: CustomAttributeKey,\n    V: AttributeValue + Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            key: self.key.clone(),\n            value: self.value.clone(),\n        }\n    }\n}\n\nimpl<K, V> Attribute for CustomAttr<K, V>\nwhere\n    K: CustomAttributeKey,\n    V: AttributeValue,\n{\n    const MIN_LENGTH: usize = 0;\n    type AsyncOutput = CustomAttr<K, V::AsyncOutput>;\n    type State = V::State;\n    type Cloneable = CustomAttr<K, V::Cloneable>;\n    type CloneableOwned = CustomAttr<K, V::CloneableOwned>;\n\n    fn html_len(&self) -> usize {\n        self.key.as_ref().len() + 3 + self.value.html_len()\n    }\n\n    fn to_html(\n        self,\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n        self.value.to_html(self.key.as_ref(), buf);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !K::KEY.is_empty() {\n            self.value.hydrate::<FROM_SERVER>(self.key.as_ref(), el)\n        } else {\n            self.value.build(el, self.key.as_ref())\n        }\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        self.value.build(el, self.key.as_ref())\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.value.rebuild(self.key.as_ref(), state);\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        CustomAttr {\n            key: self.key,\n            value: self.value.into_cloneable(),\n        }\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        CustomAttr {\n            key: self.key,\n            value: self.value.into_cloneable_owned(),\n        }\n    }\n\n    fn dry_resolve(&mut self) {\n        self.value.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        CustomAttr {\n            key: self.key,\n            value: self.value.resolve().await,\n        }\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![NamedAttributeKey::Attribute(\n            self.key.as_ref().to_string().into(),\n        )]\n    }\n}\n\nimpl<K, V> NextAttribute for CustomAttr<K, V>\nwhere\n    K: CustomAttributeKey,\n    V: AttributeValue,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\nimpl<K, V> ToTemplate for CustomAttr<K, V>\nwhere\n    K: CustomAttributeKey,\n    V: AttributeValue,\n{\n    fn to_template(\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n        if !K::KEY.is_empty() {\n            V::to_template(K::KEY, buf);\n        }\n    }\n}\n\n// TODO this needs to be a method, not a const\n/// Defines a custom attribute key.\npub trait CustomAttributeKey: Clone + AsRef<str> + Send + 'static {\n    /// The attribute name.\n    const KEY: &'static str;\n}\n\nimpl CustomAttributeKey for &'static str {\n    const KEY: &'static str = \"\";\n}\n\nimpl CustomAttributeKey for Cow<'static, str> {\n    const KEY: &'static str = \"\";\n}\n\nimpl CustomAttributeKey for String {\n    const KEY: &'static str = \"\";\n}\n\nimpl CustomAttributeKey for Arc<str> {\n    const KEY: &'static str = \"\";\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\nimpl<const K: &'static str> CustomAttributeKey\n    for crate::view::static_types::Static<K>\n{\n    const KEY: &'static str = K;\n}\n\n/// Adds a custom attribute to an element.\npub trait CustomAttribute<K, V>\nwhere\n    K: CustomAttributeKey,\n    V: AttributeValue,\n\n    Self: Sized + AddAnyAttr,\n{\n    /// Adds an HTML attribute by key and value.\n    fn attr(\n        self,\n        key: K,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<CustomAttr<K, V>> {\n        self.add_any_attr(custom_attribute(key, value))\n    }\n}\n\nimpl<T, K, V> CustomAttribute<K, V> for T\nwhere\n    T: AddAnyAttr,\n    K: CustomAttributeKey,\n    V: AttributeValue,\n{\n}\n"
  },
  {
    "path": "tachys/src/html/attribute/global.rs",
    "content": "use super::Lang;\nuse crate::{\n    html::{\n        attribute::*,\n        class::{class, Class, IntoClass},\n        element::{ElementType, HasElementType, HtmlElement},\n        event::{on, on_target, EventDescriptor, On, Targeted},\n        property::{prop, IntoProperty, Property},\n        style::{style, IntoStyle, Style},\n    },\n    prelude::RenderHtml,\n    view::add_attr::AddAnyAttr,\n};\nuse core::convert::From;\n\n/// Adds an attribute that modifies the `class`.\npub trait ClassAttribute<C>\nwhere\n    C: IntoClass,\n{\n    /// The type of the element with the new attribute added.\n    type Output;\n\n    /// Adds a CSS class to an element.\n    fn class(self, value: C) -> Self::Output;\n}\n\nimpl<E, At, Ch, C> ClassAttribute<C> for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n    C: IntoClass,\n{\n    type Output = <Self as AddAnyAttr>::Output<Class<C>>;\n\n    fn class(self, value: C) -> Self::Output {\n        self.add_any_attr(class(value))\n    }\n}\n\n/// Adds an attribute that modifies the DOM properties.\npub trait PropAttribute<K, P>\nwhere\n    P: IntoProperty,\n{\n    /// The type of the element with the new attribute added.\n    type Output;\n\n    /// Adds a DOM property to an element.\n    fn prop(self, key: K, value: P) -> Self::Output;\n}\n\nimpl<E, At, Ch, K, P> PropAttribute<K, P> for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n    K: AsRef<str> + Send,\n    P: IntoProperty,\n{\n    type Output = <Self as AddAnyAttr>::Output<Property<K, P>>;\n\n    fn prop(self, key: K, value: P) -> Self::Output {\n        self.add_any_attr(prop(key, value))\n    }\n}\n\n/// Adds an attribute that modifies the CSS styles.\npub trait StyleAttribute<S>\nwhere\n    S: IntoStyle,\n{\n    /// The type of the element with the new attribute added.\n    type Output;\n\n    /// Adds a CSS style to an element.\n    fn style(self, value: S) -> Self::Output;\n}\n\nimpl<E, At, Ch, S> StyleAttribute<S> for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n    S: IntoStyle,\n{\n    type Output = <Self as AddAnyAttr>::Output<Style<S>>;\n\n    fn style(self, value: S) -> Self::Output {\n        self.add_any_attr(style(value))\n    }\n}\n\n/// Adds an event listener to an element definition.\npub trait OnAttribute<E, F> {\n    /// The type of the element with the event listener added.\n    type Output;\n\n    /// Adds an event listener to an element.\n    fn on(self, event: E, cb: F) -> Self::Output;\n}\n\nimpl<El, At, Ch, E, F> OnAttribute<E, F> for HtmlElement<El, At, Ch>\nwhere\n    El: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n    E: EventDescriptor + Send + 'static,\n    E::EventType: 'static,\n    E::EventType: From<crate::renderer::types::Event>,\n    F: FnMut(E::EventType) + 'static,\n{\n    type Output = <Self as AddAnyAttr>::Output<On<E, F>>;\n\n    fn on(self, event: E, cb: F) -> Self::Output {\n        self.add_any_attr(on(event, cb))\n    }\n}\n\n/// Adds an event listener with a typed target to an element definition.\npub trait OnTargetAttribute<E, F, T> {\n    /// The type of the element with the new attribute added.\n    type Output;\n\n    /// Adds an event listener with a typed target to an element definition.\n    fn on_target(self, event: E, cb: F) -> Self::Output;\n}\n\nimpl<El, At, Ch, E, F> OnTargetAttribute<E, F, Self> for HtmlElement<El, At, Ch>\nwhere\n    El: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n    E: EventDescriptor + Send + 'static,\n    E::EventType: 'static,\n    E::EventType: From<crate::renderer::types::Event>,\n    F: FnMut(Targeted<E::EventType, <Self as HasElementType>::ElementType>)\n        + 'static,\n{\n    type Output =\n        <Self as AddAnyAttr>::Output<On<E, Box<dyn FnMut(E::EventType)>>>;\n\n    fn on_target(self, event: E, cb: F) -> Self::Output {\n        self.add_any_attr(on_target::<E, HtmlElement<El, At, Ch>, F>(event, cb))\n    }\n}\n\n/// Global attributes can be added to any HTML element.\npub trait GlobalAttributes<V>\nwhere\n    Self: Sized + AddAnyAttr,\n    V: AttributeValue,\n{\n    /// The `accesskey` global attribute provides a hint for generating a keyboard shortcut for the current element.\n    fn accesskey(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Accesskey, V>> {\n        self.add_any_attr(accesskey(value))\n    }\n\n    /// The `autocapitalize` global attribute controls whether and how text input is automatically capitalized as it is entered/edited by the user.\n    fn autocapitalize(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Autocapitalize, V>> {\n        self.add_any_attr(autocapitalize(value))\n    }\n\n    /// The `autofocus` global attribute is a Boolean attribute indicating that an element should receive focus as soon as the page is loaded.\n    fn autofocus(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Autofocus, V>> {\n        self.add_any_attr(autofocus(value))\n    }\n\n    /// The `contenteditable` global attribute is an enumerated attribute indicating if the element should be editable by the user.\n    fn contenteditable(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Contenteditable, V>> {\n        self.add_any_attr(contenteditable(value))\n    }\n\n    /// The `dir` global attribute is an enumerated attribute indicating the directionality of the element's text.\n    fn dir(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Dir, V>> {\n        self.add_any_attr(dir(value))\n    }\n\n    /// The `draggable` global attribute is an enumerated attribute indicating whether the element can be dragged.\n    fn draggable(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Draggable, V>> {\n        self.add_any_attr(draggable(value))\n    }\n\n    /// The `enterkeyhint` global attribute is used to customize the enter key on virtual keyboards.\n    fn enterkeyhint(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Enterkeyhint, V>> {\n        self.add_any_attr(enterkeyhint(value))\n    }\n\n    /// The `exportparts` attribute enables the sharing of parts of an element's shadow DOM with a containing document.\n    fn exportparts(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Exportparts, V>> {\n        self.add_any_attr(exportparts(value))\n    }\n\n    /// The `hidden` global attribute is a Boolean attribute indicating that the element is not yet, or is no longer, relevant.\n    fn hidden(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Hidden, V>> {\n        self.add_any_attr(hidden(value))\n    }\n\n    /// The `id` global attribute defines a unique identifier (ID) which must be unique in the whole document.\n    fn id(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Id, V>> {\n        self.add_any_attr(id(value))\n    }\n\n    /// The `inert` global attribute is a Boolean attribute that makes an element behave inertly.\n    fn inert(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Inert, V>> {\n        self.add_any_attr(inert(value))\n    }\n\n    /// The `inputmode` global attribute provides a hint to browsers for which virtual keyboard to display.\n    fn inputmode(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Inputmode, V>> {\n        self.add_any_attr(inputmode(value))\n    }\n\n    /// The `is` global attribute allows you to specify that a standard HTML element should behave like a custom built-in element.\n    fn is(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Is, V>> {\n        self.add_any_attr(is(value))\n    }\n\n    /// The `itemid` global attribute is used to specify the unique, global identifier of an item.\n    fn itemid(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Itemid, V>> {\n        self.add_any_attr(itemid(value))\n    }\n\n    /// The `itemprop` global attribute is used to add properties to an item.\n    fn itemprop(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Itemprop, V>> {\n        self.add_any_attr(itemprop(value))\n    }\n\n    /// The `itemref` global attribute is used to refer to other elements.\n    fn itemref(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Itemref, V>> {\n        self.add_any_attr(itemref(value))\n    }\n\n    /// The `itemscope` global attribute is used to create a new item.\n    fn itemscope(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Itemscope, V>> {\n        self.add_any_attr(itemscope(value))\n    }\n\n    /// The `itemtype` global attribute is used to specify the types of items.\n    fn itemtype(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Itemtype, V>> {\n        self.add_any_attr(itemtype(value))\n    }\n\n    /// The `lang` global attribute helps define the language of an element.\n    fn lang(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Lang, V>> {\n        self.add_any_attr(lang(value))\n    }\n\n    /// The `nonce` global attribute is used to specify a cryptographic nonce.\n    fn nonce(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Nonce, V>> {\n        self.add_any_attr(nonce(value))\n    }\n\n    /// The `part` global attribute identifies the element as a part of a component.\n    fn part(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Part, V>> {\n        self.add_any_attr(part(value))\n    }\n\n    /// The `popover` global attribute defines the popover's behavior.\n    fn popover(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Popover, V>> {\n        self.add_any_attr(popover(value))\n    }\n\n    /// The `role` global attribute defines the role of an element in ARIA.\n    fn role(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Role, V>> {\n        self.add_any_attr(role(value))\n    }\n\n    /// The `slot` global attribute assigns a slot in a shadow DOM.\n    fn slot(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Slot, V>> {\n        self.add_any_attr(slot(value))\n    }\n\n    /// The `spellcheck` global attribute is an enumerated attribute that defines whether the element may be checked for spelling errors.\n    fn spellcheck(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Spellcheck, V>> {\n        self.add_any_attr(spellcheck(value))\n    }\n\n    /// The `tabindex` global attribute indicates if the element can take input focus.\n    fn tabindex(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Tabindex, V>> {\n        self.add_any_attr(tabindex(value))\n    }\n\n    /// The `title` global attribute contains text representing advisory information.\n    fn title(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Title, V>> {\n        self.add_any_attr(title(value))\n    }\n\n    /// The `translate` global attribute is an enumerated attribute that specifies whether an element's attribute values and text content should be translated when the page is localized.\n    fn translate(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Translate, V>> {\n        self.add_any_attr(translate(value))\n    }\n\n    /// The `virtualkeyboardpolicy` global attribute specifies the behavior of the virtual keyboard.\n    fn virtualkeyboardpolicy(\n        self,\n        value: V,\n    ) -> <Self as AddAnyAttr>::Output<Attr<Virtualkeyboardpolicy, V>> {\n        self.add_any_attr(virtualkeyboardpolicy(value))\n    }\n}\n\nimpl<El, At, Ch, V> GlobalAttributes<V> for HtmlElement<El, At, Ch>\nwhere\n    El: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n    V: AttributeValue,\n{\n}\n\nmacro_rules! on_definitions {\n\t($(#[$meta:meta] $key:ident $html:literal),* $(,)?) => {\n        paste::paste! {\n            $(\n                #[doc = concat!(\"Adds the HTML `\", $html, \"` attribute to the element.\\n\\n**Note**: This is the HTML attribute, which takes a JavaScript string, not an `on:` listener that takes application logic written in Rust.\")]\n                #[track_caller]\n                fn $key(\n                    self,\n                    value: V,\n                ) -> <Self as AddAnyAttr>::Output<Attr<[<$key:camel>], V>>\n                {\n                    self.add_any_attr($key(value))\n                }\n            )*\n\t\t}\n    }\n}\n\n/// Provides methods for HTML event listener attributes.\npub trait GlobalOnAttributes<V>\nwhere\n    Self: Sized + AddAnyAttr,\n    V: AttributeValue,\n{\n    on_definitions! {\n        /// The `onabort` attribute specifies the event handler for the abort event.\n        onabort \"onabort\",\n        /// The `onautocomplete` attribute specifies the event handler for the autocomplete event.\n        onautocomplete \"onautocomplete\",\n        /// The `onautocompleteerror` attribute specifies the event handler for the autocompleteerror event.\n        onautocompleteerror \"onautocompleteerror\",\n        /// The `onblur` attribute specifies the event handler for the blur event.\n        onblur \"onblur\",\n        /// The `oncancel` attribute specifies the event handler for the cancel event.\n        oncancel \"oncancel\",\n        /// The `oncanplay` attribute specifies the event handler for the canplay event.\n        oncanplay \"oncanplay\",\n        /// The `oncanplaythrough` attribute specifies the event handler for the canplaythrough event.\n        oncanplaythrough \"oncanplaythrough\",\n        /// The `onchange` attribute specifies the event handler for the change event.\n        onchange \"onchange\",\n        /// The `onclick` attribute specifies the event handler for the click event.\n        onclick \"onclick\",\n        /// The `onclose` attribute specifies the event handler for the close event.\n        onclose \"onclose\",\n        /// The `oncontextmenu` attribute specifies the event handler for the contextmenu event.\n        oncontextmenu \"oncontextmenu\",\n        /// The `oncuechange` attribute specifies the event handler for the cuechange event.\n        oncuechange \"oncuechange\",\n        /// The `ondblclick` attribute specifies the event handler for the double click event.\n        ondblclick \"ondblclick\",\n        /// The `ondrag` attribute specifies the event handler for the drag event.\n        ondrag \"ondrag\",\n        /// The `ondragend` attribute specifies the event handler for the dragend event.\n        ondragend \"ondragend\",\n        /// The `ondragenter` attribute specifies the event handler for the dragenter event.\n        ondragenter \"ondragenter\",\n        /// The `ondragleave` attribute specifies the event handler for the dragleave event.\n        ondragleave \"ondragleave\",\n        /// The `ondragover` attribute specifies the event handler for the dragover event.\n        ondragover \"ondragover\",\n        /// The `ondragstart` attribute specifies the event handler for the dragstart event.\n        ondragstart \"ondragstart\",\n        /// The `ondrop` attribute specifies the event handler for the drop event.\n        ondrop \"ondrop\",\n        /// The `ondurationchange` attribute specifies the event handler for the durationchange event.\n        ondurationchange \"ondurationchange\",\n        /// The `onemptied` attribute specifies the event handler for the emptied event.\n        onemptied \"onemptied\",\n        /// The `onended` attribute specifies the event handler for the ended event.\n        onended \"onended\",\n        /// The `onerror` attribute specifies the event handler for the error event.\n        onerror \"onerror\",\n        /// The `onfocus` attribute specifies the event handler for the focus event.\n        onfocus \"onfocus\",\n        /// The `onformdata` attribute specifies the event handler for the formdata event.\n        onformdata \"onformdata\",\n        /// The `oninput` attribute specifies the event handler for the input event.\n        oninput \"oninput\",\n        /// The `oninvalid` attribute specifies the event handler for the invalid event.\n        oninvalid \"oninvalid\",\n        /// The `onkeydown` attribute specifies the event handler for the keydown event.\n        onkeydown \"onkeydown\",\n        /// The `onkeypress` attribute specifies the event handler for the keypress event.\n        onkeypress \"onkeypress\",\n        /// The `onkeyup` attribute specifies the event handler for the keyup event.\n        onkeyup \"onkeyup\",\n        /// The `onlanguagechange` attribute specifies the event handler for the languagechange event.\n        onlanguagechange \"onlanguagechange\",\n        /// The `onload` attribute specifies the event handler for the load event.\n        onload \"onload\",\n        /// The `onloadeddata` attribute specifies the event handler for the loadeddata event.\n        onloadeddata \"onloadeddata\",\n        /// The `onloadedmetadata` attribute specifies the event handler for the loadedmetadata event.\n        onloadedmetadata \"onloadedmetadata\",\n        /// The `onloadstart` attribute specifies the event handler for the loadstart event.\n        onloadstart \"onloadstart\",\n        /// The `onmousedown` attribute specifies the event handler for the mousedown event.\n        onmousedown \"onmousedown\",\n        /// The `onmouseenter` attribute specifies the event handler for the mouseenter event.\n        onmouseenter \"onmouseenter\",\n        /// The `onmouseleave` attribute specifies the event handler for the mouseleave event.\n        onmouseleave \"onmouseleave\",\n        /// The `onmousemove` attribute specifies the event handler for the mousemove event.\n        onmousemove \"onmousemove\",\n        /// The `onmouseout` attribute specifies the event handler for the mouseout event.\n        onmouseout \"onmouseout\",\n        /// The `onmouseover` attribute specifies the event handler for the mouseover event.\n        onmouseover \"onmouseover\",\n        /// The `onmouseup` attribute specifies the event handler for the mouseup event.\n        onmouseup \"onmouseup\",\n        /// The `onpause` attribute specifies the event handler for the pause event.\n        onpause \"onpause\",\n        /// The `onplay` attribute specifies the event handler for the play event.\n        onplay \"onplay\",\n        /// The `onplaying` attribute specifies the event handler for the playing event.\n        onplaying \"onplaying\",\n        /// The `onprogress` attribute specifies the event handler for the progress event.\n        onprogress \"onprogress\",\n        /// The `onratechange` attribute specifies the event handler for the ratechange event.\n        onratechange \"onratechange\",\n        /// The `onreset` attribute specifies the event handler for the reset event.\n        onreset \"onreset\",\n        /// The `onresize` attribute specifies the event handler for the resize event.\n        onresize \"onresize\",\n        /// The `onscroll` attribute specifies the event handler for the scroll event.\n        onscroll \"onscroll\",\n        /// The `onsecuritypolicyviolation` attribute specifies the event handler for the securitypolicyviolation event.\n        onsecuritypolicyviolation \"onsecuritypolicyviolation\",\n        /// The `onseeked` attribute specifies the event handler for the seeked event.\n        onseeked \"onseeked\",\n        /// The `onseeking` attribute specifies the event handler for the seeking event.\n        onseeking \"onseeking\",\n        /// The `onselect` attribute specifies the event handler for the select event.\n        onselect \"onselect\",\n        /// The `onslotchange` attribute specifies the event handler for the slotchange event.\n        onslotchange \"onslotchange\",\n        /// The `onstalled` attribute specifies the event handler for the stalled event.\n        onstalled \"onstalled\",\n        /// The `onsubmit` attribute specifies the event handler for the submit event.\n        onsubmit \"onsubmit\",\n        /// The `onsuspend` attribute specifies the event handler for the suspend event.\n        onsuspend \"onsuspend\",\n        /// The `ontimeupdate` attribute specifies the event handler for the timeupdate event.\n        ontimeupdate \"ontimeupdate\",\n        /// The `ontoggle` attribute specifies the event handler for the toggle event.\n        ontoggle \"ontoggle\",\n        /// The `onvolumechange` attribute specifies the event handler for the volumechange event.\n        onvolumechange \"onvolumechange\",\n        /// The `onwaiting` attribute specifies the event handler for the waiting event.\n        onwaiting \"onwaiting\",\n        /// The `onwebkitanimationend` attribute specifies the event handler for the webkitanimationend event.\n        onwebkitanimationend \"onwebkitanimationend\",\n        /// The `onwebkitanimationiteration` attribute specifies the event handler for the webkitanimationiteration event.\n        onwebkitanimationiteration \"onwebkitanimationiteration\",\n        /// The `onwebkitanimationstart` attribute specifies the event handler for the webkitanimationstart event.\n        onwebkitanimationstart \"onwebkitanimationstart\",\n        /// The `onwebkittransitionend` attribute specifies the event handler for the webkittransitionend event.\n        onwebkittransitionend \"onwebkittransitionend\",\n        /// The `onwheel` attribute specifies the event handler for the wheel event.\n        onwheel \"onwheel\",\n\n    }\n}\n\nimpl<El, At, Ch, V> GlobalOnAttributes<V> for HtmlElement<El, At, Ch>\nwhere\n    El: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n    V: AttributeValue,\n{\n}\n"
  },
  {
    "path": "tachys/src/html/attribute/key.rs",
    "content": "use super::{Attr, AttributeValue};\nuse std::fmt::Debug;\n\n/// An HTML attribute key.\npub trait AttributeKey: Clone + Send + 'static {\n    /// The name of the attribute.\n    const KEY: &'static str;\n}\n\nmacro_rules! attributes {\n\t($(#[$meta:meta] $key:ident $html:literal),* $(,)?) => {\n        paste::paste! {\n            $(\n                #[$meta]\n                #[track_caller]\n                pub fn $key<V>(value: V) -> Attr<[<$key:camel>], V>\n\t\t\t\twhere V: AttributeValue,\n\n                {\n                    Attr([<$key:camel>], value)\n                }\n\n                #[$meta]\n\t\t\t\t#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n\t\t\t\tpub struct [<$key:camel>];\n\n\t\t\t\timpl AttributeKey for [<$key:camel>] {\n\t\t\t\t\tconst KEY: &'static str = $html;\n\t\t\t\t}\n            )*\n\t\t}\n    }\n}\n\nattributes! {\n    // HTML\n    /// The `abbr` attribute specifies an abbreviated form of the element's content.\n    abbr \"abbr\",\n    /// The `accept-charset` attribute specifies the character encodings that are to be used for the form submission.\n    accept_charset \"accept-charset\",\n    /// The `accept` attribute specifies a list of types the server accepts, typically a file type.\n    accept \"accept\",\n    /// The `accesskey` attribute specifies a shortcut key to activate or focus an element.\n    accesskey \"accesskey\",\n    /// The `action` attribute defines the URL to which the form data will be sent.\n    action \"action\",\n    /// The `align` attribute specifies the alignment of an element.\n    align \"align\",\n    /// The `allow` attribute defines a feature policy for the content in an iframe.\n    allow \"allow\",\n    /// The `allowfullscreen` attribute allows the iframe to be displayed in fullscreen mode.\n    allowfullscreen \"allowfullscreen\",\n    /// The `allowpaymentrequest` attribute allows a cross-origin iframe to invoke the Payment Request API.\n    allowpaymentrequest \"allowpaymentrequest\",\n    /// The `alt` attribute provides alternative text for an image, if the image cannot be displayed.\n    alt \"alt\",\n    // ARIA\n    /// The `aria-activedescendant` attribute identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.\n    aria_activedescendant \"aria-activedescendant\",\n    /// The `aria-atomic` attribute indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute.\n    aria_atomic \"aria-atomic\",\n    /// The `aria-autocomplete` attribute indicates whether user input completion suggestions are provided.\n    aria_autocomplete \"aria-autocomplete\",\n    /// The `aria-busy` attribute indicates whether an element, and its subtree, are currently being updated.\n    aria_busy \"aria-busy\",\n    /// The `aria-checked` attribute indicates the current \"checked\" state of checkboxes, radio buttons, and other widgets.\n    aria_checked \"aria-checked\",\n    /// The `aria-colcount` attribute defines the total number of columns in a table, grid, or treegrid.\n    aria_colcount \"aria-colcount\",\n    /// The `aria-colindex` attribute defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.\n    aria_colindex \"aria-colindex\",\n    /// The `aria-colspan` attribute defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.\n    aria_colspan \"aria-colspan\",\n    /// The `aria-controls` attribute identifies the element (or elements) whose contents or presence are controlled by the current element.\n    aria_controls \"aria-controls\",\n    /// The `aria-current` attribute indicates the element representing the current item within a container or set of related elements.\n    aria_current \"aria-current\",\n    /// The `aria-describedby` attribute identifies the element (or elements) that describes the object.\n    aria_describedby \"aria-describedby\",\n    /// The `aria-description` attribute provides a string value that describes or annotates the current element.\n    aria_description \"aria-description\",\n    /// The `aria-details` attribute identifies the element that provides a detailed, extended description for the object.\n    aria_details \"aria-details\",\n    /// The `aria-disabled` attribute indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.\n    aria_disabled \"aria-disabled\",\n    /// The `aria-dropeffect` attribute indicates what functions can be performed when a dragged object is released on the drop target.\n    aria_dropeffect \"aria-dropeffect\",\n    /// The `aria-errormessage` attribute identifies the element that provides an error message for the object.\n    aria_errormessage \"aria-errormessage\",\n    /// The `aria-expanded` attribute indicates whether an element, or another grouping element it controls, is currently expanded or collapsed.\n    aria_expanded \"aria-expanded\",\n    /// The `aria-flowto` attribute identifies the next element (or elements) in an alternate reading order of content.\n    aria_flowto \"aria-flowto\",\n    /// The `aria-grabbed` attribute indicates an element's \"grabbed\" state in a drag-and-drop operation.\n    aria_grabbed \"aria-grabbed\",\n    /// The `aria-haspopup` attribute indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element.\n    aria_haspopup \"aria-haspopup\",\n    /// The `aria-hidden` attribute indicates whether the element is exposed to an accessibility API.\n    aria_hidden \"aria-hidden\",\n    /// The `aria-invalid` attribute indicates the entered value does not conform to the format expected by the application.\n    aria_invalid \"aria-invalid\",\n    /// The `aria-keyshortcuts` attribute indicates keyboard shortcuts that an author has implemented to activate or give focus to an element.\n    aria_keyshortcuts \"aria-keyshortcuts\",\n    /// The `aria-label` attribute defines a string value that labels the current element.\n    aria_label \"aria-label\",\n    /// The `aria-labelledby` attribute identifies the element (or elements) that labels the current element.\n    aria_labelledby \"aria-labelledby\",\n    /// The `aria-live` attribute indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region.\n    aria_live \"aria-live\",\n    /// The `aria-modal` attribute indicates whether an element is modal when displayed.\n    aria_modal \"aria-modal\",\n    /// The `aria-multiline` attribute indicates whether a text box accepts multiple lines of input or only a single line.\n    aria_multiline \"aria-multiline\",\n    /// The `aria-multiselectable` attribute indicates that the user may select more than one item from the current selectable descendants.\n    aria_multiselectable \"aria-multiselectable\",\n    /// The `aria-orientation` attribute indicates whether the element's orientation is horizontal, vertical, or unknown/ambiguous.\n    aria_orientation \"aria-orientation\",\n    /// The `aria-owns` attribute identifies an element (or elements) in order to define a relationship between the element with `aria-owns` and the target element.\n    aria_owns \"aria-owns\",\n    /// The `aria-placeholder` attribute defines a short hint (a word or short phrase) intended to aid the user with data entry when the control has no value.\n    aria_placeholder \"aria-placeholder\",\n    /// The `aria-posinset` attribute defines an element's position within a set or treegrid.\n    aria_posinset \"aria-posinset\",\n    /// The `aria-pressed` attribute indicates the current \"pressed\" state of toggle buttons.\n    aria_pressed \"aria-pressed\",\n    /// The `aria-readonly` attribute indicates that the element is not editable, but is otherwise operable.\n    aria_readonly \"aria-readonly\",\n    /// The `aria-relevant` attribute indicates what user agent changes to the accessibility tree should be monitored.\n    aria_relevant \"aria-relevant\",\n    /// The `aria-required` attribute indicates that user input is required on the element before a form may be submitted.\n    aria_required \"aria-required\",\n    /// The `aria-roledescription` attribute defines a human-readable, author-localized description for the role of an element.\n    aria_roledescription \"aria-roledescription\",\n    /// The `aria-rowcount` attribute defines the total number of rows in a table, grid, or treegrid.\n    aria_rowcount \"aria-rowcount\",\n    /// The `aria-rowindex` attribute defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.\n    aria_rowindex \"aria-rowindex\",\n    /// The `aria-rowspan` attribute defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.\n    aria_rowspan \"aria-rowspan\",\n    /// The `aria-selected` attribute indicates the current \"selected\" state of various widgets.\n    aria_selected \"aria-selected\",\n    /// The `aria-setsize` attribute defines the number of items in the current set of listitems or treeitems.\n    aria_setsize \"aria-setsize\",\n    /// The `aria-sort` attribute indicates if items in a table or grid are sorted in ascending or descending order.\n    aria_sort \"aria-sort\",\n    /// The `aria-valuemax` attribute defines the maximum allowed value for a range widget.\n    aria_valuemax \"aria-valuemax\",\n    /// The `aria-valuemin` attribute defines the minimum allowed value for a range widget.\n    aria_valuemin \"aria-valuemin\",\n    /// The `aria-valuenow` attribute defines the current value for a range widget.\n    aria_valuenow \"aria-valuenow\",\n    /// The `aria-valuetext` attribute defines the human-readable text alternative of aria-valuenow for a range widget.\n    aria_valuetext \"aria-valuetext\",\n    /// The `as` attribute specifies the type of destination for the content of the link.\n    r#as \"as\",\n    /// The `async` attribute indicates that the script should be executed asynchronously.\n    r#async \"async\",\n    /// The `attributionsrc` attribute indicates that you want the browser to send an `Attribution-Reporting-Eligible` header along with a request.\n    attributionsrc \"attributionsrc\",\n    /// The `autocapitalize` attribute controls whether and how text input is automatically capitalized as it is entered/edited by the user.\n    autocapitalize \"autocapitalize\",\n    /// The `autocomplete` attribute indicates whether an input field can have its value automatically completed by the browser.\n    autocomplete \"autocomplete\",\n    /// The `autofocus` attribute indicates that an element should be focused on page load.\n    autofocus \"autofocus\",\n    /// The `autoplay` attribute indicates that the media should start playing as soon as it is loaded.\n    autoplay \"autoplay\",\n    /// The `background` attribute sets the URL of the background image for the document.\n    background \"background\",\n    /// The `bgcolor` attribute sets the background color of an element.\n    bgcolor \"bgcolor\",\n    /// The `blocking` attribute indicates that the script will block the page loading until it is executed.\n    blocking \"blocking\",\n    /// The `border` attribute sets the width of an element's border.\n    border \"border\",\n    /// The `buffered` attribute contains the time ranges that the media has been buffered.\n    buffered \"buffered\",\n    /// The `capture` attribute indicates that the user must capture media using a camera or microphone instead of selecting a file from the file picker.\n    capture \"capture\",\n    /// The `challenge` attribute specifies the challenge string that is paired with the keygen element.\n    challenge \"challenge\",\n    /// The `closedby` attribute specifies the types of user actions that can be used to close the associated `<dialog>` element.\n    closedby \"closedby\",\n    /// The `charset` attribute specifies the character encoding of the HTML document.\n    charset \"charset\",\n    /// The `checked` attribute indicates whether an input element is checked or not.\n    checked \"checked\",\n    /// The `cite` attribute contains a URL that points to the source of the quotation or change.\n    cite \"cite\",\n    // class is handled in ../class.rs instead\n    //class \"class\",\n    /// The `code` attribute specifies the URL of the applet's class file to be loaded and executed.\n    code \"code\",\n    /// The `color` attribute specifies the color of an element's text.\n    color \"color\",\n    /// The `cols` attribute specifies the visible width of a text area.\n    cols \"cols\",\n    /// The `colspan` attribute defines the number of columns a cell should span.\n    colspan \"colspan\",\n    /// The `command` attribute defines the command to be invoked when user clicks the `<button>` element which has `commandfor` attribute specified.\n    command \"command\",\n    /// The `commandfor` attribute defines the id of the element which button is controlling. It is generic version of `popovertarget`.\n    commandfor \"commandfor\",\n    /// The `content` attribute gives the value associated with the http-equiv or name attribute.\n    content \"content\",\n    /// The `contenteditable` attribute indicates whether the element's content is editable.\n    contenteditable \"contenteditable\",\n    /// The `contextmenu` attribute specifies the ID of a `<menu>` element to open as a context menu.\n    contextmenu \"contextmenu\",\n    /// The `controls` attribute indicates whether the browser should display playback controls for the media.\n    controls \"controls\",\n    /// The `controlslist` attribute allows the control of which controls to show on the media element whenever the browser shows its native controls.\n    controlslist \"controlslist\",\n    /// The `coords` attribute specifies the coordinates of an area in an image map.\n    coords \"coords\",\n    /// The `crossorigin` attribute indicates whether the resource should be fetched with a CORS request.\n    crossorigin \"crossorigin\",\n    /// The `csp` attribute allows the embedding document to define the Content Security Policy that an embedded document must agree to enforce upon itself.\n    csp \"csp\",\n    /// The `data` attribute specifies the URL of the resource that is being embedded.\n    data \"data\",\n    /// The `datetime` attribute specifies the date and time.\n    datetime \"datetime\",\n    /// The `decoding` attribute indicates the preferred method for decoding images.\n    decoding \"decoding\",\n    /// The `default` attribute indicates that the track should be enabled unless the user's preferences indicate that another track is more appropriate.\n    default \"default\",\n    /// The `defer` attribute indicates that the script should be executed after the document has been parsed.\n    defer \"defer\",\n    /// The `dir` attribute specifies the text direction for the content in an element.\n    dir \"dir\",\n    /// The `dirname` attribute identifies the text directionality of an input element.\n    dirname \"dirname\",\n    /// The `disabled` attribute indicates whether the element is disabled.\n    disabled \"disabled\",\n    /// The `disablepictureinpicture` attribute indicates that the element is not allowed to be displayed in Picture-in-Picture mode.\n    disablepictureinpicture \"disablepictureinpicture\",\n    /// The `disableremoteplayback` attribute indicates that the element is not allowed to be displayed using remote playback.\n    disableremoteplayback \"disableremoteplayback\",\n    /// The `download` attribute indicates that the linked resource is intended to be downloaded rather than displayed in the browser.\n    download \"download\",\n    /// The `draggable` attribute indicates whether the element is draggable.\n    draggable \"draggable\",\n    /// The `elementtiming` attributes marks the element for observation by the `PerformanceElementTiming` API.\n    elementtiming \"elementtiming\",\n    /// The `enctype` attribute specifies the MIME type of the form submission.\n    enctype \"enctype\",\n    /// The `enterkeyhint` attribute allows authors to specify what kind of action label or icon will be presented to users in a virtual keyboard's enter key.\n    enterkeyhint \"enterkeyhint\",\n    /// The `exportparts` attribute enables the sharing of parts of an element's shadow DOM with a containing document.\n    exportparts \"exportparts\",\n    /// The `fetchpriority` attribute allows developers to specify the priority of a resource fetch request.\n    fetchpriority \"fetchpriority\",\n    /// The `for` attribute specifies which form element a label is bound to.\n    r#for \"for\",\n    /// The `form` attribute associates the element with a form element.\n    form \"form\",\n    /// The `formaction` attribute specifies the URL that processes the form submission.\n    formaction \"formaction\",\n    /// The `formenctype` attribute specifies how the form data should be encoded when submitted.\n    formenctype \"formenctype\",\n    /// The `formmethod` attribute specifies the HTTP method to use when submitting the form.\n    formmethod \"formmethod\",\n    /// The `formnovalidate` attribute indicates that the form should not be validated when submitted.\n    formnovalidate \"formnovalidate\",\n    /// The `formtarget` attribute specifies where to display the response after submitting the form.\n    formtarget \"formtarget\",\n    /// The `headers` attribute specifies the headers associated with the element.\n    headers \"headers\",\n    /// The `height` attribute specifies the height of an element.\n    height \"height\",\n    /// The `hidden` attribute indicates that the element is not yet, or is no longer, relevant.\n    hidden \"hidden\",\n    /// The `high` attribute specifies the range that is considered to be a high value.\n    high \"high\",\n    /// The `href` attribute specifies the URL of a linked resource.\n    href \"href\",\n    /// The `hreflang` attribute specifies the language of the linked resource.\n    hreflang \"hreflang\",\n    /// The `http-equiv` attribute provides an HTTP header for the information/value of the content attribute.\n    http_equiv \"http-equiv\",\n    /// The `icon` attribute specifies the URL of an image to be used as a graphical icon for the element.\n    icon \"icon\",\n    /// The `id` attribute specifies a unique id for an element.\n    id \"id\",\n    /// The `imagesizes` attribute specifies image sizes for different page layouts.\n    imagesizes \"imagesizes\",\n    /// The `imagesrcset` attribute specifies the URLs of multiple images to be used in different situations.\n    imagesrcset \"imagesrcset\",\n    /// The `importance` attribute specifies the relative importance of the element.\n    importance \"importance\",\n    /// The `inert` attribute indicates that the element is non-interactive and won't be accessible to user interactions or assistive technologies.\n    inert \"inert\",\n    /// The `inputmode` attribute specifies the type of data that the user will enter.\n    inputmode \"inputmode\",\n    /// The `integrity` attribute contains a hash value that the browser can use to verify that the resource hasn't been altered.\n    integrity \"integrity\",\n    /// The `intrinsicsize` attribute specifies the intrinsic size of an image or video.\n    intrinsicsize \"intrinsicsize\",\n    /// The `is` attribute allows you to specify the name of a custom element.\n    is \"is\",\n    /// The `ismap` attribute indicates that the image is part of a server-side image map.\n    ismap \"ismap\",\n    /// The `itemid` attribute assigns a unique identifier to an item.\n    itemid \"itemid\",\n    /// The `itemprop` attribute adds a property to an item.\n    itemprop \"itemprop\",\n    /// The `itemref` attribute provides a list of element IDs that have additional properties for the item.\n    itemref \"itemref\",\n    /// The `itemscope` attribute creates a new item and adds it to the page's items.\n    itemscope \"itemscope\",\n    /// The `itemtype` attribute specifies the type of an item.\n    itemtype \"itemtype\",\n    /// The `keytype` attribute specifies the type of key used by the `<keygen>` element.\n    keytype \"keytype\",\n    /// The `kind` attribute specifies the kind of text track.\n    kind \"kind\",\n    /// The `label` attribute provides a user-readable title for an element.\n    label \"label\",\n    /// The `lang` attribute specifies the language of the element's content.\n    lang \"lang\",\n    /// The `language` attribute specifies the scripting language used for the script.\n    language \"language\",\n    /// The `list` attribute identifies a `<datalist>` element that contains pre-defined options for an `<input>` element.\n    list \"list\",\n    /// The `loading` attribute indicates how the browser should load the image.\n    loading \"loading\",\n    /// The `loop` attribute indicates whether the media should start over again when it reaches the end.\n    r#loop \"loop\",\n    /// The `low` attribute specifies the range that is considered to be a low value.\n    low \"low\",\n    /// The `manifest` attribute specifies the URL of a document's cache manifest.\n    manifest \"manifest\",\n    /// The `max` attribute specifies the maximum value for an input element.\n    max \"max\",\n    /// The `maxlength` attribute specifies the maximum number of characters that an input element can accept.\n    maxlength \"maxlength\",\n    /// The `media` attribute specifies what media/device the linked resource is optimized for.\n    media \"media\",\n    /// The `method` attribute specifies the HTTP method to use when submitting the form.\n    method \"method\",\n    /// The `min` attribute specifies the minimum value for an input element.\n    min \"min\",\n    /// The `minlength` attribute specifies the minimum number of characters that an input element can accept.\n    minlength \"minlength\",\n    /// The `multiple` attribute indicates whether the user can enter more than one value.\n    multiple \"multiple\",\n    /// The `muted` attribute indicates whether the audio will be initially silenced on page load.\n    muted \"muted\",\n    /// The `name` attribute specifies the name of the element.\n    name \"name\",\n    /// The `nomodule` attribute indicates that the script should not be executed in browsers that support ES modules.\n    nomodule \"nomodule\",\n    /// The `nonce` attribute provides a cryptographic nonce to ensure that a script or style is approved for execution.\n    nonce \"nonce\",\n    /// The `novalidate` attribute indicates that the form should not be validated when submitted.\n    novalidate \"novalidate\",\n    /// The `open` attribute indicates whether the details element is open or closed.\n    open \"open\",\n    /// The `optimum` attribute specifies the range that is considered to be an optimum value.\n    optimum \"optimum\",\n    /// The `part` attribute identifies the element as a shadow DOM part.\n    part \"part\",\n    /// The `pattern` attribute specifies a regular expression that the input element's value is checked against.\n    pattern \"pattern\",\n    /// The `ping` attribute contains a space-separated list of URLs to be notified if the user follows the hyperlink.\n    ping \"ping\",\n    /// The `placeholder` attribute provides a short hint that describes the expected value of the input element.\n    placeholder \"placeholder\",\n    /// The `playsinline` attribute indicates that the video should play inline in the element's playback area.\n    playsinline \"playsinline\",\n    /// The `popover` attribute indicates that an element is a popover and specifies the event that causes the popover to be shown.\n    popover \"popover\",\n    /// The `popovertarget` attribute specifies the ID of an element to toggle a popover.\n    popovertarget \"popovertarget\",\n    /// The `popovertargetaction` attribute specifies the action that shows the popover.\n    popovertargetaction \"popovertargetaction\",\n    /// The `poster` attribute specifies an image to be shown while the video is downloading or until the user hits the play button.\n    poster \"poster\",\n    /// The `preload` attribute specifies if and how the author thinks that the media file should be loaded when the page loads.\n    preload \"preload\",\n    /// The `radiogroup` attribute specifies the name of the group to which the element belongs.\n    radiogroup \"radiogroup\",\n    /// The `readonly` attribute indicates that the user cannot modify the value of the input element.\n    readonly \"readonly\",\n    /// The `referrerpolicy` attribute specifies which referrer information to include with requests.\n    referrerpolicy \"referrerpolicy\",\n    /// The `rel` attribute specifies the relationship between the current document and the linked document.\n    rel \"rel\",\n    /// The `required` attribute indicates that the user must fill in the input element before submitting the form.\n    required \"required\",\n    /// The `reversed` attribute indicates that the list should be displayed in a descending order.\n    reversed \"reversed\",\n    /// The `role` attribute defines the role of an element in the context of a web application.\n    role \"role\",\n    /// The `rows` attribute specifies the number of visible text lines for a text area.\n    rows \"rows\",\n    /// The `rowspan` attribute defines the number of rows a cell should span.\n    rowspan \"rowspan\",\n    /// The `sandbox` attribute applies extra restrictions to the content in the `<iframe>`.\n    sandbox \"sandbox\",\n    /// The `scope` attribute specifies whether a header cell is a header for a column, row, or group of columns or rows.\n    scope \"scope\",\n    /// The `scoped` attribute indicates that the styles in a `<style>` element are scoped to the parent element.\n    scoped \"scoped\",\n    /// The `selected` attribute indicates that the option is selected.\n    selected \"selected\",\n    /// The `shape` attribute specifies the shape of the area.\n    shape \"shape\",\n    /// The `size` attribute specifies the width of the input element.\n    size \"size\",\n    /// The `sizes` attribute specifies the sizes of icons for visual media.\n    sizes \"sizes\",\n    /// The `slot` attribute assigns a slot to an element.\n    slot \"slot\",\n    /// The `span` attribute defines the number of columns in a `<colgroup>` or the number of rows in a `<rowgroup>`.\n    span \"span\",\n    /// The `spellcheck` attribute indicates whether spell checking is allowed for the element.\n    spellcheck \"spellcheck\",\n    /// The `src` attribute specifies the URL of the media resource.\n    src \"src\",\n    /// The `srcdoc` attribute specifies the HTML content of the page to show in the `<iframe>`.\n    srcdoc \"srcdoc\",\n    /// The `srclang` attribute specifies the language of the text track.\n    srclang \"srclang\",\n    /// The `srcset` attribute specifies the URLs of multiple images to be used in different situations.\n    srcset \"srcset\",\n    /// The `start` attribute specifies the start value of the list.\n    start \"start\",\n    /// The `step` attribute specifies the legal number intervals for an input element.\n    step \"step\",\n    // style is handled in ../style.rs instead\n    // style \"style\",\n    /// The `summary` attribute provides a summary of the content of the table.\n    summary \"summary\",\n    /// The `tabindex` attribute specifies the tab order of an element.\n    tabindex \"tabindex\",\n    /// The `target` attribute specifies where to open the linked document.\n    target \"target\",\n    /// The `title` attribute provides additional information about an element.\n    title \"title\",\n    /// The `translate` attribute specifies whether the content of an element should be translated or not.\n    translate \"translate\",\n    /// The `type` attribute specifies the type of the element.\n    r#type \"type\",\n    /// The `usemap` attribute specifies the image map to be used by an `<img>` element.\n    usemap \"usemap\",\n    /// The `value` attribute specifies the value of the element.\n    value \"value\",\n    /// The `virtualkeyboardpolicy` attribute controls the policy for virtual keyboards.\n    virtualkeyboardpolicy \"virtualkeyboardpolicy\",\n    /// The `width` attribute specifies the width of an element.\n    width \"width\",\n    /// The `wrap` attribute specifies how the text in a text area is to be wrapped when submitted in a form.\n    wrap \"wrap\",\n    // Event Handler Attributes\n    /// The `onabort` attribute specifies the event handler for the abort event.\n    onabort \"onabort\",\n    /// The `onautocomplete` attribute specifies the event handler for the autocomplete event.\n    onautocomplete \"onautocomplete\",\n    /// The `onautocompleteerror` attribute specifies the event handler for the autocompleteerror event.\n    onautocompleteerror \"onautocompleteerror\",\n    /// The `onblur` attribute specifies the event handler for the blur event.\n    onblur \"onblur\",\n    /// The `oncancel` attribute specifies the event handler for the cancel event.\n    oncancel \"oncancel\",\n    /// The `oncanplay` attribute specifies the event handler for the canplay event.\n    oncanplay \"oncanplay\",\n    /// The `oncanplaythrough` attribute specifies the event handler for the canplaythrough event.\n    oncanplaythrough \"oncanplaythrough\",\n    /// The `onchange` attribute specifies the event handler for the change event.\n    onchange \"onchange\",\n    /// The `onclick` attribute specifies the event handler for the click event.\n    onclick \"onclick\",\n    /// The `onclose` attribute specifies the event handler for the close event.\n    onclose \"onclose\",\n    /// The `oncontextmenu` attribute specifies the event handler for the contextmenu event.\n    oncontextmenu \"oncontextmenu\",\n    /// The `oncuechange` attribute specifies the event handler for the cuechange event.\n    oncuechange \"oncuechange\",\n    /// The `ondblclick` attribute specifies the event handler for the double click event.\n    ondblclick \"ondblclick\",\n    /// The `ondrag` attribute specifies the event handler for the drag event.\n    ondrag \"ondrag\",\n    /// The `ondragend` attribute specifies the event handler for the dragend event.\n    ondragend \"ondragend\",\n    /// The `ondragenter` attribute specifies the event handler for the dragenter event.\n    ondragenter \"ondragenter\",\n    /// The `ondragleave` attribute specifies the event handler for the dragleave event.\n    ondragleave \"ondragleave\",\n    /// The `ondragover` attribute specifies the event handler for the dragover event.\n    ondragover \"ondragover\",\n    /// The `ondragstart` attribute specifies the event handler for the dragstart event.\n    ondragstart \"ondragstart\",\n    /// The `ondrop` attribute specifies the event handler for the drop event.\n    ondrop \"ondrop\",\n    /// The `ondurationchange` attribute specifies the event handler for the durationchange event.\n    ondurationchange \"ondurationchange\",\n    /// The `onemptied` attribute specifies the event handler for the emptied event.\n    onemptied \"onemptied\",\n    /// The `onended` attribute specifies the event handler for the ended event.\n    onended \"onended\",\n    /// The `onerror` attribute specifies the event handler for the error event.\n    onerror \"onerror\",\n    /// The `onfocus` attribute specifies the event handler for the focus event.\n    onfocus \"onfocus\",\n    /// The `onformdata` attribute specifies the event handler for the formdata event.\n    onformdata \"onformdata\",\n    /// The `oninput` attribute specifies the event handler for the input event.\n    oninput \"oninput\",\n    /// The `oninvalid` attribute specifies the event handler for the invalid event.\n    oninvalid \"oninvalid\",\n    /// The `onkeydown` attribute specifies the event handler for the keydown event.\n    onkeydown \"onkeydown\",\n    /// The `onkeypress` attribute specifies the event handler for the keypress event.\n    onkeypress \"onkeypress\",\n    /// The `onkeyup` attribute specifies the event handler for the keyup event.\n    onkeyup \"onkeyup\",\n    /// The `onlanguagechange` attribute specifies the event handler for the languagechange event.\n    onlanguagechange \"onlanguagechange\",\n    /// The `onload` attribute specifies the event handler for the load event.\n    onload \"onload\",\n    /// The `onloadeddata` attribute specifies the event handler for the loadeddata event.\n    onloadeddata \"onloadeddata\",\n    /// The `onloadedmetadata` attribute specifies the event handler for the loadedmetadata event.\n    onloadedmetadata \"onloadedmetadata\",\n    /// The `onloadstart` attribute specifies the event handler for the loadstart event.\n    onloadstart \"onloadstart\",\n    /// The `onmousedown` attribute specifies the event handler for the mousedown event.\n    onmousedown \"onmousedown\",\n    /// The `onmouseenter` attribute specifies the event handler for the mouseenter event.\n    onmouseenter \"onmouseenter\",\n    /// The `onmouseleave` attribute specifies the event handler for the mouseleave event.\n    onmouseleave \"onmouseleave\",\n    /// The `onmousemove` attribute specifies the event handler for the mousemove event.\n    onmousemove \"onmousemove\",\n    /// The `onmouseout` attribute specifies the event handler for the mouseout event.\n    onmouseout \"onmouseout\",\n    /// The `onmouseover` attribute specifies the event handler for the mouseover event.\n    onmouseover \"onmouseover\",\n    /// The `onmouseup` attribute specifies the event handler for the mouseup event.\n    onmouseup \"onmouseup\",\n    /// The `onpause` attribute specifies the event handler for the pause event.\n    onpause \"onpause\",\n    /// The `onplay` attribute specifies the event handler for the play event.\n    onplay \"onplay\",\n    /// The `onplaying` attribute specifies the event handler for the playing event.\n    onplaying \"onplaying\",\n    /// The `onprogress` attribute specifies the event handler for the progress event.\n    onprogress \"onprogress\",\n    /// The `onratechange` attribute specifies the event handler for the ratechange event.\n    onratechange \"onratechange\",\n    /// The `onreset` attribute specifies the event handler for the reset event.\n    onreset \"onreset\",\n    /// The `onresize` attribute specifies the event handler for the resize event.\n    onresize \"onresize\",\n    /// The `onscroll` attribute specifies the event handler for the scroll event.\n    onscroll \"onscroll\",\n    /// The `onsecuritypolicyviolation` attribute specifies the event handler for the securitypolicyviolation event.\n    onsecuritypolicyviolation \"onsecuritypolicyviolation\",\n    /// The `onseeked` attribute specifies the event handler for the seeked event.\n    onseeked \"onseeked\",\n    /// The `onseeking` attribute specifies the event handler for the seeking event.\n    onseeking \"onseeking\",\n    /// The `onselect` attribute specifies the event handler for the select event.\n    onselect \"onselect\",\n    /// The `onslotchange` attribute specifies the event handler for the slotchange event.\n    onslotchange \"onslotchange\",\n    /// The `onstalled` attribute specifies the event handler for the stalled event.\n    onstalled \"onstalled\",\n    /// The `onsubmit` attribute specifies the event handler for the submit event.\n    onsubmit \"onsubmit\",\n    /// The `onsuspend` attribute specifies the event handler for the suspend event.\n    onsuspend \"onsuspend\",\n    /// The `ontimeupdate` attribute specifies the event handler for the timeupdate event.\n    ontimeupdate \"ontimeupdate\",\n    /// The `ontoggle` attribute specifies the event handler for the toggle event.\n    ontoggle \"ontoggle\",\n    /// The `onvolumechange` attribute specifies the event handler for the volumechange event.\n    onvolumechange \"onvolumechange\",\n    /// The `onwaiting` attribute specifies the event handler for the waiting event.\n    onwaiting \"onwaiting\",\n    /// The `onwebkitanimationend` attribute specifies the event handler for the webkitanimationend event.\n    onwebkitanimationend \"onwebkitanimationend\",\n    /// The `onwebkitanimationiteration` attribute specifies the event handler for the webkitanimationiteration event.\n    onwebkitanimationiteration \"onwebkitanimationiteration\",\n    /// The `onwebkitanimationstart` attribute specifies the event handler for the webkitanimationstart event.\n    onwebkitanimationstart \"onwebkitanimationstart\",\n    /// The `onwebkittransitionend` attribute specifies the event handler for the webkittransitionend event.\n    onwebkittransitionend \"onwebkittransitionend\",\n    /// The `onwheel` attribute specifies the event handler for the wheel event.\n    onwheel \"onwheel\",\n\n    // MathML attributes\n    /// The `accent` attribute specifies whether the element should be treated as an accent.\n    accent \"accent\",\n    /// The `accentunder` attribute specifies whether the element should be treated as an accent under the base element.\n    accentunder \"accentunder\",\n    /// The `columnalign` attribute specifies the alignment of columns.\n    columnalign \"columnalign\",\n    /// The `columnlines` attribute specifies the presence of lines between columns.\n    columnlines \"columnlines\",\n    /// The `columnspacing` attribute specifies the spacing between columns.\n    columnspacing \"columnspacing\",\n    /// The `columnspan` attribute specifies the number of columns the element should span.\n    columnspan \"columnspan\",\n    /// The `depth` attribute specifies the depth of the element.\n    depth \"depth\",\n    /// The `display` attribute specifies the display style of the element.\n    display \"display\",\n    /// The `displaystyle` attribute specifies whether the element is displayed in display style.\n    displaystyle \"displaystyle\",\n    /// The `fence` attribute specifies whether the element should act as a fence.\n    fence \"fence\",\n    /// The `frame` attribute specifies the type of frame for the element.\n    frame \"frame\",\n    /// The `framespacing` attribute specifies the spacing around frames.\n    framespacing \"framespacing\",\n    /// The `linethickness` attribute specifies the thickness of lines.\n    linethickness \"linethickness\",\n    /// The `lspace` attribute specifies the space on the left side of the element.\n    lspace \"lspace\",\n    /// The `mathbackground` attribute specifies the background color of the element.\n    mathbackground \"mathbackground\",\n    /// The `mathcolor` attribute specifies the color of the element.\n    mathcolor \"mathcolor\",\n    /// The `mathsize` attribute specifies the size of the element.\n    mathsize \"mathsize\",\n    /// The `mathvariant` attribute specifies the mathematical variant of the element.\n    mathvariant \"mathvariant\",\n    /// The `maxsize` attribute specifies the maximum size of the element.\n    maxsize \"maxsize\",\n    /// The `minsize` attribute specifies the minimum size of the element.\n    minsize \"minsize\",\n    /// The `movablelimits` attribute specifies whether the limits of the element are movable.\n    movablelimits \"movablelimits\",\n    /// The `notation` attribute specifies the type of notation for the element.\n    notation \"notation\",\n    /// The `rowalign` attribute specifies the alignment of rows.\n    rowalign \"rowalign\",\n    /// The `rowlines` attribute specifies the presence of lines between rows.\n    rowlines \"rowlines\",\n    /// The `rowspacing` attribute specifies the spacing between rows.\n    rowspacing \"rowspacing\",\n    /// The `rspace` attribute specifies the space on the right side of the element.\n    rspace \"rspace\",\n    /// The `scriptlevel` attribute specifies the script level of the element.\n    scriptlevel \"scriptlevel\",\n    /// The `separator` attribute specifies whether the element is a separator.\n    separator \"separator\",\n    /// The `stretchy` attribute specifies whether the element is stretchy.\n    stretchy \"stretchy\",\n    /// The `symmetric` attribute specifies whether the element is symmetric.\n    symmetric \"symmetric\",\n    /// The `voffset` attribute specifies the vertical offset of the element.\n    voffset \"voffset\",\n    /// The `xmlns` attribute specifies the XML namespace of the element.\n    xmlns \"xmlns\",\n}\n"
  },
  {
    "path": "tachys/src/html/attribute/maybe_next_attr_erasure_macros.rs",
    "content": "macro_rules! next_attr_output_type {\n    ($current:ty, $next:ty) => {\n        #[cfg(not(erase_components))]\n        type Output<NewAttr: Attribute> = ($current, $next);\n\n        #[cfg(erase_components)]\n        type Output<NewAttr: Attribute> =\n            Vec<$crate::html::attribute::any_attribute::AnyAttribute>;\n    };\n}\n\nmacro_rules! next_attr_combine {\n    ($self:expr, $next_attr:expr) => {{\n        #[cfg(not(erase_components))]\n        {\n            ($self, $next_attr)\n        }\n        #[cfg(erase_components)]\n        {\n            use $crate::html::attribute::any_attribute::IntoAnyAttribute;\n            vec![$self.into_any_attr(), $next_attr.into_any_attr()]\n        }\n    }};\n}\n\npub(crate) use next_attr_combine;\npub(crate) use next_attr_output_type;\n"
  },
  {
    "path": "tachys/src/html/attribute/mod.rs",
    "content": "/// A type-erased `AnyAttribute`.\npub mod any_attribute;\n/// Types for ARIA attributes.\npub mod aria;\n/// Types for custom attributes.\npub mod custom;\n/// Traits to define global attribute methods on all HTML elements.\npub mod global;\nmod key;\npub(crate) mod maybe_next_attr_erasure_macros;\nmod value;\n\nuse crate::view::{Position, ToTemplate};\npub use key::*;\nuse maybe_next_attr_erasure_macros::{\n    next_attr_combine, next_attr_output_type,\n};\nuse std::{borrow::Cow, fmt::Debug, future::Future};\npub use value::*;\n\n/// Defines an attribute: anything that can modify an element.\npub trait Attribute: NextAttribute + Send {\n    /// The minimum length of this attribute in HTML.\n    const MIN_LENGTH: usize;\n\n    /// The state that should be retained between building and rebuilding.\n    type State;\n    /// The type once all async data have loaded.\n    type AsyncOutput: Attribute;\n    /// An equivalent to this attribute that can be cloned to be shared across elements.\n    type Cloneable: Attribute + Clone;\n    /// An equivalent to this attribute that can be cloned to be shared across elements, and\n    /// captures no references shorter than `'static`.\n    type CloneableOwned: Attribute + Clone + 'static;\n\n    /// An approximation of the actual length of this attribute in HTML.\n    fn html_len(&self) -> usize;\n\n    /// Renders the attribute to HTML.\n    ///\n    /// This separates a general buffer for attribute values from the `class` and `style`\n    /// attributes, so that multiple classes or styles can be combined, and also allows for an\n    /// `inner_html` attribute that sets the child HTML instead of an attribute.\n    fn to_html(\n        self,\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n    );\n\n    /// Adds interactivity as necessary, given DOM nodes that were created from HTML that has\n    /// either been rendered on the server, or cloned for a `<template>`.\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State;\n\n    /// Adds this attribute to the element during client-side rendering.\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State;\n\n    /// Applies a new value for the attribute.\n    fn rebuild(self, state: &mut Self::State);\n\n    /// Converts this attribute into an equivalent that can be cloned.\n    fn into_cloneable(self) -> Self::Cloneable;\n\n    /// Converts this attributes into an equivalent that can be cloned and is `'static`.\n    fn into_cloneable_owned(self) -> Self::CloneableOwned;\n\n    /// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For\n    /// reactive types, this can be used to gather data about reactivity or about asynchronous data\n    /// that needs to be loaded.\n    fn dry_resolve(&mut self);\n\n    /// “Resolves” this into a type that is not waiting for any asynchronous data.\n    fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;\n\n    /// Returns a set of attribute keys, associated with this attribute, if any.\n    ///\n    /// This is only used to manage the removal of type-erased attributes, when needed.\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        // TODO: remove default implementation in 0.9, or fix this whole approach\n        // by making it easier to remove attributes\n        vec![]\n    }\n}\n\n/// An attribute key can be used to remove an attribute from an element.\npub enum NamedAttributeKey {\n    /// An ordinary attribute.\n    Attribute(Cow<'static, str>),\n    /// A DOM property.\n    Property(Cow<'static, str>),\n    /// The `inner_html` pseudo-attribute.\n    InnerHtml,\n}\n\n/// Adds another attribute to this one, returning a new attribute.\n///\n/// This is typically achieved by creating or extending a tuple of attributes.\npub trait NextAttribute {\n    /// The type of the new, combined attribute.\n    type Output<NewAttr: Attribute>: Attribute;\n\n    /// Adds a new attribute.\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr>;\n}\n\nimpl Attribute for () {\n    const MIN_LENGTH: usize = 0;\n\n    type State = ();\n    type AsyncOutput = ();\n    type Cloneable = ();\n    type CloneableOwned = ();\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(\n        self,\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _el: &crate::renderer::types::Element,\n    ) -> Self::State {\n    }\n\n    fn build(self, _el: &crate::renderer::types::Element) -> Self::State {}\n\n    fn rebuild(self, _state: &mut Self::State) {}\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::Cloneable {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {}\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![]\n    }\n}\n\nimpl NextAttribute for () {\n    #[cfg(not(erase_components))]\n    type Output<NewAttr: Attribute> = (NewAttr,);\n\n    #[cfg(erase_components)]\n    type Output<NewAttr: Attribute> =\n        Vec<crate::html::attribute::any_attribute::AnyAttribute>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        #[cfg(not(erase_components))]\n        {\n            (new_attr,)\n        }\n        #[cfg(erase_components)]\n        {\n            use crate::html::attribute::any_attribute::IntoAnyAttribute;\n\n            vec![new_attr.into_any_attr()]\n        }\n    }\n}\n\n/// An attribute with a key and value.\n#[derive(Debug)]\npub struct Attr<K, V>(pub K, pub V)\nwhere\n    K: AttributeKey,\n    V: AttributeValue;\n\nimpl<K, V> Clone for Attr<K, V>\nwhere\n    K: AttributeKey,\n    V: AttributeValue + Clone,\n{\n    fn clone(&self) -> Self {\n        Self(self.0.clone(), self.1.clone())\n    }\n}\n\nimpl<K, V> ToTemplate for Attr<K, V>\nwhere\n    K: AttributeKey,\n    V: AttributeValue,\n{\n    fn to_template(\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n        V::to_template(K::KEY, buf);\n    }\n}\n\nimpl<K, V> Attribute for Attr<K, V>\nwhere\n    K: AttributeKey + Send,\n    V: AttributeValue + Send,\n{\n    const MIN_LENGTH: usize = 0;\n\n    type State = V::State;\n    type AsyncOutput = Attr<K, V::AsyncOutput>;\n    type Cloneable = Attr<K, V::Cloneable>;\n    type CloneableOwned = Attr<K, V::CloneableOwned>;\n\n    fn html_len(&self) -> usize {\n        K::KEY.len() + 3 + self.1.html_len()\n    }\n\n    fn to_html(\n        self,\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n        self.1.to_html(K::KEY, buf);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        self.1.hydrate::<FROM_SERVER>(K::KEY, el)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        V::build(self.1, el, K::KEY)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        V::rebuild(self.1, K::KEY, state);\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        Attr(self.0, self.1.into_cloneable())\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        Attr(self.0, self.1.into_cloneable_owned())\n    }\n\n    fn dry_resolve(&mut self) {\n        self.1.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        Attr(self.0, self.1.resolve().await)\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![NamedAttributeKey::Attribute(K::KEY.into())]\n    }\n}\n\nimpl<K, V> NextAttribute for Attr<K, V>\nwhere\n    K: AttributeKey,\n    V: AttributeValue,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\nmacro_rules! impl_attr_for_tuples {\n    ($first:ident, $($ty:ident),* $(,)?) => {\n        impl<$first, $($ty),*> Attribute for ($first, $($ty,)*)\n        where\n            $first: Attribute,\n            $($ty: Attribute),*,\n        {\n            const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;\n\n            type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);\n            type State = ($first::State, $($ty::State,)*);\n            type Cloneable = ($first::Cloneable, $($ty::Cloneable,)*);\n            type CloneableOwned = ($first::CloneableOwned, $($ty::CloneableOwned,)*);\n\n            fn html_len(&self) -> usize {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                $first.html_len() $(+ $ty.html_len())*\n            }\n\n            fn to_html(self, buf: &mut String, class: &mut String, style: &mut String, inner_html: &mut String,) {\n                #[allow(non_snake_case)]\n                    let ($first, $($ty,)* ) = self;\n                    $first.to_html(buf, class, style, inner_html);\n                    $($ty.to_html(buf, class, style, inner_html));*\n            }\n\n            fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {\n                #[allow(non_snake_case)]\n                    let ($first, $($ty,)* ) = self;\n                    (\n                        $first.hydrate::<FROM_SERVER>(el),\n                        $($ty.hydrate::<FROM_SERVER>(el)),*\n                    )\n            }\n\n            fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n                #[allow(non_snake_case)]\n                    let ($first, $($ty,)*) = self;\n                    (\n                        $first.build(el),\n                        $($ty.build(el)),*\n                    )\n            }\n\n            fn rebuild(self, state: &mut Self::State) {\n                paste::paste! {\n                    let ([<$first:lower>], $([<$ty:lower>],)*) = self;\n                    let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;\n                    [<$first:lower>].rebuild([<view_ $first:lower>]);\n                    $([<$ty:lower>].rebuild([<view_ $ty:lower>]));*\n                }\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                (\n                    $first.into_cloneable(),\n                    $($ty.into_cloneable()),*\n                )\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                (\n                    $first.into_cloneable_owned(),\n                    $($ty.into_cloneable_owned()),*\n                )\n            }\n\n            fn dry_resolve(&mut self) {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                $first.dry_resolve();\n                $($ty.dry_resolve());*\n            }\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                futures::join!(\n                    $first.resolve(),\n                    $($ty.resolve()),*\n                )\n            }\n\n            fn keys(&self) -> Vec<NamedAttributeKey> {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = &self;\n                let mut buf = $first.keys();\n                $(buf.extend($ty.keys());)*\n                buf\n            }\n        }\n\n        impl<$first, $($ty),*> NextAttribute for ($first, $($ty,)*)\n        where\n            $first: Attribute,\n            $($ty: Attribute),*,\n\n        {\n            type Output<NewAttr: Attribute> = ($first, $($ty,)* NewAttr);\n\n            fn add_any_attr<NewAttr: Attribute>(\n                self,\n                new_attr: NewAttr,\n            ) -> Self::Output<NewAttr> {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                ($first, $($ty,)* new_attr)\n            }\n        }\n    };\n}\n\nmacro_rules! impl_attr_for_tuples_truncate_additional {\n    ($first:ident, $($ty:ident),* $(,)?) => {\n        impl<$first, $($ty),*> Attribute for ($first, $($ty,)*)\n        where\n            $first: Attribute,\n            $($ty: Attribute),*,\n        {\n            const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;\n\n            type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);\n            type State = ($first::State, $($ty::State,)*);\n            type Cloneable = ($first::Cloneable, $($ty::Cloneable,)*);\n            type CloneableOwned = ($first::CloneableOwned, $($ty::CloneableOwned,)*);\n\n            fn html_len(&self) -> usize {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                $first.html_len() $(+ $ty.html_len())*\n            }\n\n            fn to_html(self, buf: &mut String, class: &mut String, style: &mut String, inner_html: &mut String,) {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)* ) = self;\n                $first.to_html(buf, class, style, inner_html);\n                $($ty.to_html(buf, class, style, inner_html));*\n            }\n\n            fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)* ) = self;\n                (\n                    $first.hydrate::<FROM_SERVER>(el),\n                    $($ty.hydrate::<FROM_SERVER>(el)),*\n                )\n            }\n\n            fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                (\n                    $first.build(el),\n                    $($ty.build(el)),*\n                )\n            }\n\n            fn rebuild(self, state: &mut Self::State) {\n                paste::paste! {\n                    let ([<$first:lower>], $([<$ty:lower>],)*) = self;\n                    let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;\n                    [<$first:lower>].rebuild([<view_ $first:lower>]);\n                    $([<$ty:lower>].rebuild([<view_ $ty:lower>]));*\n                }\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                (\n                    $first.into_cloneable(),\n                    $($ty.into_cloneable()),*\n                )\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                (\n                    $first.into_cloneable_owned(),\n                    $($ty.into_cloneable_owned()),*\n                )\n            }\n\n            fn dry_resolve(&mut self) {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                $first.dry_resolve();\n                $($ty.dry_resolve());*\n            }\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                futures::join!(\n                    $first.resolve(),\n                    $($ty.resolve()),*\n                )\n            }\n\n            fn keys(&self) -> Vec<NamedAttributeKey> {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = &self;\n                let mut buf = $first.keys();\n                $(buf.extend($ty.keys());)*\n                buf\n            }\n        }\n\n        impl<$first, $($ty),*> NextAttribute for ($first, $($ty,)*)\n        where\n            $first: Attribute,\n            $($ty: Attribute),*,\n\n        {\n            type Output<NewAttr: Attribute> = ($first, $($ty,)*);\n\n            fn add_any_attr<NewAttr: Attribute>(\n                self,\n                _new_attr: NewAttr,\n            ) -> Self::Output<NewAttr> {\n                todo!(\"adding more than 26 attributes is not supported\");\n                //($first, $($ty,)*)\n            }\n        }\n    };\n}\n\nimpl<A> Attribute for (A,)\nwhere\n    A: Attribute,\n{\n    const MIN_LENGTH: usize = A::MIN_LENGTH;\n\n    type AsyncOutput = (A::AsyncOutput,);\n    type State = A::State;\n    type Cloneable = (A::Cloneable,);\n    type CloneableOwned = (A::CloneableOwned,);\n\n    fn html_len(&self) -> usize {\n        self.0.html_len()\n    }\n\n    fn to_html(\n        self,\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n    ) {\n        self.0.to_html(buf, class, style, inner_html);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        self.0.hydrate::<FROM_SERVER>(el)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        self.0.build(el)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.0.rebuild(state);\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        (self.0.into_cloneable(),)\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        (self.0.into_cloneable_owned(),)\n    }\n\n    fn dry_resolve(&mut self) {\n        self.0.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        (self.0.resolve().await,)\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        self.0.keys()\n    }\n}\n\nimpl<A> NextAttribute for (A,)\nwhere\n    A: Attribute,\n{\n    next_attr_output_type!(A, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self.0, new_attr)\n    }\n}\n\nimpl_attr_for_tuples!(A, B);\nimpl_attr_for_tuples!(A, B, C);\nimpl_attr_for_tuples!(A, B, C, D);\nimpl_attr_for_tuples!(A, B, C, D, E);\nimpl_attr_for_tuples!(A, B, C, D, E, F);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);\nimpl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);\nimpl_attr_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T\n);\nimpl_attr_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U\n);\nimpl_attr_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V\n);\nimpl_attr_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W\n);\nimpl_attr_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X\n);\nimpl_attr_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y\n);\nimpl_attr_for_tuples_truncate_additional!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,\n    Z\n);\n"
  },
  {
    "path": "tachys/src/html/attribute/value.rs",
    "content": "use crate::renderer::Rndr;\nuse std::{\n    borrow::Cow,\n    future::Future,\n    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},\n    num::{\n        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8,\n        NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64,\n        NonZeroU8, NonZeroUsize,\n    },\n    sync::Arc,\n};\n\n/// Declares that this type can be converted into some other type, which is a valid attribute value.\npub trait IntoAttributeValue {\n    /// The attribute value into which this type can be converted.\n    type Output;\n\n    /// Consumes this value, transforming it into an attribute value.\n    fn into_attribute_value(self) -> Self::Output;\n}\n\nimpl<T> IntoAttributeValue for T\nwhere\n    T: AttributeValue,\n{\n    type Output = Self;\n\n    fn into_attribute_value(self) -> Self::Output {\n        self\n    }\n}\n\n/// A possible value for an HTML attribute.\npub trait AttributeValue: Send {\n    /// The state that should be retained between building and rebuilding.\n    type State;\n\n    /// The type once all async data have loaded.\n    type AsyncOutput: AttributeValue;\n\n    /// A version of the value that can be cloned. This can be the same type, or a\n    /// reference-counted type. Generally speaking, this does *not* need to refer to the same data,\n    /// but should behave in the same way. So for example, making an event handler cloneable should\n    /// probably make it reference-counted (so that a `FnMut()` continues mutating the same\n    /// closure), but making a `String` cloneable does not necessarily need to make it an\n    /// `Arc<str>`, as two different clones of a `String` will still have the same value.\n    type Cloneable: AttributeValue + Clone;\n\n    /// A cloneable type that is also `'static`. This is used for spreading across types when the\n    /// spreadable attribute needs to be owned. In some cases (`&'a str` to `Arc<str>`, etc.) the owned\n    /// cloneable type has worse performance than the cloneable type, so they are separate.\n    type CloneableOwned: AttributeValue + Clone + 'static;\n\n    /// An approximation of the actual length of this attribute in HTML.\n    fn html_len(&self) -> usize;\n\n    /// Renders the attribute value to HTML.\n    fn to_html(self, key: &str, buf: &mut String);\n\n    /// Renders the attribute value to HTML for a `<template>`.\n    fn to_template(key: &str, buf: &mut String);\n\n    /// Adds interactivity as necessary, given DOM nodes that were created from HTML that has\n    /// either been rendered on the server, or cloned for a `<template>`.\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State;\n\n    /// Adds this attribute to the element during client-side rendering.\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State;\n\n    /// Applies a new value for the attribute.\n    fn rebuild(self, key: &str, state: &mut Self::State);\n\n    /// Converts this attribute into an equivalent that can be cloned.\n    fn into_cloneable(self) -> Self::Cloneable;\n\n    /// Converts this attributes into an equivalent that can be cloned and is `'static`.\n    fn into_cloneable_owned(self) -> Self::CloneableOwned;\n\n    /// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For\n    /// reactive types, this can be used to gather data about reactivity or about asynchronous data\n    /// that needs to be loaded.\n    fn dry_resolve(&mut self);\n\n    /// “Resolves” this into a form that is not waiting for any asynchronous data.\n    fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;\n}\n\nimpl AttributeValue for () {\n    type State = ();\n    type AsyncOutput = ();\n    type Cloneable = ();\n    type CloneableOwned = ();\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(self, _key: &str, _buf: &mut String) {}\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _key: &str,\n        _el: &crate::renderer::types::Element,\n    ) {\n    }\n\n    fn build(\n        self,\n        _el: &crate::renderer::types::Element,\n        _key: &str,\n    ) -> Self::State {\n    }\n\n    fn rebuild(self, _key: &str, _state: &mut Self::State) {}\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) {}\n}\n\nimpl<'a> AttributeValue for &'a str {\n    type State = (crate::renderer::types::Element, &'a str);\n    type AsyncOutput = &'a str;\n    type Cloneable = &'a str;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        buf.push(' ');\n        buf.push_str(key);\n        buf.push_str(\"=\\\"\");\n        buf.push_str(&escape_attr(self));\n        buf.push('\"');\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        // if we're actually hydrating from SSRed HTML, we don't need to set the attribute\n        // if we're hydrating from a CSR-cloned <template>, we do need to set non-StaticAttr attributes\n        if !FROM_SERVER {\n            Rndr::set_attribute(el, key, self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        Rndr::set_attribute(el, key, self);\n        (el.to_owned(), self)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let (el, prev_value) = state;\n        if self != *prev_value {\n            Rndr::set_attribute(el, key, self);\n        }\n        *prev_value = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl<'a> AttributeValue for Cow<'a, str> {\n    type State = (crate::renderer::types::Element, Self);\n    type AsyncOutput = Self;\n    type Cloneable = Arc<str>;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        buf.push(' ');\n        buf.push_str(key);\n        buf.push_str(\"=\\\"\");\n        buf.push_str(&escape_attr(&self));\n        buf.push('\"');\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        // if we're actually hydrating from SSRed HTML, we don't need to set the attribute\n        // if we're hydrating from a CSR-cloned <template>, we do need to set non-StaticAttr attributes\n        if !FROM_SERVER {\n            Rndr::set_attribute(el, key, &self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        Rndr::set_attribute(el, key, &self);\n        (el.to_owned(), self)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let (el, prev_value) = state;\n        if self != *prev_value {\n            Rndr::set_attribute(el, key, &self);\n        }\n        *prev_value = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\nimpl<const V: &'static str> AttributeValue\n    for crate::view::static_types::Static<V>\n{\n    type AsyncOutput = Self;\n    type State = ();\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        V.len()\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        <&str as AttributeValue>::to_html(V, key, buf);\n    }\n\n    fn to_template(key: &str, buf: &mut String) {\n        buf.push(' ');\n        buf.push_str(key);\n        buf.push_str(\"=\\\"\");\n        buf.push_str(V);\n        buf.push('\"');\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _key: &str,\n        _el: &crate::renderer::types::Element,\n    ) -> Self::State {\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        <&str as AttributeValue>::build(V, el, key);\n    }\n\n    fn rebuild(self, _key: &str, _state: &mut Self::State) {}\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl<'a> AttributeValue for &'a String {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, &'a String);\n    type Cloneable = Self;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        <&str as AttributeValue>::to_html(self.as_str(), key, buf);\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(\n            self.as_str(),\n            key,\n            el,\n        );\n        (el, self)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        Rndr::set_attribute(el, key, self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let (el, prev_value) = state;\n        if self != *prev_value {\n            Rndr::set_attribute(el, key, self);\n        }\n        *prev_value = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.as_str().into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl AttributeValue for String {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, String);\n    type Cloneable = Arc<str>;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        <&str as AttributeValue>::to_html(self.as_str(), key, buf);\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(\n            self.as_str(),\n            key,\n            el,\n        );\n        (el, self)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        Rndr::set_attribute(el, key, &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let (el, prev_value) = state;\n        if self != *prev_value {\n            Rndr::set_attribute(el, key, &self);\n        }\n        *prev_value = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl AttributeValue for Arc<str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Arc<str>);\n    type Cloneable = Arc<str>;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        <&str as AttributeValue>::to_html(self.as_ref(), key, buf);\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(\n            self.as_ref(),\n            key,\n            el,\n        );\n        (el, self)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        Rndr::set_attribute(el, key, &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let (el, prev_value) = state;\n        if self != *prev_value {\n            Rndr::set_attribute(el, key, &self);\n        }\n        *prev_value = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n// TODO impl AttributeValue for Rc<str> and Arc<str> too\n\nimpl AttributeValue for bool {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, bool);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        if self {\n            buf.push(' ');\n            buf.push_str(key);\n        }\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        // if we're actually hydrating from SSRed HTML, we don't need to set the attribute\n        // if we're hydrating from a CSR-cloned <template>, we do need to set non-StaticAttr attributes\n        if !FROM_SERVER {\n            Rndr::set_attribute(el, key, \"\");\n        }\n        (el.clone(), self)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        if self {\n            Rndr::set_attribute(el, key, \"\");\n        }\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let (el, prev_value) = state;\n        if self != *prev_value {\n            if self {\n                Rndr::set_attribute(el, key, \"\");\n            } else {\n                Rndr::remove_attribute(el, key);\n            }\n        }\n        *prev_value = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl<V> AttributeValue for Option<V>\nwhere\n    V: AttributeValue,\n{\n    type AsyncOutput = Option<V::AsyncOutput>;\n    type State = (crate::renderer::types::Element, Option<V::State>);\n    type Cloneable = Option<V::Cloneable>;\n    type CloneableOwned = Option<V::CloneableOwned>;\n\n    fn html_len(&self) -> usize {\n        match self {\n            Some(i) => i.html_len(),\n            None => 0,\n        }\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        if let Some(v) = self {\n            v.to_html(key, buf);\n        }\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let state = self.map(|v| v.hydrate::<FROM_SERVER>(key, el));\n        (el.clone(), state)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let el = el.clone();\n        let v = self.map(|v| v.build(&el, key));\n        (el, v)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let (el, prev) = state;\n        match (self, prev.as_mut()) {\n            (None, None) => {}\n            (None, Some(_)) => {\n                Rndr::remove_attribute(el, key);\n                *prev = None;\n            }\n            (Some(value), None) => {\n                *prev = Some(value.build(el, key));\n            }\n            (Some(new), Some(old)) => {\n                new.rebuild(key, old);\n            }\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.map(|value| value.into_cloneable())\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.map(|value| value.into_cloneable_owned())\n    }\n\n    fn dry_resolve(&mut self) {\n        if let Some(inner) = self.as_mut() {\n            inner.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        match self {\n            None => None,\n            Some(inner) => Some(inner.resolve().await),\n        }\n    }\n}\n\npub(crate) fn escape_attr(value: &str) -> Cow<'_, str> {\n    html_escape::encode_double_quoted_attribute(value)\n}\n\nmacro_rules! render_primitive {\n  ($($child_type:ty),* $(,)?) => {\n      $(\n        impl AttributeValue for $child_type\n        where\n\n        {\n            type AsyncOutput = $child_type;\n            type State = (crate::renderer::types::Element, $child_type);\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn html_len(&self) -> usize {\n                0\n            }\n\n            fn to_html(self, key: &str, buf: &mut String) {\n                <String as AttributeValue>::to_html(self.to_string(), key, buf);\n            }\n\n            fn to_template(_key: &str, _buf: &mut String) {}\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                key: &str,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                // if we're actually hydrating from SSRed HTML, we don't need to set the attribute\n                // if we're hydrating from a CSR-cloned <template>, we do need to set non-StaticAttr attributes\n                if !FROM_SERVER {\n                    Rndr::set_attribute(el, key, &self.to_string());\n                }\n                (el.clone(), self)\n            }\n\n            fn build(self, el: &crate::renderer::types::Element, key: &str) -> Self::State {\n                Rndr::set_attribute(el, key, &self.to_string());\n                (el.to_owned(), self)\n            }\n\n            fn rebuild(self, key: &str, state: &mut Self::State) {\n                let (el, prev_value) = state;\n                if self != *prev_value {\n                    Rndr::set_attribute(el, key, &self.to_string());\n                }\n                *prev_value = self;\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {\n            }\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n        }\n      )*\n  }\n}\n\nrender_primitive![\n    usize,\n    u8,\n    u16,\n    u32,\n    u64,\n    u128,\n    isize,\n    i8,\n    i16,\n    i32,\n    i64,\n    i128,\n    f32,\n    f64,\n    char,\n    IpAddr,\n    SocketAddr,\n    SocketAddrV4,\n    SocketAddrV6,\n    Ipv4Addr,\n    Ipv6Addr,\n    NonZeroI8,\n    NonZeroU8,\n    NonZeroI16,\n    NonZeroU16,\n    NonZeroI32,\n    NonZeroU32,\n    NonZeroI64,\n    NonZeroU64,\n    NonZeroI128,\n    NonZeroU128,\n    NonZeroIsize,\n    NonZeroUsize,\n];\n"
  },
  {
    "path": "tachys/src/html/class.rs",
    "content": "use super::attribute::{\n    maybe_next_attr_erasure_macros::next_attr_output_type, Attribute,\n    NamedAttributeKey, NextAttribute,\n};\nuse crate::{\n    html::attribute::maybe_next_attr_erasure_macros::next_attr_combine,\n    renderer::Rndr,\n    view::{Position, ToTemplate},\n};\nuse std::{borrow::Cow, future::Future, sync::Arc};\n\n/// Adds a CSS class.\n#[inline(always)]\npub fn class<C>(class: C) -> Class<C>\nwhere\n    C: IntoClass,\n{\n    Class { class }\n}\n\n/// A CSS class.\n#[derive(Debug)]\npub struct Class<C> {\n    class: C,\n}\n\nimpl<C> Clone for Class<C>\nwhere\n    C: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            class: self.class.clone(),\n        }\n    }\n}\n\nimpl<C> Attribute for Class<C>\nwhere\n    C: IntoClass,\n{\n    const MIN_LENGTH: usize = C::MIN_LENGTH;\n\n    type AsyncOutput = Class<C::AsyncOutput>;\n    type State = C::State;\n    type Cloneable = Class<C::Cloneable>;\n    type CloneableOwned = Class<C::CloneableOwned>;\n\n    fn html_len(&self) -> usize {\n        self.class.html_len() + 1\n    }\n\n    fn to_html(\n        self,\n        _buf: &mut String,\n        class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n        // If this is a class=\"...\" attribute (not class:name=value), clear previous value\n        if self.class.should_overwrite() {\n            class.clear();\n        }\n        class.push(' ');\n        self.class.to_html(class);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        self.class.hydrate::<FROM_SERVER>(el)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        self.class.build(el)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.class.rebuild(state)\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        Class {\n            class: self.class.into_cloneable(),\n        }\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        Class {\n            class: self.class.into_cloneable_owned(),\n        }\n    }\n\n    fn dry_resolve(&mut self) {\n        self.class.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        Class {\n            class: self.class.resolve().await,\n        }\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![NamedAttributeKey::Attribute(\"class\".into())]\n    }\n}\n\nimpl<C> NextAttribute for Class<C>\nwhere\n    C: IntoClass,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\nimpl<C> ToTemplate for Class<C>\nwhere\n    C: IntoClass,\n{\n    const CLASS: &'static str = C::TEMPLATE;\n\n    fn to_template(\n        _buf: &mut String,\n        class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n        C::to_template(class);\n    }\n}\n\n/// A possible value for a CSS class.\npub trait IntoClass: Send {\n    /// The HTML that should be included in a `<template>`.\n    const TEMPLATE: &'static str = \"\";\n    /// The minimum length of the HTML.\n    const MIN_LENGTH: usize = Self::TEMPLATE.len();\n\n    /// The type after all async data have resolved.\n    type AsyncOutput: IntoClass;\n    /// The view state retained between building and rebuilding.\n    type State;\n    /// An equivalent value that can be cloned.\n    type Cloneable: IntoClass + Clone;\n    /// An equivalent value that can be cloned and is `'static`.\n    type CloneableOwned: IntoClass + Clone + 'static;\n\n    /// The estimated length of the HTML.\n    fn html_len(&self) -> usize;\n\n    /// Renders the class to HTML.\n    fn to_html(self, class: &mut String);\n\n    /// Whether this class attribute should overwrite previous class values.\n    /// Returns `true` for `class=\"...\"` attributes, `false` for `class:name=value` directives.\n    fn should_overwrite(&self) -> bool {\n        false\n    }\n\n    /// Renders the class to HTML for a `<template>`.\n    #[allow(unused)] // it's used with `nightly` feature\n    fn to_template(class: &mut String) {}\n\n    /// Adds interactivity as necessary, given DOM nodes that were created from HTML that has\n    /// either been rendered on the server, or cloned for a `<template>`.\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State;\n\n    /// Adds this class to the element during client-side rendering.\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State;\n\n    /// Updates the value.\n    fn rebuild(self, state: &mut Self::State);\n\n    /// Converts this to a cloneable type.\n    fn into_cloneable(self) -> Self::Cloneable;\n\n    /// Converts this to a cloneable, owned type.\n    fn into_cloneable_owned(self) -> Self::CloneableOwned;\n\n    /// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For\n    /// reactive types, this can be used to gather data about reactivity or about asynchronous data\n    /// that needs to be loaded.\n    fn dry_resolve(&mut self);\n\n    /// “Resolves” this into a type that is not waiting for any asynchronous data.\n    fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;\n\n    /// Reset the class list to the state before this class was added.\n    fn reset(state: &mut Self::State);\n}\n\nimpl<T: IntoClass> IntoClass for Option<T> {\n    type AsyncOutput = Option<T::AsyncOutput>;\n    type State = (crate::renderer::types::Element, Option<T::State>);\n    type Cloneable = Option<T::Cloneable>;\n    type CloneableOwned = Option<T::CloneableOwned>;\n\n    fn html_len(&self) -> usize {\n        self.as_ref().map_or(0, IntoClass::html_len)\n    }\n\n    fn to_html(self, class: &mut String) {\n        if let Some(t) = self {\n            t.to_html(class);\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if let Some(t) = self {\n            (el.clone(), Some(t.hydrate::<FROM_SERVER>(el)))\n        } else {\n            (el.clone(), None)\n        }\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        if let Some(t) = self {\n            (el.clone(), Some(t.build(el)))\n        } else {\n            (el.clone(), None)\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let el = &state.0;\n        let prev_state = &mut state.1;\n        let maybe_next_t_state = match (prev_state.take(), self) {\n            (Some(mut prev_t_state), None) => {\n                T::reset(&mut prev_t_state);\n                Some(None)\n            }\n            (None, Some(t)) => Some(Some(t.build(el))),\n            (Some(mut prev_t_state), Some(t)) => {\n                t.rebuild(&mut prev_t_state);\n                Some(Some(prev_t_state))\n            }\n            (None, None) => Some(None),\n        };\n        if let Some(next_t_state) = maybe_next_t_state {\n            state.1 = next_t_state;\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.map(|t| t.into_cloneable())\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.map(|t| t.into_cloneable_owned())\n    }\n\n    fn dry_resolve(&mut self) {\n        if let Some(t) = self {\n            t.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        if let Some(t) = self {\n            Some(t.resolve().await)\n        } else {\n            None\n        }\n    }\n\n    fn reset(state: &mut Self::State) {\n        if let Some(prev_t_state) = &mut state.1 {\n            T::reset(prev_t_state);\n        }\n    }\n}\n\nimpl IntoClass for &str {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Self;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        class.push_str(self);\n    }\n\n    fn should_overwrite(&self) -> bool {\n        true\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_attribute(el, \"class\", self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"class\", self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"class\", self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"class\");\n    }\n}\n\nimpl IntoClass for Cow<'_, str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Arc<str>;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        IntoClass::to_html(&*self, class);\n    }\n\n    fn should_overwrite(&self) -> bool {\n        true\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_attribute(el, \"class\", &self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"class\", &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"class\", &self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"class\");\n    }\n}\n\nimpl IntoClass for String {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Arc<str>;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        IntoClass::to_html(self.as_str(), class);\n    }\n\n    fn should_overwrite(&self) -> bool {\n        true\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_attribute(el, \"class\", &self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"class\", &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"class\", &self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"class\");\n    }\n}\n\nimpl IntoClass for Arc<str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        IntoClass::to_html(self.as_ref(), class);\n    }\n\n    fn should_overwrite(&self) -> bool {\n        true\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_attribute(el, \"class\", &self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"class\", &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"class\", &self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"class\");\n    }\n}\n\nimpl IntoClass for (&'static str, bool) {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::ClassList, bool, &'static str);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        self.0.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        let (name, include) = self;\n        if include {\n            class.push_str(name);\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let (name, include) = self;\n        let class_list = Rndr::class_list(el);\n        if !FROM_SERVER && include {\n            Rndr::add_class(&class_list, name);\n        }\n        (class_list, self.1, name)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        let (name, include) = self;\n        let class_list = Rndr::class_list(el);\n        if include {\n            Rndr::add_class(&class_list, name);\n        }\n        (class_list, self.1, name)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (name, include) = self;\n        let (class_list, prev_include, prev_name) = state;\n        if name == *prev_name {\n            if include != *prev_include {\n                if include {\n                    Rndr::add_class(class_list, name);\n                } else {\n                    Rndr::remove_class(class_list, name);\n                }\n            }\n        } else {\n            if *prev_include {\n                Rndr::remove_class(class_list, prev_name);\n            }\n            if include {\n                Rndr::add_class(class_list, name);\n            }\n        }\n        *prev_include = include;\n        *prev_name = name;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::Cloneable {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (class_list, _, name) = state;\n        Rndr::remove_class(class_list, name);\n    }\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\nimpl<const V: &'static str> IntoClass for crate::view::static_types::Static<V> {\n    const TEMPLATE: &'static str = V;\n\n    type AsyncOutput = Self;\n    type State = ();\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        V.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        class.push_str(V);\n    }\n\n    fn to_template(class: &mut String) {\n        class.push_str(V);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _el: &crate::renderer::types::Element,\n    ) -> Self::State {\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"class\", V);\n    }\n\n    fn rebuild(self, _state: &mut Self::State) {}\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(_state: &mut Self::State) {}\n}\n\n/* #[cfg(test)]\nmod tests {\n    use crate::{\n        html::{\n            class::class,\n            element::{p, HtmlElement},\n        },\n        renderer::dom::Dom,\n        view::{Position, PositionState, RenderHtml},\n    };\n\n    #[test]\n    fn adds_simple_class() {\n        let mut html = String::new();\n        let el: HtmlElement<_, _, _, Dom> = p(class(\"foo bar\"), ());\n        el.to_html(&mut html, &PositionState::new(Position::FirstChild));\n\n        assert_eq!(html, r#\"<p class=\"foo bar\"></p>\"#);\n    }\n\n    #[test]\n    fn adds_class_with_dynamic() {\n        let mut html = String::new();\n        let el: HtmlElement<_, _, _, Dom> =\n            p((class(\"foo bar\"), class((\"baz\", true))), ());\n        el.to_html(&mut html, &PositionState::new(Position::FirstChild));\n\n        assert_eq!(html, r#\"<p class=\"foo bar baz\"></p>\"#);\n    }\n\n    #[test]\n    fn adds_class_with_dynamic_and_function() {\n        let mut html = String::new();\n        let el: HtmlElement<_, _, _, Dom> = p(\n            (\n                class(\"foo bar\"),\n                class((\"baz\", || true)),\n                class((\"boo\", false)),\n            ),\n            (),\n        );\n        el.to_html(&mut html, &PositionState::new(Position::FirstChild));\n\n        assert_eq!(html, r#\"<p class=\"foo bar baz\"></p>\"#);\n    }\n} */\n"
  },
  {
    "path": "tachys/src/html/directive.rs",
    "content": "use super::attribute::{\n    maybe_next_attr_erasure_macros::next_attr_output_type, Attribute,\n    NextAttribute,\n};\nuse crate::{\n    html::attribute::{\n        maybe_next_attr_erasure_macros::next_attr_combine, NamedAttributeKey,\n    },\n    prelude::AddAnyAttr,\n    view::{Position, ToTemplate},\n};\nuse send_wrapper::SendWrapper;\nuse std::{marker::PhantomData, sync::Arc};\n\n/// Adds a directive to the element, which runs some custom logic in the browser when the element\n/// is created or hydrated.\npub trait DirectiveAttribute<T, P, D>\nwhere\n    D: IntoDirective<T, P>,\n{\n    /// The type of the element with the directive added.\n    type Output;\n\n    /// Adds a directive to the element, which runs some custom logic in the browser when the element\n    /// is created or hydrated.\n    fn directive(self, handler: D, param: P) -> Self::Output;\n}\n\nimpl<V, T, P, D> DirectiveAttribute<T, P, D> for V\nwhere\n    V: AddAnyAttr,\n    D: IntoDirective<T, P>,\n    P: Clone + 'static,\n    T: 'static,\n{\n    type Output = <Self as AddAnyAttr>::Output<Directive<T, D, P>>;\n\n    fn directive(self, handler: D, param: P) -> Self::Output {\n        self.add_any_attr(directive(handler, param))\n    }\n}\n\n/// Adds a directive to the element, which runs some custom logic in the browser when the element\n/// is created or hydrated.\n#[inline(always)]\npub fn directive<T, P, D>(handler: D, param: P) -> Directive<T, D, P>\nwhere\n    D: IntoDirective<T, P>,\n{\n    Directive((!cfg!(feature = \"ssr\")).then(|| {\n        SendWrapper::new(DirectiveInner {\n            handler,\n            param,\n            t: PhantomData,\n        })\n    }))\n}\n\n/// Custom logic that runs in the browser when the element is created or hydrated.\n#[derive(Debug)]\npub struct Directive<T, D, P>(Option<SendWrapper<DirectiveInner<T, D, P>>>);\n\nimpl<T, D, P> Clone for Directive<T, D, P>\nwhere\n    P: Clone + 'static,\n    D: Clone,\n{\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\n#[derive(Debug)]\nstruct DirectiveInner<T, D, P> {\n    handler: D,\n    param: P,\n    t: PhantomData<T>,\n}\n\nimpl<T, D, P> Clone for DirectiveInner<T, D, P>\nwhere\n    P: Clone + 'static,\n    D: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            handler: self.handler.clone(),\n            param: self.param.clone(),\n            t: PhantomData,\n        }\n    }\n}\n\nimpl<T, P, D> Attribute for Directive<T, D, P>\nwhere\n    D: IntoDirective<T, P>,\n    P: Clone + 'static, // TODO this is just here to make them cloneable\n    T: 'static,\n{\n    const MIN_LENGTH: usize = 0;\n\n    type AsyncOutput = Self;\n    type State = crate::renderer::types::Element;\n    type Cloneable = Directive<T, D::Cloneable, P>;\n    type CloneableOwned = Directive<T, D::Cloneable, P>;\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(\n        self,\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let inner = self.0.expect(super::FEATURE_CONFLICT_DIAGNOSTIC).take();\n        inner.handler.run(el.clone(), inner.param);\n        el.clone()\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        let inner = self.0.expect(super::FEATURE_CONFLICT_DIAGNOSTIC).take();\n        inner.handler.run(el.clone(), inner.param);\n        el.clone()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let inner = self.0.expect(super::FEATURE_CONFLICT_DIAGNOSTIC).take();\n        inner.handler.run(state.clone(), inner.param);\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into_cloneable_owned()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        let inner = self.0.map(|inner| {\n            let DirectiveInner { handler, param, t } = inner.take();\n            SendWrapper::new(DirectiveInner {\n                handler: handler.into_cloneable(),\n                param,\n                t,\n            })\n        });\n        Directive(inner)\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![]\n    }\n}\n\nimpl<T, D, P> NextAttribute for Directive<T, D, P>\nwhere\n    D: IntoDirective<T, P>,\n    P: Clone + 'static,\n    T: 'static,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\nimpl<T, D, P> ToTemplate for Directive<T, D, P> {\n    const CLASS: &'static str = \"\";\n\n    fn to_template(\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n    }\n}\n\n/// Trait for a directive handler function.\n/// This is used so it's possible to use functions with one or two\n/// parameters as directive handlers.\n///\n/// You can use directives like the following.\n///\n/// ```ignore\n/// # use leptos::{*, html::AnyElement};\n///\n/// // This doesn't take an attribute value\n/// fn my_directive(el: crate::renderer::types::Element) {\n///     // do sth\n/// }\n///\n/// // This requires an attribute value\n/// fn another_directive(el: crate::renderer::types::Element, params: i32) {\n///     // do sth\n/// }\n///\n/// #[component]\n/// pub fn MyComponent() -> impl IntoView {\n///     view! {\n///         // no attribute value\n///         <div use:my_directive></div>\n///\n///         // with an attribute value\n///         <div use:another_directive=8></div>\n///     }\n/// }\n/// ```\n///\n/// A directive is just syntactic sugar for\n///\n/// ```ignore\n/// let node_ref = create_node_ref();\n///\n/// create_effect(move |_| {\n///     if let Some(el) = node_ref.get() {\n///         directive_func(el, possibly_some_param);\n///     }\n/// });\n/// ```\n///\n/// A directive can be a function with one or two parameters.\n/// The first is the element the directive is added to and the optional\n/// second is the parameter that is provided in the attribute.\npub trait IntoDirective<T: ?Sized, P> {\n    /// An equivalent to this directive that is cloneable and owned.\n    type Cloneable: IntoDirective<T, P> + Clone + 'static;\n\n    /// Calls the handler function\n    fn run(&self, el: crate::renderer::types::Element, param: P);\n\n    /// Converts this into a cloneable type.\n    fn into_cloneable(self) -> Self::Cloneable;\n}\n\nimpl<F> IntoDirective<(crate::renderer::types::Element,), ()> for F\nwhere\n    F: Fn(crate::renderer::types::Element) + 'static,\n{\n    type Cloneable = Arc<dyn Fn(crate::renderer::types::Element)>;\n\n    fn run(&self, el: crate::renderer::types::Element, _: ()) {\n        self(el)\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        Arc::new(self)\n    }\n}\n\nimpl IntoDirective<(crate::renderer::types::Element,), ()>\n    for Arc<dyn Fn(crate::renderer::types::Element)>\n{\n    type Cloneable = Arc<dyn Fn(crate::renderer::types::Element)>;\n\n    fn run(&self, el: crate::renderer::types::Element, _: ()) {\n        self(el)\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n}\n\nimpl<F, P> IntoDirective<(crate::renderer::types::Element, P), P> for F\nwhere\n    F: Fn(crate::renderer::types::Element, P) + 'static,\n    P: 'static,\n{\n    type Cloneable = Arc<dyn Fn(crate::renderer::types::Element, P)>;\n\n    fn run(&self, el: crate::renderer::types::Element, param: P) {\n        self(el, param);\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        Arc::new(self)\n    }\n}\n\nimpl<P> IntoDirective<(crate::renderer::types::Element, P), P>\n    for Arc<dyn Fn(crate::renderer::types::Element, P)>\nwhere\n    P: 'static,\n{\n    type Cloneable = Arc<dyn Fn(crate::renderer::types::Element, P)>;\n\n    fn run(&self, el: crate::renderer::types::Element, param: P) {\n        self(el, param)\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n}\n"
  },
  {
    "path": "tachys/src/html/element/custom.rs",
    "content": "use super::ElementWithChildren;\nuse crate::html::element::{ElementType, HtmlElement};\nuse std::fmt::Debug;\n\n/// Creates a custom element.\n#[track_caller]\npub fn custom<E>(tag: E) -> HtmlElement<Custom<E>, (), ()>\nwhere\n    E: AsRef<str>,\n{\n    HtmlElement {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        defined_at: std::panic::Location::caller(),\n        tag: Custom(tag),\n        attributes: (),\n        children: (),\n    }\n}\n\n/// A custom HTML element.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub struct Custom<E>(E);\n\nimpl<E: 'static> ElementType for Custom<E>\nwhere\n    E: AsRef<str> + Send,\n{\n    type Output = web_sys::HtmlElement;\n\n    const SELF_CLOSING: bool = false;\n    const ESCAPE_CHILDREN: bool = true;\n    const TAG: &'static str = \"\";\n    const NAMESPACE: Option<&'static str> = None;\n\n    fn tag(&self) -> &str {\n        self.0.as_ref()\n    }\n}\n\nimpl<E> ElementWithChildren for Custom<E> {}\n"
  },
  {
    "path": "tachys/src/html/element/element_ext.rs",
    "content": "use crate::{\n    html::{\n        attribute::Attribute,\n        class::IntoClass,\n        event::{on, EventDescriptor},\n        style::IntoStyle,\n    },\n    renderer::RemoveEventHandler,\n};\nuse wasm_bindgen::JsValue;\nuse web_sys::Element;\n\n/// Extends an HTML element, allowing you to add attributes and children to the\n/// element's built state at runtime, with a similar API to how they\n/// can be added to the static view tree at compile time.\n///\n/// ```rust,ignore\n/// use tachys::html::element::ElementExt;\n///\n/// let view: HtmlElement<_, _, _, MockDom> = button();\n///\n/// // add an event listener as part of the static type\n/// // this will be lazily added when the element is built\n/// let view = element.on(ev::click, move |_| /* ... */);\n///\n/// // `element` now contains the actual element\n/// let element = element.build();\n/// let remove = element.on(ev::blur, move |_| /* ... */);\n/// ```\npub trait ElementExt {\n    /// Adds an attribute to the element, at runtime.\n    fn attr<At>(&self, attribute: At) -> At::State\n    where\n        At: Attribute;\n\n    /// Adds a class to the element, at runtime.\n    fn class<C>(&self, class: C) -> C::State\n    where\n        C: IntoClass;\n\n    /// Adds a style to the element, at runtime.\n    fn style<S>(&self, style: S) -> S::State\n    where\n        S: IntoStyle;\n\n    /// Adds an event listener to the element, at runtime.\n    fn on<E>(\n        &self,\n        ev: E,\n        cb: impl FnMut(E::EventType) + 'static,\n    ) -> RemoveEventHandler<Element>\n    where\n        E: EventDescriptor + Send + 'static,\n        E::EventType: 'static,\n        E::EventType: From<JsValue>;\n}\n\nimpl<T> ElementExt for T\nwhere\n    T: AsRef<Element>,\n{\n    fn attr<At>(&self, attribute: At) -> At::State\n    where\n        At: Attribute,\n    {\n        attribute.build(self.as_ref())\n    }\n\n    fn class<C>(&self, class: C) -> C::State\n    where\n        C: IntoClass,\n    {\n        class.build(self.as_ref())\n    }\n\n    fn on<E>(\n        &self,\n        ev: E,\n        cb: impl FnMut(E::EventType) + 'static,\n    ) -> RemoveEventHandler<Element>\n    where\n        E: EventDescriptor + Send + 'static,\n        E::EventType: 'static,\n        E::EventType: From<JsValue>,\n    {\n        on::<E, _>(ev, cb).attach(self.as_ref())\n    }\n\n    fn style<S>(&self, style: S) -> S::State\n    where\n        S: IntoStyle,\n    {\n        style.build(self.as_ref())\n    }\n}\n"
  },
  {
    "path": "tachys/src/html/element/elements.rs",
    "content": "use crate::{\n    html::{\n        attribute::{Attr, Attribute, AttributeValue, NextAttribute},\n        element::{ElementType, ElementWithChildren, HtmlElement},\n    },\n    view::Render,\n};\nuse std::fmt::Debug;\n\nmacro_rules! html_element_inner {\n    (\n        #[$meta:meta]\n        $tag:ident\n        $struct_name:ident\n        $ty:ident\n        [$($attr:ty),*]\n        $escape:literal\n    ) => {\n        paste::paste! {\n            #[$meta]\n            #[track_caller]\n            pub fn $tag() -> HtmlElement<$struct_name, (), ()>\n            where\n\n            {\n                HtmlElement {\n                    #[cfg(any(debug_assertions, leptos_debuginfo))]\n                    defined_at: std::panic::Location::caller(),\n                    tag: $struct_name,\n                    attributes: (),\n                    children: (),\n                }\n            }\n\n            #[$meta]\n            #[derive(Debug, Copy, Clone, PartialEq, Eq)]\n            pub struct $struct_name;\n\n            // Typed attribute methods\n            impl<At, Ch> HtmlElement<$struct_name, At, Ch>\n            where\n                At: Attribute,\n                Ch: Render,\n\n            {\n                $(\n                    #[doc = concat!(\"The [`\", stringify!($attr), \"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/\", stringify!($tag), \"#\", stringify!($attr) ,\") attribute on `<\", stringify!($tag), \">`.\")]\n                    pub fn $attr<V>(self, value: V) -> HtmlElement <\n                        $struct_name,\n                        <At as NextAttribute>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>,\n                        Ch\n                    >\n                    where\n                        V: AttributeValue,\n                        At: NextAttribute,\n                        <At as NextAttribute>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,\n                    {\n                        let HtmlElement {\n                            #[cfg(any(debug_assertions, leptos_debuginfo))]\n                            defined_at,\n                            tag,\n                            children,\n                            attributes\n                        } = self;\n                        HtmlElement {\n                            #[cfg(any(debug_assertions, leptos_debuginfo))]\n                            defined_at,\n                            tag,\n                            children,\n                            attributes: attributes.add_any_attr($crate::html::attribute::$attr(value)),\n                        }\n                    }\n                )*\n            }\n\n            impl ElementType for $struct_name {\n                type Output = web_sys::$ty;\n\n                const TAG: &'static str = stringify!($tag);\n                const SELF_CLOSING: bool = false;\n                const ESCAPE_CHILDREN: bool = $escape;\n                const NAMESPACE: Option<&'static str> = None;\n\n                #[inline(always)]\n                fn tag(&self) -> &str {\n                    Self::TAG\n                }\n            }\n\n            impl ElementWithChildren for $struct_name {}\n        }\n    };\n}\n\nmacro_rules! html_elements {\n\t($(\n        #[$meta:meta]\n        $tag:ident\n        $ty:ident\n        [$($attr:ty),*]\n        $escape:literal\n      ),*\n      $(,)?\n    ) => {\n        paste::paste! {\n            $(html_element_inner! {\n                #[$meta]\n                $tag\n                [<$tag:camel>]\n                $ty\n                [$($attr),*]\n                $escape\n            })*\n        }\n    }\n}\n\nmacro_rules! html_self_closing_elements {\n\t($(\n        #[$meta:meta]\n        $tag:ident $ty:ident [$($attr:ty),*] $escape:literal\n      ),*\n      $(,)?\n    ) => {\n        paste::paste! {\n            $(\n                #[$meta]\n                #[track_caller]\n                pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>\n                where\n\n                {\n                    HtmlElement {\n                        #[cfg(any(debug_assertions, leptos_debuginfo))]\n                        defined_at: std::panic::Location::caller(),\n                        attributes: (),\n                        children: (),\n                        tag: [<$tag:camel>],\n                    }\n                }\n\n                #[$meta]\n                #[derive(Debug, Copy, Clone, PartialEq, Eq)]\n                pub struct [<$tag:camel>];\n\n                // Typed attribute methods\n                impl<At> HtmlElement<[<$tag:camel>], At, ()>\n                where\n                    At: Attribute,\n                {\n                    $(\n                        #[doc = concat!(\"The [`\", stringify!($attr), \"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/\", stringify!($tag), \"#\", stringify!($attr) ,\") attribute on `<\", stringify!($tag), \">`.\")]\n                        pub fn $attr<V>(self, value: V) -> HtmlElement<\n                            [<$tag:camel>],\n                            <At as NextAttribute>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>,\n                            (),\n                        >\n                        where\n                            V: AttributeValue,\n                            At: NextAttribute,\n                            <At as NextAttribute>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,\n                        {\n                            let HtmlElement {\n                                 #[cfg(any(debug_assertions, leptos_debuginfo))]\n                                 defined_at,\n                                tag,\n                                children,\n                                attributes,\n                            } = self;\n                            HtmlElement {\n                                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                                defined_at,\n                                tag,\n                                children,\n                                attributes: attributes.add_any_attr($crate::html::attribute::$attr(value)),\n                            }\n                        }\n                    )*\n                }\n\n                impl ElementType for [<$tag:camel>] {\n                    type Output = web_sys::$ty;\n\n                    const TAG: &'static str = stringify!($tag);\n                    const SELF_CLOSING: bool = true;\n                    const ESCAPE_CHILDREN: bool = $escape;\n                    const NAMESPACE: Option<&'static str> = None;\n\n                    #[inline(always)]\n                    fn tag(&self) -> &str {\n                        Self::TAG\n                    }\n                }\n            )*\n\t\t}\n    }\n}\n\nhtml_self_closing_elements! {\n    /// The `<area>` HTML element defines an area inside an image map that has predefined clickable areas. An image map allows geometric areas on an image to be associated with Hyperlink.\n    area HtmlAreaElement [alt, coords, download, href, hreflang, ping, rel, shape, target] true,\n    /// The `<base>` HTML element specifies the base URL to use for all relative URLs in a document. There can be only one `<base>` element in a document.\n    base HtmlBaseElement [href, target] true,\n    /// The `<br>` HTML element produces a line break in text (carriage-return). It is useful for writing a poem or an address, where the division of lines is significant.\n    br HtmlBrElement [] true,\n    /// The `<col>` HTML element defines a column within a table and is used for defining common semantics on all common cells. It is generally found within a colgroup element.\n    col HtmlTableColElement [span] true,\n    /// The `<embed>` HTML element embeds external content at the specified point in the document. This content is provided by an external application or other source of interactive content such as a browser plug-in.\n    embed HtmlEmbedElement [height, src, r#type, width] true,\n    /// The `<hr>` HTML element represents a thematic break between paragraph-level elements: for example, a change of scene in a story, or a shift of topic within a section.\n    hr HtmlHrElement [] true,\n    /// The `<img>` HTML element embeds an image into the document.\n    img HtmlImageElement [alt, attributionsrc, crossorigin, decoding, elementtiming, fetchpriority, height, ismap, loading, referrerpolicy, sizes, src, srcset, usemap, width] true,\n    /// The `<input>` HTML element is used to create interactive controls for web-based forms in order to accept data from the user; a wide variety of types of input data and control widgets are available, depending on the device and user agent. The `<input>` element is one of the most powerful and complex in all of HTML due to the sheer number of combinations of input types and attributes.\n    input HtmlInputElement [accept, alt, autocomplete, capture, checked, dirname, disabled, form, formaction, formenctype, formmethod, formnovalidate, formtarget, height, list, max, maxlength, min, minlength, multiple, name, pattern, placeholder, popovertarget, popovertargetaction, readonly, required, size, src, step, r#type, value, width] true,\n    ///\tThe `<link>` HTML element specifies relationships between the current document and an external resource. This element is most commonly used to link to CSS, but is also used to establish site icons (both \"favicon\" style icons and icons for the home screen and apps on mobile devices) among other things.\n    link HtmlLinkElement [r#as, blocking, crossorigin, fetchpriority, href, hreflang, imagesizes, imagesrcset, integrity, media, rel, referrerpolicy, sizes, r#type] true,\n    ///\tThe `<meta>` HTML element represents Metadata that cannot be represented by other HTML meta-related elements, like base, link, script, style or title.\n    meta HtmlMetaElement [charset, content, http_equiv, name] true,\n    /// The `<source>` HTML element specifies multiple media resources for the picture, the audio element, or the video element. It is an empty element, meaning that it has no content and does not have a closing tag. It is commonly used to offer the same media content in multiple file formats in order to provide compatibility with a broad range of browsers given their differing support for image file formats and media file formats.\n    source HtmlSourceElement [src, r#type, srcset, sizes, media, height, width] true,\n    /// The `<track>` HTML element is used as a child of the media elements, audio and video. It lets you specify timed text tracks (or time-based data), for example to automatically handle subtitles. The tracks are formatted in WebVTT format (.vtt files) — Web Video Text Tracks.\n    track HtmlTrackElement [default, kind, label, src, srclang] true,\n    /// The `<wbr>` HTML element represents a word break opportunity—a position within text where the browser may optionally break a line, though its line-breaking rules would not otherwise create a break at that location.\n    wbr HtmlElement [] true,\n}\n\nhtml_elements! {\n    /// The `<a>` HTML element (or anchor element), with its href attribute, creates a hyperlink to web pages, files, email addresses, locations in the same page, or anything else a URL can address.\n    a HtmlAnchorElement [download, href, hreflang, ping, referrerpolicy, rel, target, r#type ] true,\n    /// The `<abbr>` HTML element represents an abbreviation or acronym; the optional title attribute can provide an expansion or description for the abbreviation. If present, title must contain this full description and nothing else.\n    abbr HtmlElement [] true,\n    /// The `<address>` HTML element indicates that the enclosed HTML provides contact information for a person or people, or for an organization.\n    address HtmlElement [] true,\n    /// The `<article>` HTML element represents a self-contained composition in a document, page, application, or site, which is intended to be independently distributable or reusable (e.g., in syndication). Examples include: a forum post, a magazine or newspaper article, or a blog entry, a product card, a user-submitted comment, an interactive widget or gadget, or any other independent item of content.\n    article HtmlElement [] true,\n    /// The `<aside>` HTML element represents a portion of a document whose content is only indirectly related to the document's main content. Asides are frequently presented as sidebars or call-out boxes.\n    aside HtmlElement [] true,\n    /// The `<audio>` HTML element is used to embed sound content in documents. It may contain one or more audio sources, represented using the src attribute or the source element: the browser will choose the most suitable one. It can also be the destination for streamed media, using a MediaStream.\n    audio HtmlAudioElement [autoplay, controls, crossorigin, r#loop, muted, preload, src] true,\n    /// The `<b>` HTML element is used to draw the reader's attention to the element's contents, which are not otherwise granted special importance. This was formerly known as the Boldface element, and most browsers still draw the text in boldface. However, you should not use `<b>` for styling text; instead, you should use the CSS font-weight property to create boldface text, or the strong element to indicate that text is of special importance.\n    b HtmlElement [] true,\n    /// The `<bdi>` HTML element tells the browser's bidirectional algorithm to treat the text it contains in isolation from its surrounding text. It's particularly useful when a website dynamically inserts some text and doesn't know the directionality of the text being inserted.\n    bdi HtmlElement [] true,\n    /// The `<bdo>` HTML element overrides the current directionality of text, so that the text within is rendered in a different direction.\n    bdo HtmlElement [] true,\n    /// The `<blockquote>` HTML element indicates that the enclosed text is an extended quotation. Usually, this is rendered visually by indentation (see Notes for how to change it). A URL for the source of the quotation may be given using the cite attribute, while a text representation of the source can be given using the cite element.\n    blockquote HtmlQuoteElement [cite] true,\n    /// The `<body>` HTML element represents the content of an HTML document. There can be only one `<body>` element in a document.\n    body HtmlBodyElement [] true,\n    /// The `<button>` HTML element represents a clickable button, used to submit forms or anywhere in a document for accessible, standard button functionality.\n    button HtmlButtonElement [command, commandfor, disabled, form, formaction, formenctype, formmethod, formnovalidate, formtarget, name, r#type, value, popovertarget, popovertargetaction] true,\n    /// Use the HTML `<canvas>` element with either the canvas scripting API or the WebGL API to draw graphics and animations.\n    canvas HtmlCanvasElement [height, width] true,\n    /// The `<caption>` HTML element specifies the caption (or title) of a table.\n    caption HtmlTableCaptionElement [] true,\n    /// The `<cite>` HTML element is used to describe a reference to a cited creative work, and must include the title of that work. The reference may be in an abbreviated form according to context-appropriate conventions related to citation metadata.\n    cite HtmlElement [] true,\n    /// The `<code>` HTML element displays its contents styled in a fashion intended to indicate that the text is a short fragment of computer code. By default, the content text is displayed using the user agent default monospace font.\n    code HtmlElement [] true,\n    /// The `<colgroup>` HTML element defines a group of columns within a table.\n    colgroup HtmlTableColElement [span] true,\n    /// The `<data>` HTML element links a given piece of content with a machine-readable translation. If the content is time- or date-related, the time element must be used.\n    data HtmlDataElement [value] true,\n    /// The `<datalist>` HTML element contains a set of option elements that represent the permissible or recommended options available to choose from within other controls.\n    datalist HtmlDataListElement [] true,\n    /// The `<dd>` HTML element provides the description, definition, or value for the preceding term (dt) in a description list (dl).\n    dd HtmlElement [] true,\n    /// The `<del>` HTML element represents a range of text that has been deleted from a document. This can be used when rendering \"track changes\" or source code diff information, for example. The ins element can be used for the opposite purpose: to indicate text that has been added to the document.\n    del HtmlModElement [cite, datetime] true,\n    /// The `<details>` HTML element creates a disclosure widget in which information is visible only when the widget is toggled into an \"open\" state. A summary or label must be provided using the summary element.\n    details HtmlDetailsElement [name, open] true,\n    /// The `<dfn>` HTML element is used to indicate the term being defined within the context of a definition phrase or sentence. The p element, the dt/dd pairing, or the section element which is the nearest ancestor of the `<dfn>` is considered to be the definition of the term.\n    dfn HtmlElement [] true,\n    /// The `<dialog>` HTML element represents a dialog box or other interactive component, such as a dismissible alert, inspector, or subwindow.\n    dialog HtmlDialogElement [closedby, open] true,\n    /// The `<div>` HTML element is the generic container for flow content. It has no effect on the content or layout until styled in some way using CSS (e.g. styling is directly applied to it, or some kind of layout model like Flexbox is applied to its parent element).\n    div HtmlDivElement [] true,\n    /// The `<dl>` HTML element represents a description list. The element encloses a list of groups of terms (specified using the dt element) and descriptions (provided by dd elements). Common uses for this element are to implement a glossary or to display metadata (a list of key-value pairs).\n    dl HtmlDListElement [] true,\n    /// The `<dt>` HTML element specifies a term in a description or definition list, and as such must be used inside a dl element. It is usually followed by a dd element; however, multiple `<dt>` elements in a row indicate several terms that are all defined by the immediate next dd element.\n    dt HtmlElement [] true,\n    /// The `<em>` HTML element marks text that has stress emphasis. The `<em>` element can be nested, with each level of nesting indicating a greater degree of emphasis.\n    em HtmlElement [] true,\n    /// The `<fieldset>` HTML element is used to group several controls as well as labels (label) within a web form.\n    fieldset HtmlFieldSetElement [disabled, form, name] true,\n    /// The `<figcaption>` HTML element represents a caption or legend describing the rest of the contents of its parent figure element.\n    figcaption HtmlElement [] true,\n    /// The `<figure>` HTML element represents self-contained content, potentially with an optional caption, which is specified using the figcaption element. The figure, its caption, and its contents are referenced as a single unit.\n    figure HtmlElement [] true,\n    /// The `<footer>` HTML element represents a footer for its nearest sectioning content or sectioning root element. A `<footer>` typically contains information about the author of the section, copyright data or links to related documents.\n    footer HtmlElement [] true,\n    /// The `<form>` HTML element represents a document section containing interactive controls for submitting information.\n    form HtmlFormElement [accept_charset, action, autocomplete, enctype, method, name, novalidate, target] true,\n    /// The `<h1>` to `<h6>` HTML elements represent six levels of section headings. `<h1>` is the highest section level and `<h6>` is the lowest.\n    h1 HtmlHeadingElement [] true,\n    /// The `<h1>` to `<h6>` HTML elements represent six levels of section headings. `<h1>` is the highest section level and `<h6>` is the lowest.\n    h2 HtmlHeadingElement [] true,\n    /// The `<h1>` to `<h6>` HTML elements represent six levels of section headings. `<h1>` is the highest section level and `<h6>` is the lowest.\n    h3 HtmlHeadingElement [] true,\n    /// The `<h1>` to `<h6>` HTML elements represent six levels of section headings. `<h1>` is the highest section level and `<h6>` is the lowest.\n    h4 HtmlHeadingElement [] true,\n    /// The `<h1>` to `<h6>` HTML elements represent six levels of section headings. `<h1>` is the highest section level and `<h6>` is the lowest.\n    h5 HtmlHeadingElement [] true,\n    /// The `<h1>` to `<h6>` HTML elements represent six levels of section headings. `<h1>` is the highest section level and `<h6>` is the lowest.\n    h6 HtmlHeadingElement [] true,\n    ///\tThe `<head>` HTML element contains machine-readable information (metadata) about the document, like its title, scripts, and style sheets.\n    head HtmlHeadElement [] true,\n    /// The `<header>` HTML element represents introductory content, typically a group of introductory or navigational aids. It may contain some heading elements but also a logo, a search form, an author name, and other elements.\n    header HtmlElement [] true,\n    /// The `<hgroup>` HTML element represents a heading and related content. It groups a single `<h1>–<h6>` element with one or more `<p>`.\n    hgroup HtmlElement [] true,\n    /// The `<html>` HTML element represents the root (top-level element) of an HTML document, so it is also referred to as the root element. All other elements must be descendants of this element.\n    html HtmlHtmlElement [] true,\n    /// The `<i>` HTML element represents a range of text that is set off from the normal text for some reason, such as idiomatic text, technical terms, taxonomical designations, among others. Historically, these have been presented using italicized type, which is the original source of the `<i>` naming of this element.\n    i HtmlElement [] true,\n    /// The `<iframe>` HTML element represents a nested browsing context, embedding another HTML page into the current one.\n    iframe HtmlIFrameElement [allow, allowfullscreen, allowpaymentrequest, height, name, referrerpolicy, sandbox, src, srcdoc, width] true,\n    /// The `<ins>` HTML element represents a range of text that has been added to a document. You can use the del element to similarly represent a range of text that has been deleted from the document.\n    ins HtmlElement [cite, datetime] true,\n    /// The `<kbd>` HTML element represents a span of inline text denoting textual user input from a keyboard, voice input, or any other text entry device. By convention, the user agent defaults to rendering the contents of a `<kbd>` element using its default monospace font, although this is not mandated by the HTML standard.\n    kbd HtmlElement [] true,\n    /// The `<label>` HTML element represents a caption for an item in a user interface.\n    label HtmlLabelElement [r#for, form] true,\n    /// The `<legend>` HTML element represents a caption for the content of its parent fieldset.\n    legend HtmlLegendElement [] true,\n    /// The `<li>` HTML element is used to represent an item in a list. It must be contained in a parent element: an ordered list (ol), an unordered list (ul), or a menu (menu). In menus and unordered lists, list items are usually displayed using bullet points. In ordered lists, they are usually displayed with an ascending counter on the left, such as a number or letter.\n    li HtmlLiElement [value] true,\n    /// The `<main>` HTML element represents the dominant content of the body of a document. The main content area consists of content that is directly related to or expands upon the central topic of a document, or the central functionality of an application.\n    main HtmlElement [] true,\n    /// The `<map>` HTML element is used with area elements to define an image map (a clickable link area).\n    map HtmlMapElement [name] true,\n    /// The `<mark>` HTML element represents text which is marked or highlighted for reference or notation purposes, due to the marked passage's relevance or importance in the enclosing context.\n    mark HtmlElement [] true,\n    /// The `<menu>` HTML element is a semantic alternative to ul. It represents an unordered list of items (represented by li elements), each of these represent a link or other command that the user can activate.\n    menu HtmlMenuElement [] true,\n    /// The `<meter>` HTML element represents either a scalar value within a known range or a fractional value.\n    meter HtmlMeterElement [value, min, max, low, high, optimum, form] true,\n    /// The `<nav>` HTML element represents a section of a page whose purpose is to provide navigation links, either within the current document or to other documents. Common examples of navigation sections are menus, tables of contents, and indexes.\n    nav HtmlElement [] true,\n    /// The `<noscript>` HTML element defines a section of HTML to be inserted if a script type on the page is unsupported or if scripting is currently turned off in the browser.\n    noscript HtmlElement [] false,\n    /// The `<object>` HTML element represents an external resource, which can be treated as an image, a nested browsing context, or a resource to be handled by a plugin.\n    object HtmlObjectElement [data, form, height, name, r#type, usemap, width] true,\n    /// The `<ol>` HTML element represents an ordered list of items — typically rendered as a numbered list.\n    ol HtmlOListElement [reversed, start, r#type] true,\n    /// The `<optgroup>` HTML element creates a grouping of options within a select element.\n    optgroup HtmlOptGroupElement [disabled, label] true,\n    /// The `<output>` HTML element is a container element into which a site or app can inject the results of a calculation or the outcome of a user action.\n    output HtmlOutputElement [r#for, form, name] true,\n    /// The `<p>` HTML element represents a paragraph. Paragraphs are usually represented in visual media as blocks of text separated from adjacent blocks by blank lines and/or first-line indentation, but HTML paragraphs can be any structural grouping of related content, such as images or form fields.\n    p HtmlParagraphElement [] true,\n    /// The `<picture>` HTML element contains zero or more source elements and one img element to offer alternative versions of an image for different display/device scenarios.\n    picture HtmlPictureElement [] true,\n    /// The `<portal>` HTML element enables the embedding of another HTML page into the current one for the purposes of allowing smoother navigation into new pages.\n    portal HtmlElement [referrerpolicy, src] true,\n    /// The `<pre>` HTML element represents preformatted text which is to be presented exactly as written in the HTML file. The text is typically rendered using a non-proportional, or \"monospaced, font. Whitespace inside this element is displayed as written.\n    pre HtmlPreElement [] true,\n    /// The `<progress>` HTML element displays an indicator showing the completion progress of a task, typically displayed as a progress bar.\n    progress HtmlProgressElement [min, max, value] true,\n    /// The `<q>` HTML element indicates that the enclosed text is a short inline quotation. Most modern browsers implement this by surrounding the text in quotation marks. This element is intended for short quotations that don't require paragraph breaks; for long quotations use the blockquote element.\n    q HtmlQuoteElement [cite] true,\n    /// The `<rp>` HTML element is used to provide fall-back parentheses for browsers that do not support display of ruby annotations using the ruby element. One `<rp>` element should enclose each of the opening and closing parentheses that wrap the rt element that contains the annotation's text.\n    rp HtmlElement [] true,\n    /// The `<rt>` HTML element specifies the ruby text component of a ruby annotation, which is used to provide pronunciation, translation, or transliteration information for East Asian typography. The `<rt>` element must always be contained within a ruby element.\n    rt HtmlElement [] true,\n    /// The `<ruby>` HTML element represents small annotations that are rendered above, below, or next to base text, usually used for showing the pronunciation of East Asian characters. It can also be used for annotating other kinds of text, but this usage is less common.\n    ruby HtmlElement [] true,\n    /// The `<s>` HTML element renders text with a strikethrough, or a line through it. Use the `<s>` element to represent things that are no longer relevant or no longer accurate. However, `<s>` is not appropriate when indicating document edits; for that, use the del and ins elements, as appropriate.\n    s HtmlElement [] true,\n    /// The `<samp>` HTML element is used to enclose inline text which represents sample (or quoted) output from a computer program. Its contents are typically rendered using the browser's default monospaced font (such as Courier or Lucida Console).\n    samp HtmlElement [] true,\n    /// The `<script>` HTML element is used to embed executable code or data; this is typically used to embed or refer to JavaScript code. The `<script>` element can also be used with other languages, such as WebGL's GLSL shader programming language and JSON.\n    script HtmlScriptElement [r#async, crossorigin, defer, fetchpriority, integrity, nomodule, referrerpolicy, src, r#type, blocking] false,\n    /// The `<search>` HTML element is a container representing the parts of the document or application with form controls or other content related to performing a search or filtering operation.\n    search HtmlElement [] true,\n    /// The `<section>` HTML element represents a generic standalone section of a document, which doesn't have a more specific semantic element to represent it. Sections should always have a heading, with very few exceptions.\n    section HtmlElement [] true,\n    /// The `<select>` HTML element represents a control that provides a menu of options:\n    select HtmlSelectElement [autocomplete, disabled, form, multiple, name, required, size] true,\n    /// The `<slot>` HTML element—part of the Web Components technology suite—is a placeholder inside a web component that you can fill with your own markup, which lets you create separate DOM trees and present them together.\n    slot HtmlSlotElement [name] true,\n    /// The `<small>` HTML element represents side-comments and small print, like copyright and legal text, independent of its styled presentation. By default, it renders text within it one font-size smaller, such as from small to x-small.\n    small HtmlElement [] true,\n    /// The `<span>` HTML element is a generic inline container for phrasing content, which does not inherently represent anything. It can be used to group elements for styling purposes (using the class or id attributes), or because they share attribute values, such as lang. It should be used only when no other semantic element is appropriate. `<span>` is very much like a div element, but div is a block-level element whereas a `<span>` is an inline element.\n    span HtmlSpanElement [] true,\n    /// The `<strong>` HTML element indicates that its contents have strong importance, seriousness, or urgency. Browsers typically render the contents in bold type.\n    strong HtmlElement [] true,\n    ///\tThe `<style>` HTML element contains style information for a document, or part of a document. It contains CSS, which is applied to the contents of the document containing the `<style>` element.\n    style HtmlStyleElement [media, blocking] false,\n    /// The `<sub>` HTML element specifies inline text which should be displayed as subscript for solely typographical reasons. Subscripts are typically rendered with a lowered baseline using smaller text.\n    sub HtmlElement [] true,\n    /// The `<summary>` HTML element specifies a summary, caption, or legend for a details element's disclosure box. Clicking the `<summary>` element toggles the state of the parent `<details>` element open and closed.\n    summary HtmlElement [] true,\n    /// The `<sup>` HTML element specifies inline text which is to be displayed as superscript for solely typographical reasons. Superscripts are usually rendered with a raised baseline using smaller text.\n    sup HtmlElement [] true,\n    /// The `<table>` HTML element represents tabular data — that is, information presented in a two-dimensional table comprised of rows and columns of cells containing data.\n    table HtmlTableElement [] true,\n    /// The `<tbody>` HTML element encapsulates a set of table rows (tr elements), indicating that they comprise the body of the table (table).\n    tbody HtmlTableSectionElement [] true,\n    /// The `<td>` HTML element defines a cell of a table that contains data. It participates in the table model.\n    td HtmlTableCellElement [colspan, headers, rowspan] true,\n    /// The `<template>` HTML element is a mechanism for holding HTML that is not to be rendered immediately when a page is loaded but may be instantiated subsequently during runtime using JavaScript.\n    template HtmlTemplateElement [] true,\n    /// The `<textarea>` HTML element represents a multi-line plain-text editing control, useful when you want to allow users to enter a sizeable amount of free-form text, for example a comment on a review or feedback form.\n    textarea HtmlTextAreaElement [autocomplete, cols, dirname, disabled, form, maxlength, minlength, name, placeholder, readonly, required, rows, wrap] false,\n    /// The `<tfoot>` HTML element defines a set of rows summarizing the columns of the table.\n    tfoot HtmlTableSectionElement [] true,\n    /// The `<th>` HTML element defines a cell as header of a group of table cells. The exact nature of this group is defined by the scope and headers attributes.\n    th HtmlTableCellElement [abbr, colspan, headers, rowspan, scope] true,\n    /// The `<thead>` HTML element defines a set of rows defining the head of the columns of the table.\n    thead HtmlTableSectionElement [] true,\n    /// The `<time>` HTML element represents a specific period in time. It may include the datetime attribute to translate dates into machine-readable format, allowing for better search engine results or custom features such as reminders.\n    time HtmlTimeElement [datetime] true,\n    ///\tThe `<title>` HTML element defines the document's title that is shown in a Browser's title bar or a page's tab. It only contains text; tags within the element are ignored.\n    title HtmlTitleElement [] true,\n    /// The `<tr>` HTML element defines a row of cells in a table. The row's cells can then be established using a mix of td (data cell) and th (header cell) elements.\n    tr HtmlTableRowElement [] true,\n    /// The `<u>` HTML element represents a span of inline text which should be rendered in a way that indicates that it has a non-textual annotation. This is rendered by default as a simple solid underline, but may be altered using CSS.\n    u HtmlElement [] true,\n    /// The `<ul>` HTML element represents an unordered list of items, typically rendered as a bulleted list.\n    ul HtmlUListElement [] true,\n    /// The `<var>` HTML element represents the name of a variable in a mathematical expression or a programming context. It's typically presented using an italicized version of the current typeface, although that behavior is browser-dependent.\n    var HtmlElement [] true,\n    /// The `<video>` HTML element embeds a media player which supports video playback into the document. You can use `<video>` for audio content as well, but the audio element may provide a more appropriate user experience.\n    video HtmlVideoElement [autoplay, controls, controlslist, crossorigin, disablepictureinpicture, disableremoteplayback, height, r#loop, muted, playsinline, poster, preload, src, width] true,\n}\n\nhtml_element_inner! {\n    /// The `<option>` HTML element is used to define an item contained in a `<select>`, an` <optgroup>`, or a `<datalist>` element. As such, `<option>` can represent menu items in popups and other lists of items in an HTML document.\n    option Option_ HtmlOptionElement [disabled, label, selected, value] true\n}\n"
  },
  {
    "path": "tachys/src/html/element/inner_html.rs",
    "content": "use super::{ElementWithChildren, HtmlElement};\nuse crate::{\n    html::attribute::{\n        maybe_next_attr_erasure_macros::{\n            next_attr_combine, next_attr_output_type,\n        },\n        Attribute, NamedAttributeKey, NextAttribute,\n    },\n    renderer::Rndr,\n    view::add_attr::AddAnyAttr,\n};\nuse std::{future::Future, sync::Arc};\n\n/// Returns an [`Attribute`] that sets the inner HTML of an element.\n///\n/// No children should be given to this element, as this HTML will be used instead.\n///\n/// # Security\n/// Be very careful when using this method. Always remember to\n/// sanitize the input to avoid a cross-site scripting (XSS)\n/// vulnerability.\n#[inline(always)]\npub fn inner_html<T>(value: T) -> InnerHtml<T>\nwhere\n    T: InnerHtmlValue,\n{\n    InnerHtml { value }\n}\n\n/// Sets the inner HTML of an element.\n#[derive(Debug)]\npub struct InnerHtml<T> {\n    value: T,\n}\n\nimpl<T> Clone for InnerHtml<T>\nwhere\n    T: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            value: self.value.clone(),\n        }\n    }\n}\n\nimpl<T> Attribute for InnerHtml<T>\nwhere\n    T: InnerHtmlValue,\n{\n    const MIN_LENGTH: usize = 0;\n\n    type AsyncOutput = InnerHtml<T::AsyncOutput>;\n    type State = T::State;\n    type Cloneable = InnerHtml<T::Cloneable>;\n    type CloneableOwned = InnerHtml<T::CloneableOwned>;\n\n    fn html_len(&self) -> usize {\n        self.value.html_len()\n    }\n\n    fn to_html(\n        self,\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        inner_html: &mut String,\n    ) {\n        self.value.to_html(inner_html);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        self.value.hydrate::<FROM_SERVER>(el)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        self.value.build(el)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.value.rebuild(state);\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        InnerHtml {\n            value: self.value.into_cloneable(),\n        }\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        InnerHtml {\n            value: self.value.into_cloneable_owned(),\n        }\n    }\n\n    fn dry_resolve(&mut self) {\n        self.value.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        InnerHtml {\n            value: self.value.resolve().await,\n        }\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![NamedAttributeKey::InnerHtml]\n    }\n}\n\nimpl<T> NextAttribute for InnerHtml<T>\nwhere\n    T: InnerHtmlValue,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\n/// Sets the inner HTML of an element.\npub trait InnerHtmlAttribute<T>\nwhere\n    T: InnerHtmlValue,\n\n    Self: Sized + AddAnyAttr,\n{\n    /// Sets the inner HTML of this element.\n    ///\n    /// No children should be given to this element, as this HTML will be used instead.\n    ///\n    /// # Security\n    /// Be very careful when using this method. Always remember to\n    /// sanitize the input to avoid a cross-site scripting (XSS)\n    /// vulnerability.\n    fn inner_html(\n        self,\n        value: T,\n    ) -> <Self as AddAnyAttr>::Output<InnerHtml<T>> {\n        self.add_any_attr(inner_html(value))\n    }\n}\n\nimpl<T, E, At> InnerHtmlAttribute<T> for HtmlElement<E, At, ()>\nwhere\n    Self: AddAnyAttr,\n    E: ElementWithChildren,\n    At: Attribute,\n    T: InnerHtmlValue,\n{\n    fn inner_html(\n        self,\n        value: T,\n    ) -> <Self as AddAnyAttr>::Output<InnerHtml<T>> {\n        self.add_any_attr(inner_html(value))\n    }\n}\n\n/// A possible value for [`InnerHtml`].\npub trait InnerHtmlValue: Send {\n    /// The type after all async data have resolved.\n    type AsyncOutput: InnerHtmlValue;\n    /// The view state retained between building and rebuilding.\n    type State;\n    /// An equivalent value that can be cloned.\n    type Cloneable: InnerHtmlValue + Clone;\n    /// An equivalent value that can be cloned and is `'static`.\n    type CloneableOwned: InnerHtmlValue + Clone + 'static;\n\n    /// The estimated length of the HTML.\n    fn html_len(&self) -> usize;\n\n    /// Renders the class to HTML.\n    fn to_html(self, buf: &mut String);\n\n    /// Renders the class to HTML for a `<template>`.\n    fn to_template(buf: &mut String);\n\n    /// Adds interactivity as necessary, given DOM nodes that were created from HTML that has\n    /// either been rendered on the server, or cloned for a `<template>`.\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State;\n\n    /// Adds this class to the element during client-side rendering.\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State;\n\n    /// Updates the value.\n    fn rebuild(self, state: &mut Self::State);\n\n    /// Converts this to a cloneable type.\n    fn into_cloneable(self) -> Self::Cloneable;\n\n    /// Converts this to a cloneable, owned type.\n    fn into_cloneable_owned(self) -> Self::CloneableOwned;\n\n    /// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For\n    /// reactive types, this can be used to gather data about reactivity or about asynchronous data\n    /// that needs to be loaded.\n    fn dry_resolve(&mut self);\n\n    /// “Resolves” this into a type that is not waiting for any asynchronous data.\n    fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;\n}\n\nimpl InnerHtmlValue for String {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Arc<str>;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, buf: &mut String) {\n        buf.push_str(&self);\n    }\n\n    fn to_template(_buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_inner_html(el, &self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_inner_html(el, &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        if self != state.1 {\n            Rndr::set_inner_html(&state.0, &self);\n            state.1 = self;\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into()\n    }\n\n    fn into_cloneable_owned(self) -> Self::Cloneable {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl InnerHtmlValue for Arc<str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, buf: &mut String) {\n        buf.push_str(&self);\n    }\n\n    fn to_template(_buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_inner_html(el, &self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_inner_html(el, &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        if self != state.1 {\n            Rndr::set_inner_html(&state.0, &self);\n            state.1 = self;\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::Cloneable {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl InnerHtmlValue for &str {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Self;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, buf: &mut String) {\n        buf.push_str(self);\n    }\n\n    fn to_template(_buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_inner_html(el, self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_inner_html(el, self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        if self != state.1 {\n            Rndr::set_inner_html(&state.0, self);\n            state.1 = self;\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl<T> InnerHtmlValue for Option<T>\nwhere\n    T: InnerHtmlValue,\n{\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Option<T::State>);\n    type Cloneable = Option<T::Cloneable>;\n    type CloneableOwned = Option<T::CloneableOwned>;\n\n    fn html_len(&self) -> usize {\n        match self {\n            Some(i) => i.html_len(),\n            None => 0,\n        }\n    }\n\n    fn to_html(self, buf: &mut String) {\n        if let Some(value) = self {\n            value.to_html(buf);\n        }\n    }\n\n    fn to_template(_buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        (el.clone(), self.map(|n| n.hydrate::<FROM_SERVER>(el)))\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        (el.clone(), self.map(|n| n.build(el)))\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let new_state = match (self, &mut state.1) {\n            (None, None) => None,\n            (None, Some(_)) => {\n                Rndr::set_inner_html(&state.0, \"\");\n                Some(None)\n            }\n            (Some(new), None) => Some(Some(new.build(&state.0))),\n            (Some(new), Some(state)) => {\n                new.rebuild(state);\n                None\n            }\n        };\n        if let Some(new_state) = new_state {\n            state.1 = new_state;\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.map(|inner| inner.into_cloneable())\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.map(|inner| inner.into_cloneable_owned())\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n"
  },
  {
    "path": "tachys/src/html/element/mod.rs",
    "content": "#[cfg(any(debug_assertions, leptos_debuginfo))]\nuse crate::hydration::set_currently_hydrating;\n#[cfg(erase_components)]\nuse crate::view::any_view::AnyView;\nuse crate::{\n    html::attribute::Attribute,\n    hydration::{failed_to_cast_element, Cursor},\n    renderer::{CastFrom, Rndr},\n    ssr::StreamBuilder,\n    view::{\n        add_attr::AddAnyAttr, IntoRender, Mountable, Position, PositionState,\n        Render, RenderHtml, ToTemplate,\n    },\n};\nuse const_str_slice_concat::{\n    const_concat, const_concat_with_prefix, str_from_buffer,\n};\nuse futures::future::join;\nuse std::ops::Deref;\n\nmod custom;\nmod element_ext;\nmod elements;\nmod inner_html;\nuse super::attribute::{\n    any_attribute::AnyAttribute, escape_attr, NextAttribute,\n};\npub use custom::*;\npub use element_ext::*;\npub use elements::*;\npub use inner_html::*;\n#[cfg(any(debug_assertions, leptos_debuginfo))]\nuse std::panic::Location;\n\n/// The typed representation of an HTML element.\n#[derive(Debug, PartialEq, Eq)]\npub struct HtmlElement<E, At, Ch> {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    pub(crate) defined_at: &'static Location<'static>,\n    pub(crate) tag: E,\n    pub(crate) attributes: At,\n    pub(crate) children: Ch,\n}\n\nimpl<E: Clone, At: Clone, Ch: Clone> Clone for HtmlElement<E, At, Ch> {\n    fn clone(&self) -> Self {\n        HtmlElement {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            tag: self.tag.clone(),\n            attributes: self.attributes.clone(),\n            children: self.children.clone(),\n        }\n    }\n}\n\nimpl<E: Copy, At: Copy, Ch: Copy> Copy for HtmlElement<E, At, Ch> {}\n\n/*impl<E, At, Ch> ElementType for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType,\n{\n    type Output = E::Output;\n\n    const TAG: &'static str = E::TAG;\n\n    const SELF_CLOSING: bool = E::SELF_CLOSING;\n\n    fn tag(&self) -> &str {\n        Self::TAG\n    }\n}*/\n\n#[cfg(not(erase_components))]\nimpl<E, At, Ch, NewChild> ElementChild<NewChild> for HtmlElement<E, At, Ch>\nwhere\n    E: ElementWithChildren,\n    Ch: RenderHtml + next_tuple::NextTuple,\n    <Ch as next_tuple::NextTuple>::Output<NewChild::Output>: Render,\n\n    NewChild: IntoRender,\n    NewChild::Output: RenderHtml,\n{\n    type Output = HtmlElement<\n        E,\n        At,\n        <Ch as next_tuple::NextTuple>::Output<NewChild::Output>,\n    >;\n\n    fn child(self, child: NewChild) -> Self::Output {\n        HtmlElement {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            tag: self.tag,\n            attributes: self.attributes,\n            children: self.children.next_tuple(child.into_render()),\n        }\n    }\n}\n\n#[cfg(erase_components)]\nimpl<E, At, Ch, NewChild> ElementChild<NewChild> for HtmlElement<E, At, Ch>\nwhere\n    E: ElementWithChildren,\n    Ch: RenderHtml + NextChildren,\n\n    NewChild: IntoRender,\n    NewChild::Output: RenderHtml,\n{\n    type Output =\n        HtmlElement<E, At, crate::view::iterators::StaticVec<AnyView>>;\n\n    fn child(self, child: NewChild) -> Self::Output {\n        use crate::view::any_view::IntoAny;\n\n        HtmlElement {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            tag: self.tag,\n            attributes: self.attributes,\n            children: self\n                .children\n                .next_children(child.into_render().into_any()),\n        }\n    }\n}\n\n#[cfg(erase_components)]\ntrait NextChildren {\n    fn next_children(\n        self,\n        child: AnyView,\n    ) -> crate::view::iterators::StaticVec<AnyView>;\n}\n\n#[cfg(erase_components)]\nmod erased_tuples {\n    use super::*;\n    use crate::view::{any_view::IntoAny, iterators::StaticVec};\n\n    impl NextChildren for StaticVec<AnyView> {\n        fn next_children(mut self, child: AnyView) -> StaticVec<AnyView> {\n            self.0.push(child);\n            self\n        }\n    }\n\n    impl NextChildren for () {\n        fn next_children(self, child: AnyView) -> StaticVec<AnyView> {\n            vec![child].into()\n        }\n    }\n\n    impl<T: RenderHtml> NextChildren for (T,) {\n        fn next_children(self, child: AnyView) -> StaticVec<AnyView> {\n            vec![self.0.into_owned().into_any(), child].into()\n        }\n    }\n\n    macro_rules! impl_next_children_tuples {\n        ($($ty:ident),*) => {\n            impl<$($ty: RenderHtml),*> NextChildren for ($($ty,)*)\n             {\n                fn next_children(\n                    self, child: AnyView,\n                ) -> StaticVec<AnyView> {\n                    #[allow(non_snake_case)]\n                    let ($($ty,)*) = self;\n                    vec![$($ty.into_owned().into_any(),)* child].into()\n                }\n            }\n        };\n    }\n\n    impl_next_children_tuples!(AA, BB);\n    impl_next_children_tuples!(AA, BB, CC);\n    impl_next_children_tuples!(AA, BB, CC, DD);\n    impl_next_children_tuples!(AA, BB, CC, DD, EE);\n    impl_next_children_tuples!(AA, BB, CC, DD, EE, FF);\n    impl_next_children_tuples!(AA, BB, CC, DD, EE, FF, GG);\n    impl_next_children_tuples!(AA, BB, CC, DD, EE, FF, GG, HH);\n    impl_next_children_tuples!(AA, BB, CC, DD, EE, FF, GG, HH, II);\n    impl_next_children_tuples!(AA, BB, CC, DD, EE, FF, GG, HH, II, JJ);\n    impl_next_children_tuples!(AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK);\n    impl_next_children_tuples!(AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL);\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ, RR\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ, RR,\n        SS\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ, RR,\n        SS, TT\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ, RR,\n        SS, TT, UU\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ, RR,\n        SS, TT, UU, VV\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ, RR,\n        SS, TT, UU, VV, WW\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ, RR,\n        SS, TT, UU, VV, WW, XX\n    );\n    impl_next_children_tuples!(\n        AA, BB, CC, DD, EE, FF, GG, HH, II, JJ, KK, LL, MM, NN, OO, PP, QQ, RR,\n        SS, TT, UU, VV, WW, XX, YY\n    );\n}\n\nimpl<E, At, Ch> AddAnyAttr for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n{\n    type Output<SomeNewAttr: Attribute> =\n        HtmlElement<E, <At as NextAttribute>::Output<SomeNewAttr>, Ch>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        let HtmlElement {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at,\n            tag,\n            attributes,\n            children,\n        } = self;\n        HtmlElement {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at,\n            tag,\n            attributes: attributes.add_any_attr(attr),\n            children,\n        }\n    }\n}\n\n/// Adds a child to the element.\npub trait ElementChild<NewChild>\nwhere\n    NewChild: IntoRender,\n{\n    /// The type of the element, with the child added.\n    type Output;\n\n    /// Adds a child to an element.\n    fn child(self, child: NewChild) -> Self::Output;\n}\n\n/// An HTML element.\npub trait ElementType: Send + 'static {\n    /// The underlying native widget type that this represents.\n    type Output;\n\n    /// The element's tag.\n    const TAG: &'static str;\n    /// Whether the element is self-closing.\n    const SELF_CLOSING: bool;\n    /// Whether the element's children should be escaped. This should be `true` except for elements\n    /// like `<style>` and `<script>`, which include other languages that should not use HTML\n    /// entity escaping.\n    const ESCAPE_CHILDREN: bool;\n    /// The element's namespace, if it is not HTML.\n    const NAMESPACE: Option<&'static str>;\n\n    /// The element's tag.\n    fn tag(&self) -> &str;\n}\n\n/// Denotes that the type that implements this has a particular HTML element type.\npub trait HasElementType {\n    /// The element type.\n    type ElementType;\n}\n\npub(crate) trait ElementWithChildren {}\n\nimpl<E, At, Ch> HasElementType for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType,\n{\n    type ElementType = E::Output;\n}\n\nimpl<E, At, Ch> Render for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType,\n    At: Attribute,\n    Ch: Render,\n{\n    type State = ElementState<At::State, Ch::State>;\n\n    fn rebuild(self, state: &mut Self::State) {\n        // check whether the tag is the same, for custom elements\n        // because this is const `false` for all other element types,\n        // the compiler should be able to optimize it out\n        if E::TAG.is_empty() {\n            // see https://github.com/leptos-rs/leptos/issues/4412\n            let new_tag = self.tag.tag();\n\n            // this is not particularly efficient, but it saves us from\n            // having to keep track of the tag name for every element state\n            let old_tag = state.el.tag_name();\n            if new_tag != old_tag {\n                let mut new_state = self.build();\n                state.insert_before_this(&mut new_state);\n                state.unmount();\n                *state = new_state;\n                return;\n            }\n        }\n\n        // rebuild attributes and children for any element\n        let ElementState {\n            attrs, children, ..\n        } = state;\n        self.attributes.rebuild(attrs);\n        if let Some(children) = children {\n            self.children.rebuild(children);\n        }\n    }\n\n    fn build(self) -> Self::State {\n        let el = Rndr::create_element(self.tag.tag(), E::NAMESPACE);\n\n        let attrs = self.attributes.build(&el);\n\n        let children = if E::SELF_CLOSING {\n            None\n        } else {\n            let mut children = self.children.build();\n            children.mount(&el, None);\n            Some(children)\n        };\n\n        ElementState {\n            el,\n            attrs,\n            children,\n        }\n    }\n}\n\nimpl<E, At, Ch> RenderHtml for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType + Send,\n    At: Attribute + Send,\n    Ch: RenderHtml + Send,\n{\n    type AsyncOutput = HtmlElement<E, At::AsyncOutput, Ch::AsyncOutput>;\n    type Owned = HtmlElement<E, At::CloneableOwned, Ch::Owned>;\n\n    const MIN_LENGTH: usize = if E::SELF_CLOSING {\n        3 // < ... />\n        + E::TAG.len()\n        + At::MIN_LENGTH\n    } else {\n        2 // < ... >\n        + E::TAG.len()\n        + At::MIN_LENGTH\n        + Ch::MIN_LENGTH\n        + 3 // </ ... >\n        + E::TAG.len()\n    };\n\n    fn dry_resolve(&mut self) {\n        self.attributes.dry_resolve();\n        self.children.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        let (attributes, children) =\n            join(self.attributes.resolve(), self.children.resolve()).await;\n        HtmlElement {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            tag: self.tag,\n            attributes,\n            children,\n        }\n    }\n\n    fn html_len(&self) -> usize {\n        if E::SELF_CLOSING {\n            3 // < ... />\n        + E::TAG.len()\n        + self.attributes.html_len()\n        } else {\n            2 // < ... >\n        + E::TAG.len()\n        + self.attributes.html_len()\n        + self.children.html_len()\n        + 3 // </ ... >\n        + E::TAG.len()\n        }\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        _escape: bool,\n        mark_branches: bool,\n        extra_attributes: Vec<AnyAttribute>,\n    ) {\n        // opening tag\n        buf.push('<');\n        buf.push_str(self.tag.tag());\n\n        let inner_html =\n            attributes_to_html((self.attributes, extra_attributes), buf);\n\n        buf.push('>');\n\n        if !E::SELF_CLOSING {\n            if !inner_html.is_empty() {\n                buf.push_str(&inner_html);\n            } else if Ch::EXISTS {\n                // children\n                *position = Position::FirstChild;\n                self.children.to_html_with_buf(\n                    buf,\n                    position,\n                    E::ESCAPE_CHILDREN,\n                    mark_branches,\n                    vec![],\n                );\n            }\n\n            // closing tag\n            buf.push_str(\"</\");\n            buf.push_str(self.tag.tag());\n            buf.push('>');\n        }\n        *position = Position::NextChild;\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buffer: &mut StreamBuilder,\n        position: &mut Position,\n        _escape: bool,\n        mark_branches: bool,\n        extra_attributes: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let mut buf = String::with_capacity(Self::MIN_LENGTH);\n        // opening tag\n        buf.push('<');\n        buf.push_str(self.tag.tag());\n\n        let inner_html =\n            attributes_to_html((self.attributes, extra_attributes), &mut buf);\n\n        buf.push('>');\n        buffer.push_sync(&buf);\n\n        if !E::SELF_CLOSING {\n            // children\n            *position = Position::FirstChild;\n            if !inner_html.is_empty() {\n                buffer.push_sync(&inner_html);\n            } else if Ch::EXISTS {\n                self.children.to_html_async_with_buf::<OUT_OF_ORDER>(\n                    buffer,\n                    position,\n                    E::ESCAPE_CHILDREN,\n                    mark_branches,\n                    vec![],\n                );\n            }\n\n            // closing tag\n            let mut buf = String::with_capacity(3 + E::TAG.len());\n            buf.push_str(\"</\");\n            buf.push_str(self.tag.tag());\n            buf.push('>');\n            buffer.push_sync(&buf);\n        }\n        *position = Position::NextChild;\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        // non-Static custom elements need special support in templates\n        // because they haven't been inserted type-wise\n        if E::TAG.is_empty() && !FROM_SERVER {\n            panic!(\"Custom elements are not supported in ViewTemplate.\");\n        }\n\n        // codegen optimisation:\n        fn inner_1(\n            cursor: &Cursor,\n            position: &PositionState,\n            tag_name: &str,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: &'static std::panic::Location<'static>,\n        ) -> crate::renderer::types::Element {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            {\n                set_currently_hydrating(Some(defined_at));\n            }\n\n            let curr_position = position.get();\n            if curr_position == Position::FirstChild {\n                cursor.child();\n            } else if curr_position != Position::Current {\n                cursor.sibling();\n            }\n            crate::renderer::types::Element::cast_from(cursor.current())\n                .unwrap_or_else(|| {\n                    failed_to_cast_element(tag_name, cursor.current())\n                })\n        }\n        let el = inner_1(\n            cursor,\n            position,\n            E::TAG,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            self.defined_at,\n        );\n\n        let attrs = self.attributes.hydrate::<FROM_SERVER>(&el);\n\n        // hydrate children\n        let children = if !Ch::EXISTS || !E::ESCAPE_CHILDREN {\n            None\n        } else {\n            position.set(Position::FirstChild);\n            Some(self.children.hydrate::<FROM_SERVER>(cursor, position))\n        };\n\n        // codegen optimisation:\n        fn inner_2(\n            cursor: &Cursor,\n            position: &PositionState,\n            el: &crate::renderer::types::Element,\n        ) {\n            // go to next sibling\n            cursor.set(\n                <crate::renderer::types::Element as AsRef<\n                    crate::renderer::types::Node,\n                >>::as_ref(el)\n                .clone(),\n            );\n            position.set(Position::NextChild);\n        }\n        inner_2(cursor, position, &el);\n\n        ElementState {\n            el,\n            attrs,\n            children,\n        }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        // codegen optimisation:\n        fn inner_1(\n            cursor: &Cursor,\n            position: &PositionState,\n            tag_name: &str,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: &'static std::panic::Location<'static>,\n        ) -> crate::renderer::types::Element {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            {\n                set_currently_hydrating(Some(defined_at));\n            }\n\n            let curr_position = position.get();\n            if curr_position == Position::FirstChild {\n                cursor.child();\n            } else if curr_position != Position::Current {\n                cursor.sibling();\n            }\n            crate::renderer::types::Element::cast_from(cursor.current())\n                .unwrap_or_else(|| {\n                    failed_to_cast_element(tag_name, cursor.current())\n                })\n        }\n        let el = inner_1(\n            cursor,\n            position,\n            E::TAG,\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            self.defined_at,\n        );\n\n        let attrs = self.attributes.hydrate::<true>(&el);\n\n        // hydrate children\n        let children = if !Ch::EXISTS || !E::ESCAPE_CHILDREN {\n            None\n        } else {\n            position.set(Position::FirstChild);\n            Some(self.children.hydrate_async(cursor, position).await)\n        };\n\n        // codegen optimisation:\n        fn inner_2(\n            cursor: &Cursor,\n            position: &PositionState,\n            el: &crate::renderer::types::Element,\n        ) {\n            // go to next sibling\n            cursor.set(\n                <crate::renderer::types::Element as AsRef<\n                    crate::renderer::types::Node,\n                >>::as_ref(el)\n                .clone(),\n            );\n            position.set(Position::NextChild);\n        }\n        inner_2(cursor, position, &el);\n\n        ElementState {\n            el,\n            attrs,\n            children,\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        HtmlElement {\n            #[cfg(any(debug_assertions, leptos_debuginfo))]\n            defined_at: self.defined_at,\n            tag: self.tag,\n            attributes: self.attributes.into_cloneable_owned(),\n            children: self.children.into_owned(),\n        }\n    }\n}\n\n/// Renders an [`Attribute`] (which can be one or more HTML attributes) into an HTML buffer.\npub fn attributes_to_html<At>(attr: At, buf: &mut String) -> String\nwhere\n    At: Attribute,\n{\n    // `class` and `style` are created first, and pushed later\n    // this is because they can be filled by a mixture of values that include\n    // either the whole value (`class=\"...\"` or `style=\"...\"`) and individual\n    // classes and styles (`class:foo=true` or `style:height=\"40px\"`), so they\n    // need to be filled during the whole attribute-creation process and then\n    // added\n\n    // String doesn't allocate until the first push, so this is cheap if there\n    // is no class or style on an element\n    let mut class = String::new();\n    let mut style = String::new();\n    let mut inner_html = String::new();\n\n    // inject regular attributes, and fill class and style\n    attr.to_html(buf, &mut class, &mut style, &mut inner_html);\n\n    if !class.is_empty() {\n        buf.push(' ');\n        buf.push_str(\"class=\\\"\");\n        buf.push_str(&escape_attr(class.trim_start().trim_end()));\n        buf.push('\"');\n    }\n    if !style.is_empty() {\n        buf.push(' ');\n        buf.push_str(\"style=\\\"\");\n        buf.push_str(&escape_attr(style.trim_start().trim_end()));\n        buf.push('\"');\n    }\n\n    inner_html\n}\n\n/// The retained view state for an HTML element.\npub struct ElementState<At, Ch> {\n    pub(crate) el: crate::renderer::types::Element,\n    pub(crate) attrs: At,\n    pub(crate) children: Option<Ch>,\n}\n\nimpl<At, Ch> Deref for ElementState<At, Ch> {\n    type Target = crate::renderer::types::Element;\n\n    fn deref(&self) -> &Self::Target {\n        &self.el\n    }\n}\n\nimpl<At, Ch> Mountable for ElementState<At, Ch> {\n    fn unmount(&mut self) {\n        Rndr::remove(&self.el);\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        Rndr::insert_node(parent, &self.el, marker);\n    }\n\n    fn try_mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) -> bool {\n        Rndr::try_insert_node(parent, &self.el, marker)\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        // codegen optimisation:\n        fn inner(\n            element: &crate::renderer::types::Element,\n            child: &mut dyn Mountable,\n        ) -> bool {\n            if let Some(parent) = Rndr::get_parent(element)\n                .and_then(crate::renderer::types::Element::cast_from)\n            {\n                child.mount(&parent, Some(element));\n                true\n            } else {\n                false\n            }\n        }\n        inner(&self.el, child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        // codegen optimisation:\n        fn inner(\n            element: &crate::renderer::types::Element,\n        ) -> Vec<crate::renderer::types::Element> {\n            vec![element.clone()]\n        }\n        inner(&self.el)\n    }\n}\n\nimpl<E, At, Ch> ToTemplate for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType,\n    At: Attribute + ToTemplate,\n    Ch: Render + ToTemplate,\n{\n    const TEMPLATE: &'static str = str_from_buffer(&const_concat(&[\n        \"<\",\n        E::TAG,\n        At::TEMPLATE,\n        str_from_buffer(&const_concat_with_prefix(\n            &[At::CLASS],\n            \" class=\\\"\",\n            \"\\\"\",\n        )),\n        str_from_buffer(&const_concat_with_prefix(\n            &[At::STYLE],\n            \" style=\\\"\",\n            \"\\\"\",\n        )),\n        \">\",\n        Ch::TEMPLATE,\n        \"</\",\n        E::TAG,\n        \">\",\n    ]));\n\n    #[allow(unused)] // the variables `class` and `style` might be used, but only with `nightly` feature\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        // for custom elements without type known at compile time, do nothing\n        if !E::TAG.is_empty() {\n            // opening tag and attributes\n            let mut class = String::new();\n            let mut style = String::new();\n            let mut inner_html = String::new();\n\n            buf.push('<');\n            buf.push_str(E::TAG);\n            <At as ToTemplate>::to_template_attribute(\n                buf,\n                &mut class,\n                &mut style,\n                &mut inner_html,\n                position,\n            );\n\n            if !class.is_empty() {\n                buf.push(' ');\n                buf.push_str(\"class=\\\"\");\n                buf.push_str(class.trim_start().trim_end());\n                buf.push('\"');\n            }\n            if !style.is_empty() {\n                buf.push(' ');\n                buf.push_str(\"style=\\\"\");\n                buf.push_str(style.trim_start().trim_end());\n                buf.push('\"');\n            }\n            buf.push('>');\n\n            // children\n            *position = Position::FirstChild;\n            class.clear();\n            style.clear();\n            inner_html.clear();\n            Ch::to_template(\n                buf,\n                &mut class,\n                &mut style,\n                &mut inner_html,\n                position,\n            );\n\n            // closing tag\n            buf.push_str(\"</\");\n            buf.push_str(E::TAG);\n            buf.push('>');\n            *position = Position::NextChild;\n        }\n    }\n}\n/*\n#[cfg(all(test, feature = \"testing\"))]\nmod tests {\n    #[cfg(all(feature = \"nightly\", rustc_nightly))]\n    use super::RenderHtml;\n    use super::{main, p, HtmlElement};\n    use crate::{\n        html::{\n            attribute::global::GlobalAttributes,\n            element::{em, ElementChild, Main},\n        },\n        renderer::mock_dom::MockDom,\n        view::Render,\n    };\n\n    #[test]\n    fn mock_dom_creates_element() {\n        let el: HtmlElement<Main, _, _, MockDom> =\n            main().child(p().id(\"test\").lang(\"en\").child(\"Hello, world!\"));\n        let el = el.build();\n        assert_eq!(\n            el.el.to_debug_html(),\n            \"<main><p id=\\\"test\\\" lang=\\\"en\\\">Hello, world!</p></main>\"\n        );\n    }\n\n    #[test]\n    fn mock_dom_creates_element_with_several_children() {\n        let el: HtmlElement<Main, _, _, MockDom> = main().child(p().child((\n            \"Hello, \",\n            em().child(\"beautiful\"),\n            \" world!\",\n        )));\n        let el = el.build();\n        assert_eq!(\n            el.el.to_debug_html(),\n            \"<main><p>Hello, <em>beautiful</em> world!</p></main>\"\n        );\n    }\n\n    #[cfg(all(feature = \"nightly\", rustc_nightly))]\n    #[test]\n    fn html_render_allocates_appropriate_buffer() {\n        use crate::view::static_types::Static;\n\n        let el: HtmlElement<Main, _, _, MockDom> = main().child(p().child((\n            Static::<\"Hello, \">,\n            em().child(Static::<\"beautiful\">),\n            Static::<\" world!\">,\n        )));\n        let allocated_len = el.html_len();\n        let html = el.to_html();\n        assert_eq!(\n            html,\n            \"<main><p>Hello, <em>beautiful</em> world!</p></main>\"\n        );\n        assert_eq!(html.len(), allocated_len);\n    }\n}\n */\n"
  },
  {
    "path": "tachys/src/html/event.rs",
    "content": "use crate::{\n    html::attribute::{\n        maybe_next_attr_erasure_macros::next_attr_combine, Attribute,\n        NamedAttributeKey,\n    },\n    renderer::{CastFrom, RemoveEventHandler, Rndr},\n    view::{Position, ToTemplate},\n};\nuse send_wrapper::SendWrapper;\nuse std::{\n    borrow::Cow,\n    cell::RefCell,\n    fmt::Debug,\n    marker::PhantomData,\n    ops::{Deref, DerefMut},\n    rc::Rc,\n};\nuse wasm_bindgen::convert::FromWasmAbi;\n\n/// A cloneable event callback.\npub type SharedEventCallback<E> = Rc<RefCell<dyn FnMut(E)>>;\n\n/// A function that can be called in response to an event.\npub trait EventCallback<E>: 'static {\n    /// Runs the event handler.\n    fn invoke(&mut self, event: E);\n\n    /// Converts this into a cloneable/shared event handler.\n    fn into_shared(self) -> SharedEventCallback<E>;\n}\n\nimpl<E: 'static> EventCallback<E> for SharedEventCallback<E> {\n    fn invoke(&mut self, event: E) {\n        let mut fun = self.borrow_mut();\n        fun(event)\n    }\n\n    fn into_shared(self) -> SharedEventCallback<E> {\n        self\n    }\n}\n\nimpl<F, E> EventCallback<E> for F\nwhere\n    F: FnMut(E) + 'static,\n{\n    fn invoke(&mut self, event: E) {\n        self(event)\n    }\n\n    fn into_shared(self) -> SharedEventCallback<E> {\n        Rc::new(RefCell::new(self))\n    }\n}\n\n/// An event listener with a typed event target.\npub struct Targeted<E, T> {\n    event: E,\n    el_ty: PhantomData<T>,\n}\n\nimpl<E, T> Targeted<E, T> {\n    /// Returns the inner event.\n    pub fn into_inner(self) -> E {\n        self.event\n    }\n\n    /// Returns the event's target, as an HTML element of the correct type.\n    pub fn target(&self) -> T\n    where\n        T: CastFrom<crate::renderer::types::Element>,\n\n        crate::renderer::types::Event: From<E>,\n        E: Clone,\n    {\n        let ev = crate::renderer::types::Event::from(self.event.clone());\n        Rndr::event_target(&ev)\n    }\n}\n\nimpl<E, T> Deref for Targeted<E, T> {\n    type Target = E;\n\n    fn deref(&self) -> &Self::Target {\n        &self.event\n    }\n}\n\nimpl<E, T> DerefMut for Targeted<E, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.event\n    }\n}\n\nimpl<E, T> From<E> for Targeted<E, T> {\n    fn from(event: E) -> Self {\n        Targeted {\n            event,\n            el_ty: PhantomData,\n        }\n    }\n}\n\n/// Creates an [`Attribute`] that will add an event listener to an element.\npub fn on<E, F>(event: E, cb: F) -> On<E, F>\nwhere\n    F: FnMut(E::EventType) + 'static,\n    E: EventDescriptor + Send + 'static,\n    E::EventType: 'static,\n    E::EventType: From<crate::renderer::types::Event>,\n{\n    On {\n        event,\n        #[cfg(feature = \"reactive_graph\")]\n        owner: reactive_graph::owner::Owner::current().unwrap_or_default(),\n        cb: (!cfg!(feature = \"ssr\")).then(|| SendWrapper::new(cb)),\n    }\n}\n\n/// Creates an [`Attribute`] that will add an event listener with a typed target to an element.\n#[allow(clippy::type_complexity)]\npub fn on_target<E, T, F>(\n    event: E,\n    mut cb: F,\n) -> On<E, Box<dyn FnMut(E::EventType)>>\nwhere\n    T: HasElementType,\n    F: FnMut(Targeted<E::EventType, <T as HasElementType>::ElementType>)\n        + 'static,\n    E: EventDescriptor + Send + 'static,\n    E::EventType: 'static,\n\n    E::EventType: From<crate::renderer::types::Event>,\n{\n    on(event, Box::new(move |ev: E::EventType| cb(ev.into())))\n}\n\n/// An [`Attribute`] that adds an event listener to an element.\npub struct On<E, F> {\n    event: E,\n    #[cfg(feature = \"reactive_graph\")]\n    owner: reactive_graph::owner::Owner,\n    cb: Option<SendWrapper<F>>,\n}\n\nimpl<E, F> Clone for On<E, F>\nwhere\n    E: Clone,\n    F: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            event: self.event.clone(),\n            #[cfg(feature = \"reactive_graph\")]\n            owner: self.owner.clone(),\n            cb: self.cb.clone(),\n        }\n    }\n}\n\nimpl<E, F> On<E, F>\nwhere\n    F: EventCallback<E::EventType>,\n    E: EventDescriptor + Send + 'static,\n    E::EventType: 'static,\n    E::EventType: From<crate::renderer::types::Event>,\n{\n    /// Attaches the event listener to the element.\n    pub fn attach(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> RemoveEventHandler<crate::renderer::types::Element> {\n        fn attach_inner(\n            el: &crate::renderer::types::Element,\n            cb: Box<dyn FnMut(crate::renderer::types::Event)>,\n            name: Cow<'static, str>,\n            // TODO investigate: does passing this as an option\n            // (rather than, say, having a const DELEGATED: bool)\n            // add to binary size?\n            delegation_key: Option<Cow<'static, str>>,\n        ) -> RemoveEventHandler<crate::renderer::types::Element> {\n            match delegation_key {\n                None => Rndr::add_event_listener(el, &name, cb),\n                Some(key) => {\n                    Rndr::add_event_listener_delegated(el, name, key, cb)\n                }\n            }\n        }\n\n        let mut cb = self.cb.expect(super::FEATURE_CONFLICT_DIAGNOSTIC).take();\n\n        #[cfg(feature = \"tracing\")]\n        let span = tracing::Span::current();\n\n        let cb = Box::new(move |ev: crate::renderer::types::Event| {\n            #[cfg(all(debug_assertions, feature = \"reactive_graph\"))]\n            let _rx_guard =\n                reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n            #[cfg(feature = \"tracing\")]\n            let _tracing_guard = span.enter();\n\n            let ev = E::EventType::from(ev);\n\n            #[cfg(feature = \"reactive_graph\")]\n            self.owner.with(|| cb.invoke(ev));\n            #[cfg(not(feature = \"reactive_graph\"))]\n            cb.invoke(ev);\n        }) as Box<dyn FnMut(crate::renderer::types::Event)>;\n\n        attach_inner(\n            el,\n            cb,\n            self.event.name(),\n            (E::BUBBLES && cfg!(feature = \"delegation\"))\n                .then(|| self.event.event_delegation_key()),\n        )\n    }\n\n    /// Attaches the event listener to the element as a listener that is triggered during the capture phase,\n    /// meaning it will fire before any event listeners further down in the DOM.\n    pub fn attach_capture(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> RemoveEventHandler<crate::renderer::types::Element> {\n        fn attach_inner(\n            el: &crate::renderer::types::Element,\n            cb: Box<dyn FnMut(crate::renderer::types::Event)>,\n            name: Cow<'static, str>,\n        ) -> RemoveEventHandler<crate::renderer::types::Element> {\n            Rndr::add_event_listener_use_capture(el, &name, cb)\n        }\n\n        let mut cb = self.cb.expect(super::FEATURE_CONFLICT_DIAGNOSTIC).take();\n\n        #[cfg(feature = \"tracing\")]\n        let span = tracing::Span::current();\n\n        let cb = Box::new(move |ev: crate::renderer::types::Event| {\n            #[cfg(all(debug_assertions, feature = \"reactive_graph\"))]\n            let _rx_guard =\n                reactive_graph::diagnostics::SpecialNonReactiveZone::enter();\n            #[cfg(feature = \"tracing\")]\n            let _tracing_guard = span.enter();\n\n            let ev = E::EventType::from(ev);\n\n            #[cfg(feature = \"reactive_graph\")]\n            self.owner.with(|| cb.invoke(ev));\n            #[cfg(not(feature = \"reactive_graph\"))]\n            cb.invoke(ev);\n        }) as Box<dyn FnMut(crate::renderer::types::Event)>;\n\n        attach_inner(el, cb, self.event.name())\n    }\n}\n\nimpl<E, F> Debug for On<E, F>\nwhere\n    E: Debug,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_tuple(\"On\").field(&self.event).finish()\n    }\n}\n\nimpl<E, F> Attribute for On<E, F>\nwhere\n    F: EventCallback<E::EventType>,\n    E: EventDescriptor + Send + 'static,\n    E::EventType: 'static,\n\n    E::EventType: From<crate::renderer::types::Event>,\n{\n    const MIN_LENGTH: usize = 0;\n    type AsyncOutput = Self;\n    // a function that can be called once to remove the event listener\n    type State = (\n        crate::renderer::types::Element,\n        Option<RemoveEventHandler<crate::renderer::types::Element>>,\n    );\n    type Cloneable = On<E, SharedEventCallback<E::EventType>>;\n    type CloneableOwned = On<E, SharedEventCallback<E::EventType>>;\n\n    #[inline(always)]\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    #[inline(always)]\n    fn to_html(\n        self,\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n    }\n\n    #[inline(always)]\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let cleanup = if E::CAPTURE {\n            self.attach_capture(el)\n        } else {\n            self.attach(el)\n        };\n        (el.clone(), Some(cleanup))\n    }\n\n    #[inline(always)]\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        let cleanup = if E::CAPTURE {\n            self.attach_capture(el)\n        } else {\n            self.attach(el)\n        };\n        (el.clone(), Some(cleanup))\n    }\n\n    #[inline(always)]\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev_cleanup) = state;\n        if let Some(prev) = prev_cleanup.take() {\n            if let Some(remove) = prev.into_inner() {\n                remove();\n            }\n        }\n        *prev_cleanup = Some(if E::CAPTURE {\n            self.attach_capture(el)\n        } else {\n            self.attach(el)\n        });\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        On {\n            cb: self.cb.map(|cb| SendWrapper::new(cb.take().into_shared())),\n            #[cfg(feature = \"reactive_graph\")]\n            owner: self.owner,\n            event: self.event,\n        }\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        On {\n            cb: self.cb.map(|cb| SendWrapper::new(cb.take().into_shared())),\n            #[cfg(feature = \"reactive_graph\")]\n            owner: self.owner,\n            event: self.event,\n        }\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![]\n    }\n}\n\nimpl<E, F> NextAttribute for On<E, F>\nwhere\n    F: EventCallback<E::EventType>,\n    E: EventDescriptor + Send + 'static,\n    E::EventType: 'static,\n\n    E::EventType: From<crate::renderer::types::Event>,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\nimpl<E, F> ToTemplate for On<E, F> {\n    #[inline(always)]\n    fn to_template(\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n    }\n}\n\n/// A trait for converting types into [web_sys events](web_sys).\npub trait EventDescriptor: Clone {\n    /// The [`web_sys`] event type, such as [`web_sys::MouseEvent`].\n    type EventType: FromWasmAbi;\n\n    /// Indicates if this event bubbles. For example, `click` bubbles,\n    /// but `focus` does not.\n    ///\n    /// If this is true, then the event will be delegated globally if the `delegation`\n    /// feature is enabled. Otherwise, event listeners will be directly attached to the element.\n    const BUBBLES: bool;\n\n    /// Indicates if this event should be handled during the capture phase.\n    const CAPTURE: bool = false;\n\n    /// The name of the event, such as `click` or `mouseover`.\n    fn name(&self) -> Cow<'static, str>;\n\n    /// The key used for event delegation.\n    fn event_delegation_key(&self) -> Cow<'static, str>;\n\n    /// Return the options for this type. This is only used when you create a [`Custom`] event\n    /// handler.\n    #[inline(always)]\n    fn options(&self) -> Option<&web_sys::AddEventListenerOptions> {\n        None\n    }\n}\n\n/// A wrapper that tells the framework to handle an event during the capture phase.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Capture<E> {\n    inner: E,\n}\n\n/// Wraps an event to indicate that it should be handled during the capture phase.\npub fn capture<E>(event: E) -> Capture<E> {\n    Capture { inner: event }\n}\n\nimpl<E: EventDescriptor> EventDescriptor for Capture<E> {\n    type EventType = E::EventType;\n\n    const CAPTURE: bool = true;\n    const BUBBLES: bool = E::BUBBLES;\n\n    fn name(&self) -> Cow<'static, str> {\n        self.inner.name()\n    }\n\n    fn event_delegation_key(&self) -> Cow<'static, str> {\n        self.inner.event_delegation_key()\n    }\n}\n\n/// A custom event.\n#[derive(Debug)]\npub struct Custom<E: FromWasmAbi = web_sys::Event> {\n    name: Cow<'static, str>,\n    options: Option<SendWrapper<web_sys::AddEventListenerOptions>>,\n    _event_type: PhantomData<fn() -> E>,\n}\n\nimpl<E: FromWasmAbi> Clone for Custom<E> {\n    fn clone(&self) -> Self {\n        Self {\n            name: self.name.clone(),\n            options: self.options.clone(),\n            _event_type: PhantomData,\n        }\n    }\n}\n\nimpl<E: FromWasmAbi> EventDescriptor for Custom<E> {\n    type EventType = E;\n\n    fn name(&self) -> Cow<'static, str> {\n        self.name.clone()\n    }\n\n    fn event_delegation_key(&self) -> Cow<'static, str> {\n        format!(\"$$${}\", self.name).into()\n    }\n\n    const BUBBLES: bool = false;\n\n    #[inline(always)]\n    fn options(&self) -> Option<&web_sys::AddEventListenerOptions> {\n        self.options.as_deref()\n    }\n}\n\nimpl<E: FromWasmAbi> Custom<E> {\n    /// Creates a custom event type that can be used within\n    /// [`OnAttribute::on`](crate::prelude::OnAttribute::on), for events\n    /// which are not covered in the [`ev`](crate::html::event) module.\n    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {\n        Self {\n            name: name.into(),\n            options: None,\n            _event_type: PhantomData,\n        }\n    }\n\n    /// Modify the [`AddEventListenerOptions`] used for this event listener.\n    ///\n    /// ```rust\n    /// # use tachys::prelude::*;\n    /// # use tachys::html;\n    /// # use tachys::html::event as ev;\n    /// # fn custom_event() -> impl Render {\n    /// let mut non_passive_wheel = ev::Custom::new(\"wheel\");\n    /// non_passive_wheel.options_mut().set_passive(false);\n    ///\n    /// let canvas =\n    ///     html::element::canvas().on(non_passive_wheel, |e: ev::WheelEvent| {\n    ///         // handle event\n    ///     });\n    /// # canvas\n    /// # }\n    /// ```\n    ///\n    /// [`AddEventListenerOptions`]: web_sys::AddEventListenerOptions\n    pub fn options_mut(&mut self) -> &mut web_sys::AddEventListenerOptions {\n        // It is valid to construct a `SendWrapper` here because\n        // its inner data will only be accessed in the browser's main thread.\n        self.options.get_or_insert_with(|| {\n            SendWrapper::new(web_sys::AddEventListenerOptions::new())\n        })\n    }\n}\n\nmacro_rules! generate_event_types {\n  {$(\n    $( #[$does_not_bubble:ident] )?\n    $( $event:ident )+ : $web_event:ident\n  ),* $(,)?} => {\n    ::paste::paste! {\n      $(\n        #[doc = \"The `\" [< $($event)+ >] \"` event, which receives [\" $web_event \"](web_sys::\" $web_event \") as its argument.\"]\n        #[derive(Copy, Clone, Debug)]\n        #[allow(non_camel_case_types)]\n        pub struct [<$( $event )+ >];\n\n        impl EventDescriptor for [< $($event)+ >] {\n          type EventType = web_sys::$web_event;\n\n          #[inline(always)]\n          fn name(&self) -> Cow<'static, str> {\n            stringify!([< $($event)+ >]).into()\n          }\n\n          #[inline(always)]\n          fn event_delegation_key(&self) -> Cow<'static, str> {\n            concat!(\"$$$\", stringify!([< $($event)+ >])).into()\n          }\n\n          const BUBBLES: bool = true $(&& generate_event_types!($does_not_bubble))?;\n        }\n      )*\n    }\n  };\n\n  (does_not_bubble) => { false }\n}\n\ngenerate_event_types! {\n  // =========================================================\n  // WindowEventHandlersEventMap\n  // =========================================================\n  #[does_not_bubble]\n  after print: Event,\n  #[does_not_bubble]\n  before print: Event,\n  #[does_not_bubble]\n  before unload: BeforeUnloadEvent,\n  #[does_not_bubble]\n  gamepad connected: GamepadEvent,\n  #[does_not_bubble]\n  gamepad disconnected: GamepadEvent,\n  hash change: HashChangeEvent,\n  #[does_not_bubble]\n  language change: Event,\n  #[does_not_bubble]\n  message: MessageEvent,\n  #[does_not_bubble]\n  message error: MessageEvent,\n  #[does_not_bubble]\n  offline: Event,\n  #[does_not_bubble]\n  online: Event,\n  #[does_not_bubble]\n  page hide: PageTransitionEvent,\n  #[does_not_bubble]\n  page show: PageTransitionEvent,\n  pop state: PopStateEvent,\n  rejection handled: PromiseRejectionEvent,\n  #[does_not_bubble]\n  storage: StorageEvent,\n  #[does_not_bubble]\n  unhandled rejection: PromiseRejectionEvent,\n  #[does_not_bubble]\n  unload: Event,\n\n  // =========================================================\n  // GlobalEventHandlersEventMap\n  // =========================================================\n  #[does_not_bubble]\n  abort: UiEvent,\n  animation cancel: AnimationEvent,\n  animation end: AnimationEvent,\n  animation iteration: AnimationEvent,\n  animation start: AnimationEvent,\n  aux click: MouseEvent,\n  before input: InputEvent,\n  before toggle: Event, // web_sys does not include `ToggleEvent`\n  #[does_not_bubble]\n  blur: FocusEvent,\n  #[does_not_bubble]\n  can play: Event,\n  #[does_not_bubble]\n  can play through: Event,\n  change: Event,\n  click: MouseEvent,\n  #[does_not_bubble]\n  close: Event,\n  composition end: CompositionEvent,\n  composition start: CompositionEvent,\n  composition update: CompositionEvent,\n  context menu: MouseEvent,\n  #[does_not_bubble]\n  cue change: Event,\n  dbl click: MouseEvent,\n  drag: DragEvent,\n  drag end: DragEvent,\n  drag enter: DragEvent,\n  drag leave: DragEvent,\n  drag over: DragEvent,\n  drag start: DragEvent,\n  drop: DragEvent,\n  #[does_not_bubble]\n  duration change: Event,\n  #[does_not_bubble]\n  emptied: Event,\n  #[does_not_bubble]\n  ended: Event,\n  #[does_not_bubble]\n  error: ErrorEvent,\n  #[does_not_bubble]\n  focus: FocusEvent,\n  #[does_not_bubble]\n  focus in: FocusEvent,\n  #[does_not_bubble]\n  focus out: FocusEvent,\n  form data: Event, // web_sys does not include `FormDataEvent`\n  #[does_not_bubble]\n  got pointer capture: PointerEvent,\n  input: Event,\n  #[does_not_bubble]\n  invalid: Event,\n  key down: KeyboardEvent,\n  key press: KeyboardEvent,\n  key up: KeyboardEvent,\n  #[does_not_bubble]\n  load: Event,\n  #[does_not_bubble]\n  loaded data: Event,\n  #[does_not_bubble]\n  loaded metadata: Event,\n  #[does_not_bubble]\n  load start: Event,\n  lost pointer capture: PointerEvent,\n  mouse down: MouseEvent,\n  #[does_not_bubble]\n  mouse enter: MouseEvent,\n  #[does_not_bubble]\n  mouse leave: MouseEvent,\n  mouse move: MouseEvent,\n  mouse out: MouseEvent,\n  mouse over: MouseEvent,\n  mouse up: MouseEvent,\n  #[does_not_bubble]\n  pause: Event,\n  #[does_not_bubble]\n  play: Event,\n  #[does_not_bubble]\n  playing: Event,\n  pointer cancel: PointerEvent,\n  pointer down: PointerEvent,\n  #[does_not_bubble]\n  pointer enter: PointerEvent,\n  #[does_not_bubble]\n  pointer leave: PointerEvent,\n  pointer move: PointerEvent,\n  pointer out: PointerEvent,\n  pointer over: PointerEvent,\n  pointer up: PointerEvent,\n  #[does_not_bubble]\n  progress: ProgressEvent,\n  #[does_not_bubble]\n  rate change: Event,\n  reset: Event,\n  #[does_not_bubble]\n  resize: UiEvent,\n  #[does_not_bubble]\n  scroll: Event,\n  #[does_not_bubble]\n  scroll end: Event,\n  security policy violation: SecurityPolicyViolationEvent,\n  #[does_not_bubble]\n  seeked: Event,\n  #[does_not_bubble]\n  seeking: Event,\n  select: Event,\n  #[does_not_bubble]\n  selection change: Event,\n  select start: Event,\n  slot change: Event,\n  #[does_not_bubble]\n  stalled: Event,\n  submit: SubmitEvent,\n  #[does_not_bubble]\n  suspend: Event,\n  #[does_not_bubble]\n  time update: Event,\n  #[does_not_bubble]\n  toggle: Event,\n  touch cancel: TouchEvent,\n  touch end: TouchEvent,\n  touch move: TouchEvent,\n  touch start: TouchEvent,\n  transition cancel: TransitionEvent,\n  transition end: TransitionEvent,\n  transition run: TransitionEvent,\n  transition start: TransitionEvent,\n  #[does_not_bubble]\n  volume change: Event,\n  #[does_not_bubble]\n  waiting: Event,\n  webkit animation end: Event,\n  webkit animation iteration: Event,\n  webkit animation start: Event,\n  webkit transition end: Event,\n  wheel: WheelEvent,\n\n  // =========================================================\n  // WindowEventMap\n  // =========================================================\n  D O M Content Loaded: Event, // Hack for correct casing\n  #[does_not_bubble]\n  device motion: DeviceMotionEvent,\n  #[does_not_bubble]\n  device orientation: DeviceOrientationEvent,\n  #[does_not_bubble]\n  orientation change: Event,\n\n  // =========================================================\n  // DocumentAndElementEventHandlersEventMap\n  // =========================================================\n  copy: ClipboardEvent,\n  cut: ClipboardEvent,\n  paste: ClipboardEvent,\n\n  // =========================================================\n  // DocumentEventMap\n  // =========================================================\n  fullscreen change: Event,\n  fullscreen error: Event,\n  pointer lock change: Event,\n  pointer lock error: Event,\n  #[does_not_bubble]\n  ready state change: Event,\n  visibility change: Event,\n}\n\n// Export `web_sys` event types\nuse super::{\n    attribute::{\n        maybe_next_attr_erasure_macros::next_attr_output_type, NextAttribute,\n    },\n    element::HasElementType,\n};\n#[doc(no_inline)]\npub use web_sys::{\n    AnimationEvent, BeforeUnloadEvent, ClipboardEvent, CompositionEvent,\n    CustomEvent, DeviceMotionEvent, DeviceOrientationEvent, DragEvent,\n    ErrorEvent, Event, FocusEvent, GamepadEvent, HashChangeEvent, InputEvent,\n    KeyboardEvent, MessageEvent, MouseEvent, PageTransitionEvent, PointerEvent,\n    PopStateEvent, ProgressEvent, PromiseRejectionEvent,\n    SecurityPolicyViolationEvent, StorageEvent, SubmitEvent, TouchEvent,\n    TransitionEvent, UiEvent, WheelEvent,\n};\n"
  },
  {
    "path": "tachys/src/html/islands.rs",
    "content": "use super::attribute::{any_attribute::AnyAttribute, Attribute};\nuse crate::{\n    hydration::Cursor,\n    prelude::{Render, RenderHtml},\n    ssr::StreamBuilder,\n    view::{add_attr::AddAnyAttr, Position, PositionState},\n};\n\n/// An island of interactivity in an otherwise-inert HTML document.\npub struct Island<View> {\n    has_element_representation: bool,\n    component: &'static str,\n    props_json: String,\n    view: View,\n}\nconst ISLAND_TAG: &str = \"leptos-island\";\nconst ISLAND_CHILDREN_TAG: &str = \"leptos-children\";\n\nimpl<View> Island<View> {\n    /// Creates a new island with the given component name.\n    pub fn new(component: &'static str, view: View) -> Self {\n        Island {\n            has_element_representation:\n                Self::should_have_element_representation(),\n            component,\n            props_json: String::new(),\n            view,\n        }\n    }\n\n    /// Adds serialized component props as JSON.\n    pub fn with_props(mut self, props_json: String) -> Self {\n        self.props_json = props_json;\n        self\n    }\n\n    fn open_tag(component: &'static str, props: &str, buf: &mut String) {\n        buf.push('<');\n        buf.push_str(ISLAND_TAG);\n        buf.push(' ');\n        buf.push_str(\"data-component=\\\"\");\n        buf.push_str(component);\n        buf.push('\"');\n        if !props.is_empty() {\n            buf.push_str(\" data-props=\\\"\");\n            buf.push_str(&html_escape::encode_double_quoted_attribute(&props));\n            buf.push('\"');\n        }\n        buf.push('>');\n    }\n\n    fn close_tag(buf: &mut String) {\n        buf.push_str(\"</\");\n        buf.push_str(ISLAND_TAG);\n        buf.push('>');\n    }\n\n    /// Whether this island should be represented by an actual HTML element\n    fn should_have_element_representation() -> bool {\n        #[cfg(feature = \"reactive_graph\")]\n        {\n            use reactive_graph::owner::{use_context, IsHydrating};\n            let already_hydrating =\n                use_context::<IsHydrating>().map(|h| h.0).unwrap_or(false);\n            !already_hydrating\n        }\n        #[cfg(not(feature = \"reactive_graph\"))]\n        {\n            true\n        }\n    }\n}\n\nimpl<View> Render for Island<View>\nwhere\n    View: Render,\n{\n    type State = View::State;\n\n    fn build(self) -> Self::State {\n        self.view.build()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.view.rebuild(state);\n    }\n}\n\nimpl<View> AddAnyAttr for Island<View>\nwhere\n    View: RenderHtml,\n{\n    type Output<SomeNewAttr: Attribute> =\n        Island<<View as AddAnyAttr>::Output<SomeNewAttr>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let Island {\n            has_element_representation,\n            component,\n            props_json,\n            view,\n        } = self;\n        Island {\n            has_element_representation,\n            component,\n            props_json,\n            view: view.add_any_attr(attr),\n        }\n    }\n}\n\nimpl<View> RenderHtml for Island<View>\nwhere\n    View: RenderHtml,\n{\n    type AsyncOutput = Island<View::AsyncOutput>;\n    type Owned = Island<View::Owned>;\n\n    const MIN_LENGTH: usize = ISLAND_TAG.len() * 2\n        + \"<>\".len()\n        + \"</>\".len()\n        + \"data-component\".len()\n        + View::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {\n        self.view.dry_resolve()\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        let Island {\n            has_element_representation,\n            component,\n            props_json,\n            view,\n        } = self;\n        Island {\n            has_element_representation,\n            component,\n            props_json,\n            view: view.resolve().await,\n        }\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        let has_element = self.has_element_representation;\n        if has_element {\n            Self::open_tag(self.component, &self.props_json, buf);\n        }\n        self.view.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n        if has_element {\n            Self::close_tag(buf);\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let has_element = self.has_element_representation;\n        // insert the opening tag synchronously\n        let mut tag = String::new();\n        if has_element {\n            Self::open_tag(self.component, &self.props_json, &mut tag);\n        }\n        buf.push_sync(&tag);\n\n        // streaming render for the view\n        self.view.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n\n        // and insert the closing tag synchronously\n        tag.clear();\n        if has_element {\n            Self::close_tag(&mut tag);\n        }\n        buf.push_sync(&tag);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        if self.has_element_representation {\n            if position.get() == Position::FirstChild {\n                cursor.child();\n            } else if position.get() == Position::NextChild {\n                cursor.sibling();\n            }\n            position.set(Position::FirstChild);\n        }\n\n        self.view.hydrate::<FROM_SERVER>(cursor, position)\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        Island {\n            has_element_representation: self.has_element_representation,\n            component: self.component,\n            props_json: self.props_json,\n            view: self.view.into_owned(),\n        }\n    }\n}\n\n/// The children that will be projected into an [`Island`].\npub struct IslandChildren<View> {\n    view: View,\n    on_hydrate: Option<Box<dyn Fn() + Send + Sync>>,\n}\n\nimpl<View> IslandChildren<View> {\n    /// Creates a new representation of the children.\n    pub fn new(view: View) -> Self {\n        IslandChildren {\n            view,\n            on_hydrate: None,\n        }\n    }\n\n    /// Creates a new representation of the children, with a function to be called whenever\n    /// a child island hydrates.\n    pub fn new_with_on_hydrate(\n        view: View,\n        on_hydrate: impl Fn() + Send + Sync + 'static,\n    ) -> Self {\n        IslandChildren {\n            view,\n            on_hydrate: Some(Box::new(on_hydrate)),\n        }\n    }\n\n    fn open_tag(buf: &mut String) {\n        buf.push('<');\n        buf.push_str(ISLAND_CHILDREN_TAG);\n        buf.push('>');\n    }\n\n    fn close_tag(buf: &mut String) {\n        buf.push_str(\"</\");\n        buf.push_str(ISLAND_CHILDREN_TAG);\n        buf.push('>');\n    }\n}\n\nimpl<View> Render for IslandChildren<View>\nwhere\n    View: Render,\n{\n    type State = ();\n\n    fn build(self) -> Self::State {}\n\n    fn rebuild(self, _state: &mut Self::State) {}\n}\n\nimpl<View> AddAnyAttr for IslandChildren<View>\nwhere\n    View: RenderHtml,\n{\n    type Output<SomeNewAttr: Attribute> =\n        IslandChildren<<View as AddAnyAttr>::Output<SomeNewAttr>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let IslandChildren { view, on_hydrate } = self;\n        IslandChildren {\n            view: view.add_any_attr(attr),\n            on_hydrate,\n        }\n    }\n}\n\nimpl<View> RenderHtml for IslandChildren<View>\nwhere\n    View: RenderHtml,\n{\n    type AsyncOutput = IslandChildren<View::AsyncOutput>;\n    type Owned = IslandChildren<View::Owned>;\n\n    const MIN_LENGTH: usize = ISLAND_CHILDREN_TAG.len() * 2\n        + \"<>\".len()\n        + \"</>\".len()\n        + View::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {\n        self.view.dry_resolve()\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        let IslandChildren { view, on_hydrate } = self;\n        IslandChildren {\n            view: view.resolve().await,\n            on_hydrate,\n        }\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        Self::open_tag(buf);\n        self.view.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n        Self::close_tag(buf);\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        // insert the opening tag synchronously\n        let mut tag = String::new();\n        Self::open_tag(&mut tag);\n        buf.push_sync(&tag);\n\n        // streaming render for the view\n        self.view.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n\n        // and insert the closing tag synchronously\n        tag.clear();\n        Self::close_tag(&mut tag);\n        buf.push_sync(&tag);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        // island children aren't hydrated\n        // we update the walk to pass over them\n        // but we don't hydrate their children\n        let curr_position = position.get();\n        if curr_position == Position::FirstChild {\n            cursor.child();\n        } else if curr_position != Position::Current {\n            cursor.sibling();\n        }\n        position.set(Position::NextChild);\n\n        if let Some(on_hydrate) = self.on_hydrate {\n            use crate::{\n                hydration::failed_to_cast_element, renderer::CastFrom,\n            };\n\n            let el =\n                crate::renderer::types::Element::cast_from(cursor.current())\n                    .unwrap_or_else(|| {\n                        failed_to_cast_element(\n                            \"leptos-children\",\n                            cursor.current(),\n                        )\n                    });\n            let cb = wasm_bindgen::closure::Closure::wrap(\n                on_hydrate as Box<dyn Fn()>,\n            );\n            _ = js_sys::Reflect::set(\n                &el,\n                &wasm_bindgen::JsValue::from_str(\"$$on_hydrate\"),\n                &cb.into_js_value(),\n            );\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        IslandChildren {\n            view: self.view.into_owned(),\n            on_hydrate: self.on_hydrate,\n        }\n    }\n}\n"
  },
  {
    "path": "tachys/src/html/mod.rs",
    "content": "use self::attribute::Attribute;\nuse crate::{\n    hydration::Cursor,\n    no_attrs,\n    prelude::{AddAnyAttr, Mountable},\n    renderer::{\n        dom::{Element, Node},\n        CastFrom, Rndr,\n    },\n    view::{Position, PositionState, Render, RenderHtml},\n};\nuse attribute::any_attribute::AnyAttribute;\nuse std::borrow::Cow;\n\n/// Diagnostic message shared by event, directive, and property `.expect()` calls.\n///\n/// When the `ssr` feature is active, tachys skips creating client-side values\n/// (event handlers, directives, properties) to avoid `SendWrapper` cross-thread\n/// panics on multithreaded servers. If these `.expect()` calls fire, it means\n/// the `ssr` feature was activated unintentionally via Cargo feature\n/// unification in a client-side (CSR or hydrate) build.\npub(crate) const FEATURE_CONFLICT_DIAGNOSTIC: &str =\n    \"Value is None because the `ssr` feature is active. When `ssr` is \\\n     enabled, tachys skips creating client-side values (event handlers, \\\n     directives, properties) to avoid cross-thread panics on multithreaded \\\n     servers. If you are building a client-side (CSR or hydrate) target, this \\\n     means the `ssr` feature is being activated unintentionally via Cargo \\\n     feature unification; another dependency in your workspace is enabling \\\n     it. Run `cargo tree -e features -i tachys` to identify the source.\";\n\n/// Types for HTML attributes.\npub mod attribute;\n/// Types for manipulating the `class` attribute and `classList`.\npub mod class;\n/// Types for creating user-defined attributes with custom behavior (directives).\npub mod directive;\n/// Types for HTML elements.\npub mod element;\n/// Types for DOM events.\npub mod event;\n/// Types for adding interactive islands to inert HTML pages.\npub mod islands;\n/// Types for accessing a reference to an HTML element.\npub mod node_ref;\n/// Types for DOM properties.\npub mod property;\n/// Types for the `style` attribute and individual style manipulation.\npub mod style;\n\n/// A `<!DOCTYPE>` declaration.\npub struct Doctype {\n    value: &'static str,\n}\n\n/// Creates a `<!DOCTYPE>`.\npub fn doctype(value: &'static str) -> Doctype {\n    Doctype { value }\n}\n\nimpl Render for Doctype {\n    type State = ();\n\n    fn build(self) -> Self::State {}\n\n    fn rebuild(self, _state: &mut Self::State) {}\n}\n\nno_attrs!(Doctype);\n\nimpl RenderHtml for Doctype {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = \"<!DOCTYPE html>\".len();\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        _position: &mut Position,\n        _escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        buf.push_str(\"<!DOCTYPE \");\n        buf.push_str(self.value);\n        buf.push('>');\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _cursor: &Cursor,\n        _position: &PositionState,\n    ) -> Self::State {\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\n/// An element that contains no interactivity, and whose contents can be known at compile time.\npub struct InertElement {\n    html: Cow<'static, str>,\n}\n\nimpl InertElement {\n    /// Creates a new inert element.\n    pub fn new(html: impl Into<Cow<'static, str>>) -> Self {\n        Self { html: html.into() }\n    }\n}\n\n/// Retained view state for [`InertElement`].\npub struct InertElementState(Cow<'static, str>, Element);\n\nimpl Mountable for InertElementState {\n    fn unmount(&mut self) {\n        self.1.unmount();\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        self.1.mount(parent, marker)\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.1.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![self.1.clone()]\n    }\n}\n\nimpl Render for InertElement {\n    type State = InertElementState;\n\n    fn build(self) -> Self::State {\n        let el = Rndr::create_element_from_html(self.html.clone());\n        InertElementState(self.html, el)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let InertElementState(prev, el) = state;\n        if &self.html != prev {\n            let mut new_el = Rndr::create_element_from_html(self.html.clone());\n            el.insert_before_this(&mut new_el);\n            el.unmount();\n            *el = new_el;\n            *prev = self.html;\n        }\n    }\n}\n\nimpl AddAnyAttr for InertElement {\n    type Output<SomeNewAttr: Attribute> = Self;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        panic!(\n            \"InertElement does not support adding attributes. It should only \\\n             be used as a child, and not returned at the top level.\"\n        )\n    }\n}\n\nimpl RenderHtml for InertElement {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn html_len(&self) -> usize {\n        self.html.len()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        _escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        buf.push_str(&self.html);\n        *position = Position::NextChild;\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let curr_position = position.get();\n        if curr_position == Position::FirstChild {\n            cursor.child();\n        } else if curr_position != Position::Current {\n            cursor.sibling();\n        }\n        let el = crate::renderer::types::Element::cast_from(cursor.current())\n            .unwrap();\n        position.set(Position::NextChild);\n        InertElementState(self.html, el)\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n"
  },
  {
    "path": "tachys/src/html/node_ref.rs",
    "content": "use super::{\n    attribute::{\n        maybe_next_attr_erasure_macros::next_attr_output_type, Attribute,\n        NextAttribute,\n    },\n    element::ElementType,\n};\nuse crate::{\n    html::{\n        attribute::{\n            maybe_next_attr_erasure_macros::next_attr_combine,\n            NamedAttributeKey,\n        },\n        element::HtmlElement,\n    },\n    prelude::Render,\n    view::add_attr::AddAnyAttr,\n};\nuse std::marker::PhantomData;\n\n/// Describes a container that can be used to hold a reference to an HTML element.\npub trait NodeRefContainer<E>: Send + Clone + 'static\nwhere\n    E: ElementType,\n{\n    /// Fills the container with the element.\n    fn load(self, el: &crate::renderer::types::Element);\n}\n\n/// An [`Attribute`] that will fill a [`NodeRefContainer`] with an HTML element.\n#[derive(Debug)]\npub struct NodeRefAttr<E, C> {\n    container: C,\n    ty: PhantomData<E>,\n}\n\nimpl<E, C> Clone for NodeRefAttr<E, C>\nwhere\n    C: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            container: self.container.clone(),\n            ty: PhantomData,\n        }\n    }\n}\n\n/// Creates an attribute that will fill a [`NodeRefContainer`] with the element it is applied to.\npub fn node_ref<E, C>(container: C) -> NodeRefAttr<E, C>\nwhere\n    E: ElementType,\n    C: NodeRefContainer<E>,\n{\n    NodeRefAttr {\n        container,\n        ty: PhantomData,\n    }\n}\n\nimpl<E, C> Attribute for NodeRefAttr<E, C>\nwhere\n    E: ElementType,\n    C: NodeRefContainer<E>,\n\n    crate::renderer::types::Element: PartialEq,\n{\n    const MIN_LENGTH: usize = 0;\n    type AsyncOutput = Self;\n    type State = crate::renderer::types::Element;\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    #[inline(always)]\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(\n        self,\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        self.container.load(el);\n        el.to_owned()\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        self.container.load(el);\n        el.to_owned()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.container.load(state);\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::Cloneable {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![]\n    }\n}\n\nimpl<E, C> NextAttribute for NodeRefAttr<E, C>\nwhere\n    E: ElementType,\n    C: NodeRefContainer<E>,\n\n    crate::renderer::types::Element: PartialEq,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\n/// Adds the `node_ref` attribute to an element.\npub trait NodeRefAttribute<E, C>\nwhere\n    E: ElementType,\n    C: NodeRefContainer<E>,\n\n    crate::renderer::types::Element: PartialEq,\n{\n    /// Binds this HTML element to a [`NodeRefContainer`].\n    fn node_ref(\n        self,\n        container: C,\n    ) -> <Self as AddAnyAttr>::Output<NodeRefAttr<E, C>>\n    where\n        Self: Sized + AddAnyAttr,\n        <Self as AddAnyAttr>::Output<NodeRefAttr<E, C>>: Render,\n    {\n        self.add_any_attr(node_ref(container))\n    }\n}\n\nimpl<E, At, Ch, C> NodeRefAttribute<E, C> for HtmlElement<E, At, Ch>\nwhere\n    E: ElementType,\n    At: Attribute,\n    Ch: Render,\n    C: NodeRefContainer<E>,\n\n    crate::renderer::types::Element: PartialEq,\n{\n}\n"
  },
  {
    "path": "tachys/src/html/property.rs",
    "content": "use super::attribute::{\n    maybe_next_attr_erasure_macros::next_attr_output_type, Attribute,\n    NextAttribute,\n};\nuse crate::{\n    html::attribute::{\n        maybe_next_attr_erasure_macros::next_attr_combine, NamedAttributeKey,\n    },\n    renderer::Rndr,\n    view::{Position, ToTemplate},\n};\nuse send_wrapper::SendWrapper;\nuse std::{borrow::Cow, sync::Arc};\nuse wasm_bindgen::JsValue;\n\n/// Creates an [`Attribute`] that will set a DOM property on an element.\n#[inline(always)]\npub fn prop<K, P>(key: K, value: P) -> Property<K, P>\nwhere\n    K: AsRef<str>,\n    P: IntoProperty,\n{\n    Property {\n        key,\n        value: (!cfg!(feature = \"ssr\")).then(|| SendWrapper::new(value)),\n    }\n}\n\n/// An [`Attribute`] that will set a DOM property on an element.\n#[derive(Debug)]\npub struct Property<K, P> {\n    key: K,\n    // property values will only be accessed in the browser\n    value: Option<SendWrapper<P>>,\n}\n\nimpl<K, P> Clone for Property<K, P>\nwhere\n    K: Clone,\n    P: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            key: self.key.clone(),\n            value: self.value.clone(),\n        }\n    }\n}\n\nimpl<K, P> Attribute for Property<K, P>\nwhere\n    K: AsRef<str> + Send,\n    P: IntoProperty,\n{\n    const MIN_LENGTH: usize = 0;\n\n    type AsyncOutput = Self;\n    type State = P::State;\n    type Cloneable = Property<Arc<str>, P::Cloneable>;\n    type CloneableOwned = Property<Arc<str>, P::CloneableOwned>;\n\n    #[inline(always)]\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(\n        self,\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        self.value\n            .expect(super::FEATURE_CONFLICT_DIAGNOSTIC)\n            .take()\n            .hydrate::<FROM_SERVER>(el, self.key.as_ref())\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        self.value\n            .expect(super::FEATURE_CONFLICT_DIAGNOSTIC)\n            .take()\n            .build(el, self.key.as_ref())\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.value\n            .expect(super::FEATURE_CONFLICT_DIAGNOSTIC)\n            .take()\n            .rebuild(state, self.key.as_ref())\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        Property {\n            key: self.key.as_ref().into(),\n            value: self\n                .value\n                .map(|value| SendWrapper::new(value.take().into_cloneable())),\n        }\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        Property {\n            key: self.key.as_ref().into(),\n            value: self.value.map(|value| {\n                SendWrapper::new(value.take().into_cloneable_owned())\n            }),\n        }\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![NamedAttributeKey::Property(\n            self.key.as_ref().to_string().into(),\n        )]\n    }\n}\n\nimpl<K, P> NextAttribute for Property<K, P>\nwhere\n    K: AsRef<str> + Send,\n    P: IntoProperty,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\nimpl<K, P> ToTemplate for Property<K, P>\nwhere\n    K: AsRef<str>,\n    P: IntoProperty,\n{\n    fn to_template(\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n    }\n}\n\n/// A possible value for a DOM property.\npub trait IntoProperty {\n    /// The view state retained between building and rebuilding.\n    type State;\n    /// An equivalent value that can be cloned.\n    type Cloneable: IntoProperty + Clone;\n    /// An equivalent value that can be cloned and is `'static`.\n    type CloneableOwned: IntoProperty + Clone + 'static;\n\n    /// Adds the property on an element created from HTML.\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State;\n\n    /// Adds the property during client-side rendering.\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State;\n\n    /// Updates the property with a new value.\n    fn rebuild(self, state: &mut Self::State, key: &str);\n\n    /// Converts this to a cloneable type.\n    fn into_cloneable(self) -> Self::Cloneable;\n\n    /// Converts this to a cloneable, owned type.\n    fn into_cloneable_owned(self) -> Self::CloneableOwned;\n}\n\nmacro_rules! prop_type {\n    ($prop_type:ty) => {\n        impl IntoProperty for $prop_type {\n            type State = (crate::renderer::types::Element, JsValue);\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                let value = self.into();\n                Rndr::set_property_or_value(el, key, &value);\n                (el.clone(), value)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                let value = self.into();\n                Rndr::set_property_or_value(el, key, &value);\n                (el.clone(), value)\n            }\n\n            fn rebuild(self, state: &mut Self::State, key: &str) {\n                let (el, prev) = state;\n                let value = self.into();\n                Rndr::set_property_or_value(el, key, &value);\n                *prev = value;\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n        }\n\n        impl IntoProperty for Option<$prop_type> {\n            type State = (crate::renderer::types::Element, JsValue);\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                let was_some = self.is_some();\n                let value = self.into();\n                if was_some {\n                    Rndr::set_property_or_value(el, key, &value);\n                }\n                (el.clone(), value)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                let was_some = self.is_some();\n                let value = self.into();\n                if was_some {\n                    Rndr::set_property_or_value(el, key, &value);\n                }\n                (el.clone(), value)\n            }\n\n            fn rebuild(self, state: &mut Self::State, key: &str) {\n                let (el, prev) = state;\n                let value = self.into();\n                Rndr::set_property_or_value(el, key, &value);\n                *prev = value;\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n        }\n    };\n}\n\nmacro_rules! prop_type_str {\n    ($prop_type:ty) => {\n        impl IntoProperty for $prop_type {\n            type State = (crate::renderer::types::Element, JsValue);\n            type Cloneable = Arc<str>;\n            type CloneableOwned = Arc<str>;\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                let value = JsValue::from(&*self);\n                Rndr::set_property_or_value(el, key, &value);\n                (el.clone(), value)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                let value = JsValue::from(&*self);\n                Rndr::set_property_or_value(el, key, &value);\n                (el.clone(), value)\n            }\n\n            fn rebuild(self, state: &mut Self::State, key: &str) {\n                let (el, prev) = state;\n                let value = JsValue::from(&*self);\n                Rndr::set_property_or_value(el, key, &value);\n                *prev = value;\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                let this: &str = &*self;\n                this.into()\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                let this: &str = &*self;\n                this.into()\n            }\n        }\n\n        impl IntoProperty for Option<$prop_type> {\n            type State = (crate::renderer::types::Element, JsValue);\n            type Cloneable = Option<Arc<str>>;\n            type CloneableOwned = Option<Arc<str>>;\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                let was_some = self.is_some();\n                let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));\n                if was_some {\n                    Rndr::set_property_or_value(el, key, &value);\n                }\n                (el.clone(), value)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                let was_some = self.is_some();\n                let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));\n                if was_some {\n                    Rndr::set_property_or_value(el, key, &value);\n                }\n                (el.clone(), value)\n            }\n\n            fn rebuild(self, state: &mut Self::State, key: &str) {\n                let (el, prev) = state;\n                let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));\n                Rndr::set_property_or_value(el, key, &value);\n                *prev = value;\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self.map(|n| {\n                    let this: &str = &*n;\n                    this.into()\n                })\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self.map(|n| {\n                    let this: &str = &*n;\n                    this.into()\n                })\n            }\n        }\n    };\n}\n\nimpl IntoProperty for Arc<str> {\n    type State = (crate::renderer::types::Element, JsValue);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let value = JsValue::from_str(self.as_ref());\n        Rndr::set_property_or_value(el, key, &value);\n        (el.clone(), value)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let value = JsValue::from_str(self.as_ref());\n        Rndr::set_property_or_value(el, key, &value);\n        (el.clone(), value)\n    }\n\n    fn rebuild(self, state: &mut Self::State, key: &str) {\n        let (el, prev) = state;\n        let value = JsValue::from_str(self.as_ref());\n        Rndr::set_property_or_value(el, key, &value);\n        *prev = value;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n}\n\nimpl IntoProperty for Option<Arc<str>> {\n    type State = (crate::renderer::types::Element, JsValue);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let was_some = self.is_some();\n        let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));\n        if was_some {\n            Rndr::set_property_or_value(el, key, &value);\n        }\n        (el.clone(), value)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let was_some = self.is_some();\n        let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));\n        if was_some {\n            Rndr::set_property_or_value(el, key, &value);\n        }\n        (el.clone(), value)\n    }\n\n    fn rebuild(self, state: &mut Self::State, key: &str) {\n        let (el, prev) = state;\n        let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));\n        Rndr::set_property_or_value(el, key, &value);\n        *prev = value;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n}\n\nprop_type!(JsValue);\nprop_type!(usize);\nprop_type!(u8);\nprop_type!(u16);\nprop_type!(u32);\nprop_type!(u64);\nprop_type!(u128);\nprop_type!(isize);\nprop_type!(i8);\nprop_type!(i16);\nprop_type!(i32);\nprop_type!(i64);\nprop_type!(i128);\nprop_type!(f32);\nprop_type!(f64);\nprop_type!(bool);\n\nprop_type_str!(String);\nprop_type_str!(&String);\nprop_type_str!(&str);\nprop_type_str!(Cow<'_, str>);\n"
  },
  {
    "path": "tachys/src/html/style.rs",
    "content": "use super::attribute::{\n    maybe_next_attr_erasure_macros::next_attr_output_type, Attribute,\n    NextAttribute,\n};\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\nuse crate::view::static_types::Static;\nuse crate::{\n    html::attribute::{\n        maybe_next_attr_erasure_macros::next_attr_combine, NamedAttributeKey,\n    },\n    renderer::{dom::CssStyleDeclaration, Rndr},\n    view::{Position, ToTemplate},\n};\nuse std::{future::Future, sync::Arc};\n\n/// Returns an [`Attribute`] that will add to an element's CSS styles.\n#[inline(always)]\npub fn style<S>(style: S) -> Style<S>\nwhere\n    S: IntoStyle,\n{\n    Style { style }\n}\n\n/// An [`Attribute`] that will add to an element's CSS styles.\n#[derive(Debug)]\npub struct Style<S> {\n    style: S,\n}\n\nimpl<S> Clone for Style<S>\nwhere\n    S: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            style: self.style.clone(),\n        }\n    }\n}\n\nimpl<S> Attribute for Style<S>\nwhere\n    S: IntoStyle,\n{\n    const MIN_LENGTH: usize = 0;\n\n    type AsyncOutput = Style<S::AsyncOutput>;\n    type State = S::State;\n    type Cloneable = Style<S::Cloneable>;\n    type CloneableOwned = Style<S::CloneableOwned>;\n\n    // TODO\n    #[inline(always)]\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(\n        self,\n        _buf: &mut String,\n        _style: &mut String,\n        style: &mut String,\n        _inner_html: &mut String,\n    ) {\n        self.style.to_html(style);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        self.style.hydrate::<FROM_SERVER>(el)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        self.style.build(el)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.style.rebuild(state)\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        Style {\n            style: self.style.into_cloneable(),\n        }\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        Style {\n            style: self.style.into_cloneable_owned(),\n        }\n    }\n\n    fn dry_resolve(&mut self) {\n        self.style.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        Style {\n            style: self.style.resolve().await,\n        }\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![NamedAttributeKey::Attribute(\"style\".into())]\n    }\n}\n\nimpl<S> NextAttribute for Style<S>\nwhere\n    S: IntoStyle,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\nimpl<S> ToTemplate for Style<S>\nwhere\n    S: IntoStyle,\n{\n    fn to_template(\n        _buf: &mut String,\n        _style: &mut String,\n        _class: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n        // TODO: should there be some templating for static styles?\n    }\n}\n\n/// Any type that can be added to the `style` attribute or set as a style in\n/// the [`CssStyleDeclaration`](web_sys::CssStyleDeclaration).\n///\n/// This could be a plain string, or a property name-value pair.\npub trait IntoStyle: Send {\n    /// The type after all async data have resolved.\n    type AsyncOutput: IntoStyle;\n    /// The view state retained between building and rebuilding.\n    type State;\n    /// An equivalent value that can be cloned.\n    type Cloneable: IntoStyle + Clone;\n    /// An equivalent value that can be cloned and is `'static`.\n    type CloneableOwned: IntoStyle + Clone + 'static;\n\n    /// Renders the style to HTML.\n    fn to_html(self, style: &mut String);\n\n    /// Adds interactivity as necessary, given DOM nodes that were created from HTML that has\n    /// either been rendered on the server, or cloned for a `<template>`.\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State;\n\n    /// Adds this style to the element during client-side rendering.\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State;\n\n    /// Updates the value.\n    fn rebuild(self, state: &mut Self::State);\n\n    /// Converts this to a cloneable type.\n    fn into_cloneable(self) -> Self::Cloneable;\n\n    /// Converts this to a cloneable, owned type.\n    fn into_cloneable_owned(self) -> Self::CloneableOwned;\n\n    /// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For\n    /// reactive types, this can be used to gather data about reactivity or about asynchronous data\n    /// that needs to be loaded.\n    fn dry_resolve(&mut self);\n\n    /// “Resolves” this into a type that is not waiting for any asynchronous data.\n    fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;\n\n    /// Reset the styling to the state before this style was added.\n    fn reset(state: &mut Self::State);\n}\n\nimpl<T: IntoStyle> IntoStyle for Option<T> {\n    type AsyncOutput = Option<T::AsyncOutput>;\n    type State = (crate::renderer::types::Element, Option<T::State>);\n    type Cloneable = Option<T::Cloneable>;\n    type CloneableOwned = Option<T::CloneableOwned>;\n\n    fn to_html(self, style: &mut String) {\n        if let Some(t) = self {\n            t.to_html(style);\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if let Some(t) = self {\n            (el.clone(), Some(t.hydrate::<FROM_SERVER>(el)))\n        } else {\n            (el.clone(), None)\n        }\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        if let Some(t) = self {\n            (el.clone(), Some(t.build(el)))\n        } else {\n            (el.clone(), None)\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let el = &state.0;\n        let prev_state = &mut state.1;\n        let maybe_next_t_state = match (prev_state.take(), self) {\n            (Some(mut prev_t_state), None) => {\n                T::reset(&mut prev_t_state);\n                Some(None)\n            }\n            (None, Some(t)) => Some(Some(t.build(el))),\n            (Some(mut prev_t_state), Some(t)) => {\n                t.rebuild(&mut prev_t_state);\n                Some(Some(prev_t_state))\n            }\n            (None, None) => Some(None),\n        };\n        if let Some(next_t_state) = maybe_next_t_state {\n            state.1 = next_t_state;\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.map(|t| t.into_cloneable())\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.map(|t| t.into_cloneable_owned())\n    }\n\n    fn dry_resolve(&mut self) {\n        if let Some(t) = self {\n            t.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        if let Some(t) = self {\n            Some(t.resolve().await)\n        } else {\n            None\n        }\n    }\n\n    fn reset(state: &mut Self::State) {\n        if let Some(prev_t_state) = &mut state.1 {\n            T::reset(prev_t_state);\n        }\n    }\n}\n\nimpl<'a> IntoStyle for &'a str {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, &'a str);\n    type Cloneable = Self;\n    type CloneableOwned = Arc<str>;\n\n    fn to_html(self, style: &mut String) {\n        style.push_str(self);\n        style.push(';');\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"style\", self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"style\", self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"style\");\n    }\n}\n\nimpl IntoStyle for Arc<str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Arc<str>);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn to_html(self, style: &mut String) {\n        style.push_str(&self);\n        style.push(';');\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"style\", &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"style\", &self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"style\");\n    }\n}\n\nimpl IntoStyle for String {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, String);\n    type Cloneable = Arc<str>;\n    type CloneableOwned = Arc<str>;\n\n    fn to_html(self, style: &mut String) {\n        style.push_str(&self);\n        style.push(';');\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"style\", &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"style\", &self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"style\");\n    }\n}\n\n/// Any type that can be used to set an individual style in the\n/// [`CssStyleDeclaration`](web_sys::CssStyleDeclaration).\n///\n/// This is the value in a `(name, value)` tuple that implements [`IntoStyle`].\npub trait IntoStyleValue: Send {\n    /// The type after all async data have resolved.\n    type AsyncOutput: IntoStyleValue;\n    /// The view state retained between building and rebuilding.\n    type State;\n    /// An equivalent value that can be cloned.\n    type Cloneable: Clone + IntoStyleValue;\n    /// An equivalent value that can be cloned and is `'static`.\n    type CloneableOwned: Clone + IntoStyleValue + 'static;\n\n    /// Renders the style to HTML.\n    fn to_html(self, name: &str, style: &mut String);\n\n    /// Adds this style to the element during client-side rendering.\n    fn build(self, style: &CssStyleDeclaration, name: &str) -> Self::State;\n\n    /// Updates the value.\n    fn rebuild(\n        self,\n        style: &CssStyleDeclaration,\n        name: &str,\n        state: &mut Self::State,\n    );\n\n    /// Adds interactivity as necessary, given DOM nodes that were created from HTML that has\n    /// either been rendered on the server, or cloned for a `<template>`.\n    fn hydrate(self, style: &CssStyleDeclaration, name: &str) -> Self::State;\n\n    /// Converts this to a cloneable type.\n    fn into_cloneable(self) -> Self::Cloneable;\n\n    /// Converts this to a cloneable, owned type.\n    fn into_cloneable_owned(self) -> Self::CloneableOwned;\n\n    /// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For\n    /// reactive types, this can be used to gather data about reactivity or about asynchronous data\n    /// that needs to be loaded.\n    fn dry_resolve(&mut self);\n\n    /// “Resolves” this into a type that is not waiting for any asynchronous data.\n    fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;\n}\n\nimpl<K, V> IntoStyle for (K, V)\nwhere\n    K: AsRef<str> + Clone + Send + 'static,\n    V: IntoStyleValue,\n{\n    type AsyncOutput = (K, V::AsyncOutput);\n    type State = (crate::renderer::types::CssStyleDeclaration, K, V::State);\n    type Cloneable = (K, V::Cloneable);\n    type CloneableOwned = (K, V::CloneableOwned);\n\n    fn to_html(self, style: &mut String) {\n        let (name, value) = self;\n        value.to_html(name.as_ref(), style);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let style = Rndr::style(el);\n        let state = self.1.hydrate(&style, self.0.as_ref());\n        (style, self.0, state)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        let (name, value) = self;\n        let style = Rndr::style(el);\n        let state = value.build(&style, name.as_ref());\n        (style, name, state)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (name, value) = self;\n        // state.1 was the previous name, theoretically the css name could be changed:\n        if name.as_ref() != state.1.as_ref() {\n            <Self as IntoStyle>::reset(state);\n            state.2 = value.build(&state.0, name.as_ref());\n        } else {\n            value.rebuild(&state.0, name.as_ref(), &mut state.2);\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        (self.0, self.1.into_cloneable())\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        (self.0, self.1.into_cloneable_owned())\n    }\n\n    fn dry_resolve(&mut self) {\n        self.1.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        (self.0, self.1.resolve().await)\n    }\n\n    /// Reset the renderer to the state before this style was added.\n    fn reset(state: &mut Self::State) {\n        let (style, name, _value) = state;\n        Rndr::remove_css_property(style, name.as_ref());\n    }\n}\n\nmacro_rules! impl_style_value {\n    ($ty:ty) => {\n        impl IntoStyleValue for $ty {\n            type AsyncOutput = Self;\n            type State = Self;\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn to_html(self, name: &str, style: &mut String) {\n                style.push_str(name);\n                style.push(':');\n                style.push_str(&self);\n                style.push(';');\n            }\n\n            fn build(\n                self,\n                style: &CssStyleDeclaration,\n                name: &str,\n            ) -> Self::State {\n                Rndr::set_css_property(style, name, &self);\n                self\n            }\n\n            fn rebuild(\n                self,\n                style: &CssStyleDeclaration,\n                name: &str,\n                state: &mut Self::State,\n            ) {\n                if &self != &*state {\n                    Rndr::set_css_property(style, name, &self);\n                }\n                *state = self;\n            }\n\n            fn hydrate(\n                self,\n                _style: &CssStyleDeclaration,\n                _name: &str,\n            ) -> Self::State {\n                self\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n        }\n\n        impl IntoStyleValue for Option<$ty> {\n            type AsyncOutput = Self;\n            type State = Self;\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn to_html(self, name: &str, style: &mut String) {\n                if let Some(value) = self {\n                    style.push_str(name);\n                    style.push(':');\n                    style.push_str(&value);\n                    style.push(';');\n                }\n            }\n\n            fn build(\n                self,\n                style: &CssStyleDeclaration,\n                name: &str,\n            ) -> Self::State {\n                if let Some(value) = &self {\n                    Rndr::set_css_property(style, name, &value);\n                }\n                self\n            }\n\n            fn rebuild(\n                self,\n                style: &CssStyleDeclaration,\n                name: &str,\n                state: &mut Self::State,\n            ) {\n                match (&state, &self) {\n                    (None, None) => {}\n                    (Some(_), None) => Rndr::remove_css_property(style, name),\n                    (None, Some(value)) => {\n                        Rndr::set_css_property(style, name, &value)\n                    }\n                    (Some(old), Some(new)) => {\n                        if new != &*old {\n                            Rndr::set_css_property(style, name, &new);\n                        }\n                    }\n                }\n                *state = self;\n            }\n\n            fn hydrate(\n                self,\n                _style: &CssStyleDeclaration,\n                _name: &str,\n            ) -> Self::State {\n                self\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n        }\n    };\n}\n\nimpl_style_value!(&'static str);\nimpl_style_value!(Arc<str>);\nimpl_style_value!(String);\n#[cfg(feature = \"oco\")]\nimpl_style_value!(oco_ref::Oco<'static, str>);\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\nimpl<const V: &'static str> IntoStyleValue for Static<V> {\n    type AsyncOutput = Self;\n    type State = Self;\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn to_html(self, name: &str, style: &mut String) {\n        style.push_str(name);\n        style.push(':');\n        style.push_str(V);\n        style.push(';');\n    }\n\n    fn build(self, style: &CssStyleDeclaration, name: &str) -> Self::State {\n        Rndr::set_css_property(style, name, V);\n        self\n    }\n\n    fn rebuild(\n        self,\n        _style: &CssStyleDeclaration,\n        _name: &str,\n        _state: &mut Self::State,\n    ) {\n    }\n\n    fn hydrate(self, _style: &CssStyleDeclaration, _name: &str) -> Self::State {\n        self\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\nimpl<const V: &'static str> IntoStyleValue for Option<Static<V>> {\n    type AsyncOutput = Self;\n    type State = Self;\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn to_html(self, name: &str, style: &mut String) {\n        if self.is_some() {\n            style.push_str(name);\n            style.push(':');\n            style.push_str(V);\n            style.push(';');\n        }\n    }\n\n    fn build(self, style: &CssStyleDeclaration, name: &str) -> Self::State {\n        if self.is_some() {\n            Rndr::set_css_property(style, name, V);\n        }\n        self\n    }\n\n    fn rebuild(\n        self,\n        style: &CssStyleDeclaration,\n        name: &str,\n        state: &mut Self::State,\n    ) {\n        match (&state, &self) {\n            (None, None) => {}\n            (Some(_), None) => Rndr::remove_css_property(style, name),\n            (None, Some(_)) => Rndr::set_css_property(style, name, V),\n            (Some(_), Some(_)) => {}\n        }\n        *state = self;\n    }\n\n    fn hydrate(self, _style: &CssStyleDeclaration, _name: &str) -> Self::State {\n        self\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\nimpl<const V: &'static str> IntoStyle for crate::view::static_types::Static<V> {\n    type AsyncOutput = Self;\n    type State = ();\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn to_html(self, style: &mut String) {\n        style.push_str(V);\n        style.push(';');\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _el: &crate::renderer::types::Element,\n    ) -> Self::State {\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"style\", V);\n    }\n\n    fn rebuild(self, _state: &mut Self::State) {}\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(_state: &mut Self::State) {}\n}\n\n/*\n#[cfg(test)]\nmod tests {\n    use crate::{\n        html::{\n            element::{p, HtmlElement},\n            style::style,\n        },\n        renderer::dom::Dom,\n        view::{Position, PositionState, RenderHtml},\n    };\n\n    #[test]\n    fn adds_simple_style() {\n        let mut html = String::new();\n        let el: HtmlElement<_, _, _, Dom> = p(style(\"display: block\"), ());\n        el.to_html(&mut html, &PositionState::new(Position::FirstChild));\n\n        assert_eq!(html, r#\"<p style=\"display: block;\"></p>\"#);\n    }\n\n    #[test]\n    fn mixes_plain_and_specific_styles() {\n        let mut html = String::new();\n        let el: HtmlElement<_, _, _, Dom> =\n            p((style(\"display: block\"), style((\"color\", \"blue\"))), ());\n        el.to_html(&mut html, &PositionState::new(Position::FirstChild));\n\n        assert_eq!(html, r#\"<p style=\"display: block;color:blue;\"></p>\"#);\n    }\n\n    #[test]\n    fn handles_dynamic_styles() {\n        let mut html = String::new();\n        let el: HtmlElement<_, _, _, Dom> = p(\n            (\n                style(\"display: block\"),\n                style((\"color\", \"blue\")),\n                style((\"font-weight\", || \"bold\".to_string())),\n            ),\n            (),\n        );\n        el.to_html(&mut html, &PositionState::new(Position::FirstChild));\n\n        assert_eq!(\n            html,\n            r#\"<p style=\"display: block;color:blue;font-weight:bold;\"></p>\"#\n        );\n    }\n}\n */\n"
  },
  {
    "path": "tachys/src/hydration.rs",
    "content": "use crate::{\n    renderer::{CastFrom, Rndr},\n    view::{Position, PositionState},\n};\n#[cfg(any(debug_assertions, leptos_debuginfo))]\nuse std::cell::Cell;\nuse std::{cell::RefCell, panic::Location, rc::Rc};\nuse web_sys::{Comment, Element, Node, Text};\n\n#[cfg(feature = \"mark_branches\")]\nconst COMMENT_NODE: u16 = 8;\n\n/// Hydration works by walking over the DOM, adding interactivity as needed.\n///\n/// This cursor tracks the location in the DOM that is currently being hydrated. Each that type\n/// implements [`RenderHtml`](crate::view::RenderHtml) knows how to advance the cursor to access\n/// the nodes it needs.\n#[derive(Debug)]\npub struct Cursor(Rc<RefCell<crate::renderer::types::Node>>);\n\nimpl Clone for Cursor {\n    fn clone(&self) -> Self {\n        Self(Rc::clone(&self.0))\n    }\n}\n\nimpl Cursor\nwhere\n    crate::renderer::types::Element: AsRef<crate::renderer::types::Node>,\n{\n    /// Creates a new cursor starting at the root element.\n    pub fn new(root: crate::renderer::types::Element) -> Self {\n        let root = <crate::renderer::types::Element as AsRef<\n            crate::renderer::types::Node,\n        >>::as_ref(&root)\n        .clone();\n        Self(Rc::new(RefCell::new(root)))\n    }\n\n    /// Returns the node at which the cursor is currently located.\n    pub fn current(&self) -> crate::renderer::types::Node {\n        self.0.borrow().clone()\n    }\n\n    /// Advances to the next child of the node at which the cursor is located.\n    ///\n    /// Does nothing if there is no child.\n    pub fn child(&self) {\n        let mut inner = self.0.borrow_mut();\n        if let Some(node) = Rndr::first_child(&inner) {\n            *inner = node;\n        }\n\n        #[cfg(feature = \"mark_branches\")]\n        {\n            while inner.node_type() == COMMENT_NODE {\n                if let Some(content) = inner.text_content() {\n                    if content.starts_with(\"bo\") || content.starts_with(\"bc\") {\n                        if let Some(sibling) = Rndr::next_sibling(&inner) {\n                            *inner = sibling;\n                            continue;\n                        }\n                    }\n                }\n\n                break;\n            }\n        }\n        // //drop(inner);\n        //crate::log(\">> which is \");\n        //Rndr::log_node(&self.current());\n    }\n\n    /// Advances to the next sibling of the node at which the cursor is located.\n    ///\n    /// Does nothing if there is no sibling.\n    pub fn sibling(&self) {\n        let mut inner = self.0.borrow_mut();\n        if let Some(node) = Rndr::next_sibling(&inner) {\n            *inner = node;\n        }\n\n        #[cfg(feature = \"mark_branches\")]\n        {\n            while inner.node_type() == COMMENT_NODE {\n                if let Some(content) = inner.text_content() {\n                    if content.starts_with(\"bo\") || content.starts_with(\"bc\") {\n                        if let Some(sibling) = Rndr::next_sibling(&inner) {\n                            *inner = sibling;\n                            continue;\n                        }\n                    }\n                }\n                break;\n            }\n        }\n        //drop(inner);\n        //crate::log(\">> which is \");\n        //Rndr::log_node(&self.current());\n    }\n\n    /// Moves to the parent of the node at which the cursor is located.\n    ///\n    /// Does nothing if there is no parent.\n    pub fn parent(&self) {\n        let mut inner = self.0.borrow_mut();\n        if let Some(node) = Rndr::get_parent(&inner) {\n            *inner = node;\n        }\n    }\n\n    /// Sets the cursor to some node.\n    pub fn set(&self, node: crate::renderer::types::Node) {\n        *self.0.borrow_mut() = node;\n    }\n\n    /// Advances to the next placeholder node and returns it\n    pub fn next_placeholder(\n        &self,\n        position: &PositionState,\n    ) -> crate::renderer::types::Placeholder {\n        //crate::dom::log(\"looking for placeholder after\");\n        //Rndr::log_node(&self.current());\n        self.advance_to_placeholder(position);\n        let marker = self.current();\n        crate::renderer::types::Placeholder::cast_from(marker.clone())\n            .unwrap_or_else(|| failed_to_cast_marker_node(marker))\n    }\n\n    /// Advances to the next placeholder node.\n    pub fn advance_to_placeholder(&self, position: &PositionState) {\n        if position.get() == Position::FirstChild {\n            self.child();\n        } else {\n            self.sibling();\n        }\n        position.set(Position::NextChild);\n    }\n}\n\n#[cfg(any(debug_assertions, leptos_debuginfo))]\nthread_local! {\n    static CURRENTLY_HYDRATING: Cell<Option<&'static Location<'static>>> = const { Cell::new(None) };\n}\n\npub(crate) fn set_currently_hydrating(\n    location: Option<&'static Location<'static>>,\n) {\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    {\n        CURRENTLY_HYDRATING.set(location);\n    }\n    #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n    {\n        _ = location;\n    }\n}\n\npub(crate) fn failed_to_cast_element(tag_name: &str, node: Node) -> Element {\n    #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n    {\n        _ = node;\n        unreachable!();\n    }\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    {\n        let hydrating = CURRENTLY_HYDRATING\n            .take()\n            .map(|n| n.to_string())\n            .unwrap_or_else(|| \"{unknown}\".to_string());\n        web_sys::console::error_3(\n            &wasm_bindgen::JsValue::from_str(&format!(\n                \"A hydration error occurred while trying to hydrate an \\\n                 element defined at {hydrating}.\\n\\nThe framework expected an \\\n                 HTML <{tag_name}> element, but found this instead: \",\n            )),\n            &node,\n            &wasm_bindgen::JsValue::from_str(\n                \"\\n\\nThe hydration mismatch may have occurred slightly \\\n                 earlier, but this is the first time the framework found a \\\n                 node of an unexpected type.\",\n            ),\n        );\n        panic!(\n            \"Unrecoverable hydration error. Please read the error message \\\n             directly above this for more details.\"\n        );\n    }\n}\n\npub(crate) fn failed_to_cast_marker_node(node: Node) -> Comment {\n    #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n    {\n        _ = node;\n        unreachable!();\n    }\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    {\n        let hydrating = CURRENTLY_HYDRATING\n            .take()\n            .map(|n| n.to_string())\n            .unwrap_or_else(|| \"{unknown}\".to_string());\n        web_sys::console::error_3(\n            &wasm_bindgen::JsValue::from_str(&format!(\n                \"A hydration error occurred while trying to hydrate an \\\n                 element defined at {hydrating}.\\n\\nThe framework expected a \\\n                 marker node, but found this instead: \",\n            )),\n            &node,\n            &wasm_bindgen::JsValue::from_str(\n                \"\\n\\nThe hydration mismatch may have occurred slightly \\\n                 earlier, but this is the first time the framework found a \\\n                 node of an unexpected type.\",\n            ),\n        );\n        panic!(\n            \"Unrecoverable hydration error. Please read the error message \\\n             directly above this for more details.\"\n        );\n    }\n}\n\npub(crate) fn failed_to_cast_text_node(node: Node) -> Text {\n    #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n    {\n        _ = node;\n        unreachable!();\n    }\n    #[cfg(any(debug_assertions, leptos_debuginfo))]\n    {\n        let hydrating = CURRENTLY_HYDRATING\n            .take()\n            .map(|n| n.to_string())\n            .unwrap_or_else(|| \"{unknown}\".to_string());\n        web_sys::console::error_3(\n            &wasm_bindgen::JsValue::from_str(&format!(\n                \"A hydration error occurred while trying to hydrate an \\\n                 element defined at {hydrating}.\\n\\nThe framework expected a \\\n                 text node, but found this instead: \",\n            )),\n            &node,\n            &wasm_bindgen::JsValue::from_str(\n                \"\\n\\nThe hydration mismatch may have occurred slightly \\\n                 earlier, but this is the first time the framework found a \\\n                 node of an unexpected type.\",\n            ),\n        );\n        panic!(\n            \"Unrecoverable hydration error. Please read the error message \\\n             directly above this for more details.\"\n        );\n    }\n}\n"
  },
  {
    "path": "tachys/src/lib.rs",
    "content": "//! Allows rendering user interfaces based on a statically-typed view tree.\n//!\n//! This view tree is generic over rendering backends, and agnostic about reactivity/change\n//! detection.\n\n// this is specifically used for `unsized_const_params` below\n// this allows us to use const generic &'static str for static text nodes and attributes\n#![allow(incomplete_features)]\n#![cfg_attr(\n    all(feature = \"nightly\", rustc_nightly),\n    feature(unsized_const_params)\n)]\n// support for const generic &'static str has now moved back and forth between\n// these two features a couple times; we'll just enable both\n#![cfg_attr(all(feature = \"nightly\", rustc_nightly), feature(adt_const_params))]\n#![deny(missing_docs)]\n\n/// Commonly-used traits.\npub mod prelude {\n    pub use crate::{\n        html::{\n            attribute::{\n                any_attribute::IntoAnyAttribute,\n                aria::AriaAttributes,\n                custom::CustomAttribute,\n                global::{\n                    ClassAttribute, GlobalAttributes, GlobalOnAttributes,\n                    OnAttribute, OnTargetAttribute, PropAttribute,\n                    StyleAttribute,\n                },\n                IntoAttributeValue,\n            },\n            directive::DirectiveAttribute,\n            element::{ElementChild, ElementExt, InnerHtmlAttribute},\n            node_ref::NodeRefAttribute,\n        },\n        renderer::{dom::Dom, Renderer},\n        view::{\n            add_attr::AddAnyAttr,\n            any_view::{AnyView, IntoAny, IntoMaybeErased},\n            IntoRender, Mountable, Render, RenderHtml,\n        },\n    };\n}\n\nuse wasm_bindgen::JsValue;\nuse web_sys::Node;\n\n/// Helpers for interacting with the DOM.\npub mod dom;\n/// Types for building a statically-typed HTML view tree.\npub mod html;\n/// Supports adding interactivity to HTML.\npub mod hydration;\n/// Types for MathML.\npub mod mathml;\n/// Defines various backends that can render views.\npub mod renderer;\n/// Rendering views to HTML.\npub mod ssr;\n/// Types for SVG.\npub mod svg;\n/// Core logic for manipulating views.\npub mod view;\n\npub use either_of as either;\n#[cfg(feature = \"islands\")]\n#[doc(hidden)]\npub use wasm_bindgen;\n#[cfg(feature = \"islands\")]\n#[doc(hidden)]\npub use web_sys;\n\n/// View implementations for the `oco_ref` crate (cheaply-cloned string types).\n#[cfg(feature = \"oco\")]\npub mod oco;\n/// View implementations for the `reactive_graph` crate.\n#[cfg(feature = \"reactive_graph\")]\npub mod reactive_graph;\n\n/// A type-erased container.\npub mod erased;\n\npub(crate) trait UnwrapOrDebug {\n    type Output;\n\n    fn or_debug(self, el: &Node, label: &'static str);\n\n    fn ok_or_debug(\n        self,\n        el: &Node,\n        label: &'static str,\n    ) -> Option<Self::Output>;\n}\n\nimpl<T> UnwrapOrDebug for Result<T, JsValue> {\n    type Output = T;\n\n    #[track_caller]\n    fn or_debug(self, el: &Node, name: &'static str) {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            if let Err(err) = self {\n                let location = std::panic::Location::caller();\n                web_sys::console::warn_3(\n                    &JsValue::from_str(&format!(\n                        \"[WARNING] Non-fatal error at {location}, while \\\n                         calling {name} on \"\n                    )),\n                    el,\n                    &err,\n                );\n            }\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            _ = self;\n        }\n    }\n\n    #[track_caller]\n    fn ok_or_debug(\n        self,\n        el: &Node,\n        name: &'static str,\n    ) -> Option<Self::Output> {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        {\n            if let Err(err) = &self {\n                let location = std::panic::Location::caller();\n                web_sys::console::warn_3(\n                    &JsValue::from_str(&format!(\n                        \"[WARNING] Non-fatal error at {location}, while \\\n                         calling {name} on \"\n                    )),\n                    el,\n                    err,\n                );\n            }\n            self.ok()\n        }\n        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]\n        {\n            self.ok()\n        }\n    }\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! or_debug {\n    ($action:expr, $el:expr, $label:literal) => {\n        if cfg!(any(debug_assertions, leptos_debuginfo)) {\n            $crate::UnwrapOrDebug::or_debug($action, $el, $label);\n        } else {\n            _ = $action;\n        }\n    };\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! ok_or_debug {\n    ($action:expr, $el:expr, $label:literal) => {\n        if cfg!(any(debug_assertions, leptos_debuginfo)) {\n            $crate::UnwrapOrDebug::ok_or_debug($action, $el, $label)\n        } else {\n            $action.ok()\n        }\n    };\n}\n"
  },
  {
    "path": "tachys/src/mathml/mod.rs",
    "content": "use crate::{\n    html::{\n        attribute::{Attr, Attribute, AttributeValue, NextAttribute},\n        element::{ElementType, ElementWithChildren, HtmlElement},\n    },\n    view::Render,\n};\nuse std::fmt::Debug;\n\nmacro_rules! mathml_global {\n\t($tag:ty, $attr:ty) => {\n\t\tpaste::paste! {\n            /// A MathML attribute.\n\t\t\tpub fn $attr<V>(self, value: V) -> HtmlElement <\n\t\t\t\t[<$tag:camel>],\n\t\t\t\t<At as NextAttribute>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>,\n\t\t\t\tCh\n\t\t\t>\n\t\t\twhere\n\t\t\t\tV: AttributeValue,\n\t\t\t\tAt: NextAttribute,\n\t\t\t\t<At as NextAttribute>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,\n\t\t\t{\n\t\t\t\tlet HtmlElement {\n                    #[cfg(any(debug_assertions, leptos_debuginfo))]\n                    defined_at,\n                    tag,\n                    children,\n                    attributes\n                } = self;\n\t\t\t\tHtmlElement {\n                    #[cfg(any(debug_assertions, leptos_debuginfo))]\n                    defined_at,\n\t\t\t\t\ttag,\n\t\t\t\t\tchildren,\n\t\t\t\t\tattributes: attributes.add_any_attr($crate::html::attribute::$attr(value)),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nmacro_rules! mathml_elements {\n\t($($tag:ident  [$($attr:ty),*]),* $(,)?) => {\n        paste::paste! {\n            $(\n                // `tag()` function\n                /// A MathML element.\n                #[track_caller]\n                pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>\n                where\n\n                {\n                    HtmlElement {\n                        #[cfg(any(debug_assertions, leptos_debuginfo))]\n                        defined_at: std::panic::Location::caller(),\n                        tag: [<$tag:camel>],\n                        attributes: (),\n                        children: (),\n                    }\n                }\n\n                /// A MathML element.\n                #[derive(Debug, Copy, Clone, PartialEq, Eq)]\n                pub struct [<$tag:camel>];\n\n\t\t\t\timpl<At, Ch> HtmlElement<[<$tag:camel>], At, Ch>\n\t\t\t\twhere\n\t\t\t\t\tAt: Attribute,\n\t\t\t\t\tCh: Render,\n\n\t\t\t\t{\n\t\t\t\t\tmathml_global!($tag, displaystyle);\n\t\t\t\t\tmathml_global!($tag, href);\n\t\t\t\t\tmathml_global!($tag, id);\n\t\t\t\t\tmathml_global!($tag, mathbackground);\n\t\t\t\t\tmathml_global!($tag, mathcolor);\n\t\t\t\t\tmathml_global!($tag, mathsize);\n\t\t\t\t\tmathml_global!($tag, mathvariant);\n\t\t\t\t\tmathml_global!($tag, scriptlevel);\n\n\t\t\t\t\t$(\n                        /// A MathML attribute.\n                        pub fn $attr<V>(self, value: V) -> HtmlElement <\n                            [<$tag:camel>],\n                            <At as NextAttribute>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>,\n                            Ch\n                        >\n                        where\n                            V: AttributeValue,\n                            At: NextAttribute,\n                            <At as NextAttribute>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,\n                        {\n                            let HtmlElement {\n                                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                                defined_at,\n                                tag,\n                                children,\n                                attributes\n                            } = self;\n                            HtmlElement {\n                                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                                defined_at,\n                                tag,\n                                children,\n                                attributes: attributes.add_any_attr($crate::html::attribute::$attr(value)),\n                            }\n                        }\n\t\t\t\t\t)*\n\t\t\t\t}\n\n                impl ElementType for [<$tag:camel>] {\n                    type Output = web_sys::Element;\n\n                    const TAG: &'static str = stringify!($tag);\n                    const SELF_CLOSING: bool = false;\n                    const ESCAPE_CHILDREN: bool = true;\n                    const NAMESPACE: Option<&'static str> = Some(\"http://www.w3.org/1998/Math/MathML\");\n\n                    #[inline(always)]\n                    fn tag(&self) -> &str {\n                        Self::TAG\n                    }\n                }\n\n                impl ElementWithChildren for [<$tag:camel>] {}\n            )*\n\t\t}\n    }\n}\n\nmathml_elements![\n    math [display, xmlns],\n    mi [],\n    mn [],\n    mo [\n        accent, fence, lspace, maxsize, minsize, movablelimits,\n        rspace, separator, stretchy, symmetric, form\n    ],\n    ms [],\n    mspace [height, width],\n    mtext [],\n    menclose [notation],\n    merror [],\n    mfenced [],\n    mfrac [linethickness],\n    mpadded [depth, height, voffset, width],\n    mphantom [],\n    mroot [],\n    mrow [],\n    msqrt [],\n    mstyle [],\n    mmultiscripts [],\n    mover [accent],\n    mprescripts [],\n    msub [],\n    msubsup [],\n    msup [],\n    munder [accentunder],\n    munderover [accent, accentunder],\n    mtable [\n        align, columnalign, columnlines, columnspacing, frame,\n        framespacing, rowalign, rowlines, rowspacing, width\n    ],\n    mtd [columnalign, columnspan, rowalign, rowspan],\n    mtr [columnalign, rowalign],\n    maction [],\n    annotation [],\n    semantics [],\n];\n"
  },
  {
    "path": "tachys/src/oco.rs",
    "content": "use crate::{\n    html::{\n        attribute::{any_attribute::AnyAttribute, AttributeValue},\n        class::IntoClass,\n        element::InnerHtmlValue,\n        property::IntoProperty,\n        style::IntoStyle,\n    },\n    hydration::Cursor,\n    no_attrs,\n    prelude::{Mountable, Render, RenderHtml},\n    renderer::Rndr,\n    view::{strings::StrState, Position, PositionState, ToTemplate},\n};\nuse oco_ref::Oco;\nuse wasm_bindgen::JsValue;\n\n/// Retained view state for [`Oco`].\npub struct OcoStrState {\n    node: crate::renderer::types::Text,\n    str: Oco<'static, str>,\n}\n\nimpl Render for Oco<'static, str> {\n    type State = OcoStrState;\n\n    fn build(self) -> Self::State {\n        let node = Rndr::create_text_node(&self);\n        OcoStrState { node, str: self }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let OcoStrState { node, str } = state;\n        if &self != str {\n            Rndr::set_text(node, &self);\n            *str = self;\n        }\n    }\n}\n\nno_attrs!(Oco<'static, str>);\n\nimpl RenderHtml for Oco<'static, str> {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        <&str as RenderHtml>::to_html_with_buf(\n            &self,\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let this: &str = self.as_ref();\n        let StrState { node, .. } = <&str as RenderHtml>::hydrate::<FROM_SERVER>(\n            this, cursor, position,\n        );\n        OcoStrState { node, str: self }\n    }\n\n    fn into_owned(self) -> <Self as RenderHtml>::Owned {\n        self\n    }\n}\n\nimpl ToTemplate for Oco<'static, str> {\n    const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;\n\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        <&str as ToTemplate>::to_template(\n            buf, class, style, inner_html, position,\n        )\n    }\n}\n\nimpl Mountable for OcoStrState {\n    fn unmount(&mut self) {\n        self.node.unmount()\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        Rndr::insert_node(parent, self.node.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.node.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\nimpl AttributeValue for Oco<'static, str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Oco<'static, str>);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        self.as_str().len()\n    }\n\n    fn to_html(self, key: &str, buf: &mut String) {\n        <&str as AttributeValue>::to_html(self.as_str(), key, buf);\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(\n            self.as_str(),\n            key,\n            el,\n        );\n        (el, self)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        Rndr::set_attribute(el, key, &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let (el, prev_value) = state;\n        if self != *prev_value {\n            Rndr::set_attribute(el, key, &self);\n        }\n        *prev_value = self;\n    }\n\n    fn into_cloneable(mut self) -> Self::Cloneable {\n        // ensure it's reference-counted\n        self.upgrade_inplace();\n        self\n    }\n\n    fn into_cloneable_owned(mut self) -> Self::CloneableOwned {\n        // ensure it's reference-counted\n        self.upgrade_inplace();\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl IntoClass for Oco<'static, str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        self.as_str().len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        IntoClass::to_html(self.as_str(), class);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_attribute(el, \"class\", &self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"class\", &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"class\", &self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(mut self) -> Self::Cloneable {\n        // ensure it's reference-counted\n        self.upgrade_inplace();\n        self\n    }\n\n    fn into_cloneable_owned(mut self) -> Self::CloneableOwned {\n        // ensure it's reference-counted\n        self.upgrade_inplace();\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"class\");\n    }\n}\n\nimpl IntoProperty for Oco<'static, str> {\n    type State = (crate::renderer::types::Element, JsValue);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let value = JsValue::from_str(self.as_ref());\n        Rndr::set_property_or_value(el, key, &value);\n        (el.clone(), value)\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let value = JsValue::from_str(self.as_ref());\n        Rndr::set_property_or_value(el, key, &value);\n        (el.clone(), value)\n    }\n\n    fn rebuild(self, state: &mut Self::State, key: &str) {\n        let (el, prev) = state;\n        let value = JsValue::from_str(self.as_ref());\n        Rndr::set_property_or_value(el, key, &value);\n        *prev = value;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n}\n\nimpl IntoStyle for Oco<'static, str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn to_html(self, style: &mut String) {\n        style.push_str(&self);\n        style.push(';');\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, \"style\", &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (el, prev) = state;\n        if self != *prev {\n            Rndr::set_attribute(el, \"style\", &self);\n        }\n        *prev = self;\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn reset(state: &mut Self::State) {\n        let (el, _prev) = state;\n        Rndr::remove_attribute(el, \"style\");\n    }\n}\n\nimpl InnerHtmlValue for Oco<'static, str> {\n    type AsyncOutput = Self;\n    type State = (crate::renderer::types::Element, Self);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, buf: &mut String) {\n        buf.push_str(&self);\n    }\n\n    fn to_template(_buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        if !FROM_SERVER {\n            Rndr::set_inner_html(el, &self);\n        }\n        (el.clone(), self)\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_inner_html(el, &self);\n        (el.clone(), self)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        if self != state.1 {\n            Rndr::set_inner_html(&state.0, &self);\n            state.1 = self;\n        }\n    }\n\n    fn into_cloneable(mut self) -> Self::Cloneable {\n        // ensure it's reference-counted\n        self.upgrade_inplace();\n        self\n    }\n\n    fn into_cloneable_owned(mut self) -> Self::CloneableOwned {\n        // ensure it's reference-counted\n        self.upgrade_inplace();\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n"
  },
  {
    "path": "tachys/src/reactive_graph/bind.rs",
    "content": "use crate::{\n    dom::{event_target_checked, event_target_value},\n    html::{\n        attribute::{\n            maybe_next_attr_erasure_macros::{\n                next_attr_combine, next_attr_output_type,\n            },\n            Attribute, AttributeKey, AttributeValue, NamedAttributeKey,\n            NextAttribute,\n        },\n        event::{change, input, on},\n        property::{prop, IntoProperty},\n    },\n    prelude::AddAnyAttr,\n    renderer::{types::Element, RemoveEventHandler},\n    view::{Position, ToTemplate},\n};\nuse reactive_graph::{\n    signal::{ReadSignal, RwSignal, WriteSignal},\n    traits::{Get, Set},\n    wrappers::read::Signal,\n};\nuse send_wrapper::SendWrapper;\nuse wasm_bindgen::JsValue;\n#[cfg(feature = \"reactive_stores\")]\nuse {\n    reactive_graph::owner::Storage,\n    reactive_stores::{\n        ArcField, AtIndex, AtKeyed, DerefedField, Field, KeyedSubfield,\n        StoreField, Subfield,\n    },\n    std::ops::{Deref, DerefMut, IndexMut},\n};\n\n/// `group` attribute used for radio inputs with `bind`.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\npub struct Group;\n\nimpl AttributeKey for Group {\n    const KEY: &'static str = \"group\";\n}\n\n/// Adds a two-way binding to the element, which adds an attribute and an event listener to the\n/// element when the element is created or hydrated.\npub trait BindAttribute<Key, Sig, T>\nwhere\n    Key: AttributeKey,\n    Sig: IntoSplitSignal<Value = T>,\n    T: FromEventTarget + AttributeValue + 'static,\n{\n    /// The type of the element with the two-way binding added.\n    type Output;\n\n    /// Adds a two-way binding to the element, which adds an attribute and an event listener to the\n    /// element when the element is created or hydrated.\n    ///\n    /// Example:\n    ///\n    /// ```ignore\n    /// // You can use `RwSignal`s\n    /// let is_awesome = RwSignal::new(true);\n    ///\n    /// // And you can use split signals\n    /// let (text, set_text) = signal(\"Hello world\".to_string());\n    ///\n    /// // Use `Checked` and a `bool` signal for a checkbox\n    /// checkbox_element.bind(Checked, is_awesome);\n    ///\n    /// // Use `Group` and `String` for radio inputs\n    /// radio_element.bind(Group, (text, set_text));\n    ///\n    /// // Use `Value` and `String` for everything else\n    /// input_element.bind(Value, (text, set_text));\n    /// ```\n    ///\n    /// Depending on the input different events are listened to.\n    /// - `<input type=\"checkbox\">`, `<input type=\"radio\">` and `<select>` use the `change` event;\n    /// - `<input>` with the rest of the types and `<textarea>` elements use the `input` event;\n    fn bind(self, key: Key, signal: Sig) -> Self::Output;\n}\n\nimpl<V, Key, Sig, T> BindAttribute<Key, Sig, T> for V\nwhere\n    V: AddAnyAttr,\n    Key: AttributeKey,\n    Sig: IntoSplitSignal<Value = T>,\n    T: FromEventTarget + AttributeValue + PartialEq + Sync + 'static,\n    Signal<BoolOrT<T>>: IntoProperty,\n    <Sig as IntoSplitSignal>::Read:\n        Get<Value = T> + Send + Sync + Clone + 'static,\n    <Sig as IntoSplitSignal>::Write: Send + Clone + 'static,\n    Element: GetValue<T>,\n{\n    type Output = <Self as AddAnyAttr>::Output<\n        Bind<\n            Key,\n            T,\n            <Sig as IntoSplitSignal>::Read,\n            <Sig as IntoSplitSignal>::Write,\n        >,\n    >;\n\n    fn bind(self, key: Key, signal: Sig) -> Self::Output {\n        self.add_any_attr(bind(key, signal))\n    }\n}\n\n/// Adds a two-way binding to the element, which adds an attribute and an event listener to the\n/// element when the element is created or hydrated.\n#[inline(always)]\npub fn bind<Key, Sig, T>(\n    key: Key,\n    signal: Sig,\n) -> Bind<Key, T, <Sig as IntoSplitSignal>::Read, <Sig as IntoSplitSignal>::Write>\nwhere\n    Key: AttributeKey,\n    Sig: IntoSplitSignal<Value = T>,\n    T: FromEventTarget + AttributeValue + 'static,\n    <Sig as IntoSplitSignal>::Read: Get<Value = T> + Clone + 'static,\n    <Sig as IntoSplitSignal>::Write: Send + Clone + 'static,\n{\n    let (read_signal, write_signal) = signal.into_split_signal();\n\n    Bind {\n        key,\n        read_signal,\n        write_signal,\n    }\n}\n\n/// Two-way binding of an attribute and an event listener\n#[derive(Debug)]\npub struct Bind<Key, T, R, W>\nwhere\n    Key: AttributeKey,\n    T: FromEventTarget + AttributeValue + 'static,\n    R: Get<Value = T> + Clone + 'static,\n    W: Set<Value = T>,\n{\n    key: Key,\n    read_signal: R,\n    write_signal: W,\n}\n\nimpl<Key, T, R, W> Clone for Bind<Key, T, R, W>\nwhere\n    Key: AttributeKey,\n    T: FromEventTarget + AttributeValue + 'static,\n    R: Get<Value = T> + Clone + 'static,\n    W: Set<Value = T> + Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            key: self.key.clone(),\n            read_signal: self.read_signal.clone(),\n            write_signal: self.write_signal.clone(),\n        }\n    }\n}\n\nimpl<Key, T, R, W> Bind<Key, T, R, W>\nwhere\n    Key: AttributeKey,\n    T: FromEventTarget + AttributeValue + PartialEq + Sync + 'static,\n    R: Get<Value = T> + Clone + Send + Sync + 'static,\n    W: Set<Value = T> + Clone + 'static,\n    Element: ChangeEvent + GetValue<T>,\n{\n    /// Attaches the event listener that updates the signal value to the element.\n    pub fn attach(self, el: &Element) -> RemoveEventHandler<Element> {\n        el.attach_change_event::<T, W>(Key::KEY, self.write_signal.clone())\n    }\n\n    /// Creates the signal to update the value of the attribute. This signal is different\n    /// when using a `\"group\"` attribute\n    pub fn read_signal(&self, el: &Element) -> Signal<BoolOrT<T>> {\n        let read_signal = self.read_signal.clone();\n\n        if Key::KEY == \"group\" {\n            let el = SendWrapper::new(el.clone());\n\n            Signal::derive(move || {\n                BoolOrT::Bool(el.get_value() == read_signal.get())\n            })\n        } else {\n            Signal::derive(move || BoolOrT::T(read_signal.get()))\n        }\n    }\n\n    /// Returns the key of the attribute. If the key is `\"group\"` it returns `\"checked\"`, otherwise\n    /// the one which was provided originally.\n    pub fn key(&self) -> &'static str {\n        if Key::KEY == \"group\" {\n            \"checked\"\n        } else {\n            Key::KEY\n        }\n    }\n}\n\nimpl<Key, T, R, W> Attribute for Bind<Key, T, R, W>\nwhere\n    Key: AttributeKey,\n    T: FromEventTarget + AttributeValue + PartialEq + Sync + 'static,\n    R: Get<Value = T> + Clone + Send + Sync + 'static,\n    Signal<BoolOrT<T>>: IntoProperty,\n    W: Set<Value = T> + Clone + Send + 'static,\n    Element: ChangeEvent + GetValue<T>,\n{\n    const MIN_LENGTH: usize = 0;\n\n    type State = (\n        <Signal<BoolOrT<T>> as IntoProperty>::State,\n        (Element, Option<RemoveEventHandler<Element>>),\n    );\n    type AsyncOutput = Self;\n    type Cloneable = Bind<Key, T, R, W>;\n    type CloneableOwned = Bind<Key, T, R, W>;\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(\n        self,\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n    }\n\n    #[inline(always)]\n    fn hydrate<const FROM_SERVER: bool>(self, el: &Element) -> Self::State {\n        let signal = self.read_signal(el);\n        let attr_state = prop(self.key(), signal).hydrate::<FROM_SERVER>(el);\n\n        let cleanup = self.attach(el);\n\n        (attr_state, (el.clone(), Some(cleanup)))\n    }\n\n    #[inline(always)]\n    fn build(self, el: &Element) -> Self::State {\n        let signal = self.read_signal(el);\n        let attr_state = prop(self.key(), signal).build(el);\n\n        let cleanup = self.attach(el);\n\n        (attr_state, (el.clone(), Some(cleanup)))\n    }\n\n    #[inline(always)]\n    fn rebuild(self, state: &mut Self::State) {\n        let (attr_state, (el, prev_cleanup)) = state;\n\n        let signal = self.read_signal(el);\n        prop(self.key(), signal).rebuild(attr_state);\n\n        if let Some(prev) = prev_cleanup.take() {\n            if let Some(remove) = prev.into_inner() {\n                remove();\n            }\n        }\n        *prev_cleanup = Some(self.attach(el));\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into_cloneable_owned()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![]\n    }\n}\n\nimpl<Key, T, R, W> NextAttribute for Bind<Key, T, R, W>\nwhere\n    Key: AttributeKey,\n    T: FromEventTarget + AttributeValue + PartialEq + Sync + 'static,\n    R: Get<Value = T> + Clone + Send + Sync + 'static,\n    Signal<BoolOrT<T>>: IntoProperty,\n    W: Set<Value = T> + Clone + Send + 'static,\n    Element: ChangeEvent + GetValue<T>,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(self, new_attr)\n    }\n}\n\nimpl<Key, T, R, W> ToTemplate for Bind<Key, T, R, W>\nwhere\n    Key: AttributeKey,\n    T: FromEventTarget + AttributeValue + 'static,\n    R: Get<Value = T> + Clone + 'static,\n    W: Set<Value = T> + Clone,\n{\n    #[inline(always)]\n    fn to_template(\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n    }\n}\n\n/// Splits a combined signal into its read and write parts.\n///\n/// This allows you to either provide a `RwSignal` or a tuple `(ReadSignal, WriteSignal)`.\npub trait IntoSplitSignal {\n    /// The actual contained value of the signal\n    type Value;\n    /// The read part of the signal\n    type Read: Get<Value = Self::Value>;\n    /// The write part of the signal\n    type Write: Set<Value = Self::Value>;\n    /// Splits a combined signal into its read and write parts.\n    fn into_split_signal(self) -> (Self::Read, Self::Write);\n}\n\nimpl<T> IntoSplitSignal for RwSignal<T>\nwhere\n    T: Send + Sync + 'static,\n    ReadSignal<T>: Get<Value = T>,\n{\n    type Value = T;\n    type Read = ReadSignal<T>;\n    type Write = WriteSignal<T>;\n\n    fn into_split_signal(self) -> (ReadSignal<T>, WriteSignal<T>) {\n        self.split()\n    }\n}\n\nimpl<T, R, W> IntoSplitSignal for (R, W)\nwhere\n    R: Get<Value = T>,\n    W: Set<Value = T>,\n{\n    type Value = T;\n    type Read = R;\n    type Write = W;\n\n    fn into_split_signal(self) -> (Self::Read, Self::Write) {\n        self\n    }\n}\n\n#[cfg(feature = \"reactive_stores\")]\nimpl<Inner, Prev, T> IntoSplitSignal for Subfield<Inner, Prev, T>\nwhere\n    Self: Get<Value = T> + Set<Value = T> + Clone,\n{\n    type Value = T;\n    type Read = Self;\n    type Write = Self;\n\n    fn into_split_signal(self) -> (Self::Read, Self::Write) {\n        (self.clone(), self.clone())\n    }\n}\n\n#[cfg(feature = \"reactive_stores\")]\nimpl<T, S> IntoSplitSignal for Field<T, S>\nwhere\n    Self: Get<Value = T> + Set<Value = T> + Clone,\n    S: Storage<ArcField<T>>,\n{\n    type Value = T;\n    type Read = Self;\n    type Write = Self;\n\n    fn into_split_signal(self) -> (Self::Read, Self::Write) {\n        (self, self)\n    }\n}\n\n#[cfg(feature = \"reactive_stores\")]\nimpl<Inner, Prev, K, T> IntoSplitSignal for KeyedSubfield<Inner, Prev, K, T>\nwhere\n    Self: Get<Value = T> + Set<Value = T> + Clone,\n    for<'a> &'a T: IntoIterator,\n{\n    type Value = T;\n    type Read = Self;\n    type Write = Self;\n\n    fn into_split_signal(self) -> (Self::Read, Self::Write) {\n        (self.clone(), self.clone())\n    }\n}\n\n#[cfg(feature = \"reactive_stores\")]\nimpl<Inner, Prev, K, T> IntoSplitSignal for AtKeyed<Inner, Prev, K, T>\nwhere\n    Self: Get<Value = T> + Set<Value = T> + Clone,\n    for<'a> &'a T: IntoIterator,\n{\n    type Value = T;\n    type Read = Self;\n    type Write = Self;\n\n    fn into_split_signal(self) -> (Self::Read, Self::Write) {\n        (self.clone(), self.clone())\n    }\n}\n\n#[cfg(feature = \"reactive_stores\")]\nimpl<Inner, Prev> IntoSplitSignal for AtIndex<Inner, Prev>\nwhere\n    Prev: Send + Sync + IndexMut<usize> + 'static,\n    Inner: Send + Sync + Clone + 'static,\n    Self: Get<Value = Prev::Output> + Set<Value = Prev::Output> + Clone,\n    Prev::Output: Sized,\n{\n    type Value = Prev::Output;\n    type Read = Self;\n    type Write = Self;\n\n    fn into_split_signal(self) -> (Self::Read, Self::Write) {\n        (self.clone(), self.clone())\n    }\n}\n\n#[cfg(feature = \"reactive_stores\")]\nimpl<S> IntoSplitSignal for DerefedField<S>\nwhere\n    Self: Get<Value = <S::Value as Deref>::Target>\n        + Set<Value = <S::Value as Deref>::Target>\n        + Clone,\n    S: Clone + StoreField + Send + Sync + 'static,\n    <S as StoreField>::Value: Deref + DerefMut,\n    <S::Value as Deref>::Target: Sized,\n{\n    type Value = <S::Value as Deref>::Target;\n    type Read = Self;\n    type Write = Self;\n\n    fn into_split_signal(self) -> (Self::Read, Self::Write) {\n        (self.clone(), self.clone())\n    }\n}\n\n/// Returns self from an event target.\npub trait FromEventTarget {\n    /// Returns self from an event target.\n    fn from_event_target(evt: &web_sys::Event) -> Self;\n}\n\nimpl FromEventTarget for bool {\n    fn from_event_target(evt: &web_sys::Event) -> Self {\n        event_target_checked(evt)\n    }\n}\n\nimpl FromEventTarget for String {\n    fn from_event_target(evt: &web_sys::Event) -> Self {\n        event_target_value(evt)\n    }\n}\n\n/// Attaches the appropriate change event listener to the element.\n/// - `<input>` with text types and `<textarea>` elements use the `input` event;\n/// - `<input type=\"checkbox\">`, `<input type=\"radio\">` and `<select>` use the `change` event;\npub trait ChangeEvent {\n    /// Attaches the appropriate change event listener to the element.\n    fn attach_change_event<T, W>(\n        &self,\n        key: &str,\n        write_signal: W,\n    ) -> RemoveEventHandler<Self>\n    where\n        T: FromEventTarget + AttributeValue + 'static,\n        W: Set<Value = T> + 'static,\n        Self: Sized;\n}\n\nimpl ChangeEvent for web_sys::Element {\n    fn attach_change_event<T, W>(\n        &self,\n        key: &str,\n        write_signal: W,\n    ) -> RemoveEventHandler<Self>\n    where\n        T: FromEventTarget + AttributeValue + 'static,\n        W: Set<Value = T> + 'static,\n    {\n        if key == \"group\" {\n            let handler = move |evt| {\n                let checked = event_target_checked(&evt);\n                if checked {\n                    write_signal.try_set(T::from_event_target(&evt));\n                }\n            };\n\n            on::<_, _>(change, handler).attach(self)\n        } else {\n            let handler = move |evt| {\n                write_signal.try_set(T::from_event_target(&evt));\n            };\n\n            if key == \"checked\" || self.tag_name() == \"SELECT\" {\n                on::<_, _>(change, handler).attach(self)\n            } else {\n                on::<_, _>(input, handler).attach(self)\n            }\n        }\n    }\n}\n\n/// Get the value attribute of an element (input).\n/// Reads `value` if `T` is `String` and `checked` if `T` is `bool`.\npub trait GetValue<T> {\n    /// Get the value attribute of an element (input).\n    fn get_value(&self) -> T;\n}\n\nimpl GetValue<String> for web_sys::Element {\n    fn get_value(&self) -> String {\n        self.get_attribute(\"value\").unwrap_or_default()\n    }\n}\n\nimpl GetValue<bool> for web_sys::Element {\n    fn get_value(&self) -> bool {\n        self.get_attribute(\"checked\").unwrap_or_default() == \"true\"\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\n/// Bool or a type. Needed to make the `group` attribute work. It is decided at runtime\n/// if the derived signal value is a bool or a type `T`.\npub enum BoolOrT<T> {\n    /// We have definitely a boolean value for the `group` attribute\n    Bool(bool),\n    /// Standard case with some type `T`\n    T(T),\n}\n\nimpl<T> IntoProperty for BoolOrT<T>\nwhere\n    T: IntoProperty<State = (Element, JsValue)>\n        + Into<JsValue>\n        + Clone\n        + 'static,\n{\n    type State = (Element, JsValue);\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &Element,\n        key: &str,\n    ) -> Self::State {\n        match self.clone() {\n            Self::T(s) => {\n                s.hydrate::<FROM_SERVER>(el, key);\n            }\n            Self::Bool(b) => {\n                <bool as IntoProperty>::hydrate::<FROM_SERVER>(b, el, key);\n            }\n        };\n\n        (el.clone(), self.into())\n    }\n\n    fn build(self, el: &Element, key: &str) -> Self::State {\n        match self.clone() {\n            Self::T(s) => {\n                s.build(el, key);\n            }\n            Self::Bool(b) => {\n                <bool as IntoProperty>::build(b, el, key);\n            }\n        }\n\n        (el.clone(), self.into())\n    }\n\n    fn rebuild(self, state: &mut Self::State, key: &str) {\n        let (el, prev) = state;\n\n        match self {\n            Self::T(s) => s.rebuild(&mut (el.clone(), prev.clone()), key),\n            Self::Bool(b) => <bool as IntoProperty>::rebuild(\n                b,\n                &mut (el.clone(), prev.clone()),\n                key,\n            ),\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n}\n\nimpl<T> From<BoolOrT<T>> for JsValue\nwhere\n    T: Into<JsValue>,\n{\n    fn from(value: BoolOrT<T>) -> Self {\n        match value {\n            BoolOrT::Bool(b) => b.into(),\n            BoolOrT::T(t) => t.into(),\n        }\n    }\n}\n"
  },
  {
    "path": "tachys/src/reactive_graph/class.rs",
    "content": "use super::{ReactiveFunction, SharedReactiveFunction};\nuse crate::{html::class::IntoClass, renderer::Rndr};\nuse reactive_graph::effect::RenderEffect;\nuse std::borrow::Borrow;\n\npub struct RenderEffectWithClassName<T>\nwhere\n    T: 'static,\n{\n    name: &'static str,\n    effect: RenderEffect<T>,\n}\n\nimpl<T> RenderEffectWithClassName<T>\nwhere\n    T: 'static,\n{\n    fn new(name: &'static str, effect: RenderEffect<T>) -> Self {\n        Self { effect, name }\n    }\n}\n\nimpl<F, C> IntoClass for F\nwhere\n    F: ReactiveFunction<Output = C>,\n    C: IntoClass + 'static,\n    C::State: 'static,\n{\n    type AsyncOutput = C::AsyncOutput;\n    type State = RenderEffect<C::State>;\n    type Cloneable = SharedReactiveFunction<C>;\n    type CloneableOwned = SharedReactiveFunction<C>;\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(mut self, class: &mut String) {\n        let value = self.invoke();\n        value.to_html(class);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        mut self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        // TODO FROM_SERVER vs template\n        let el = el.clone();\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state);\n                state\n            } else {\n                value.hydrate::<FROM_SERVER>(&el)\n            }\n        })\n    }\n\n    fn build(mut self, el: &crate::renderer::types::Element) -> Self::State {\n        let el = el.to_owned();\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state);\n                state\n            } else {\n                value.build(&el)\n            }\n        })\n    }\n\n    fn rebuild(mut self, state: &mut Self::State) {\n        let prev_value = state.take_value();\n        *state = RenderEffect::new_with_value(\n            move |prev| {\n                let value = self.invoke();\n                if let Some(mut state) = prev {\n                    value.rebuild(&mut state);\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            prev_value,\n        );\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into_shared()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into_shared()\n    }\n\n    fn dry_resolve(&mut self) {\n        self.invoke().dry_resolve();\n    }\n\n    async fn resolve(mut self) -> Self::AsyncOutput {\n        self.invoke().resolve().await\n    }\n\n    fn reset(state: &mut Self::State) {\n        *state = RenderEffect::new_with_value(\n            move |prev| {\n                if let Some(mut state) = prev {\n                    C::reset(&mut state);\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            state.take_value(),\n        );\n    }\n}\n\nimpl<F, T> IntoClass for (&'static str, F)\nwhere\n    F: ReactiveFunction<Output = T>,\n    T: Borrow<bool> + Send + 'static,\n{\n    type AsyncOutput = (&'static str, bool);\n    type State =\n        RenderEffectWithClassName<(crate::renderer::types::ClassList, bool)>;\n    type Cloneable = (&'static str, SharedReactiveFunction<T>);\n    type CloneableOwned = (&'static str, SharedReactiveFunction<T>);\n\n    fn html_len(&self) -> usize {\n        self.0.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        let (name, mut f) = self;\n        let include = *f.invoke().borrow();\n        if include {\n            <&str as IntoClass>::to_html(name, class);\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        // TODO FROM_SERVER vs template\n        let (name, mut f) = self;\n        let class_list = Rndr::class_list(el);\n        let name = Rndr::intern(name);\n\n        RenderEffectWithClassName::new(\n            name,\n            RenderEffect::new(\n                move |prev: Option<(\n                    crate::renderer::types::ClassList,\n                    bool,\n                )>| {\n                    let include = *f.invoke().borrow();\n                    if let Some((class_list, prev)) = prev {\n                        if include {\n                            if !prev {\n                                Rndr::add_class(&class_list, name);\n                            }\n                        } else if prev {\n                            Rndr::remove_class(&class_list, name);\n                        }\n                    }\n                    (class_list.clone(), include)\n                },\n            ),\n        )\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        let (name, mut f) = self;\n        let class_list = Rndr::class_list(el);\n        let name = Rndr::intern(name);\n\n        RenderEffectWithClassName::new(\n            name,\n            RenderEffect::new(\n                move |prev: Option<(\n                    crate::renderer::types::ClassList,\n                    bool,\n                )>| {\n                    let include = *f.invoke().borrow();\n                    match prev {\n                        Some((class_list, prev)) => {\n                            if include {\n                                if !prev {\n                                    Rndr::add_class(&class_list, name);\n                                }\n                            } else if prev {\n                                Rndr::remove_class(&class_list, name);\n                            }\n                        }\n                        None => {\n                            if include {\n                                Rndr::add_class(&class_list, name);\n                            }\n                        }\n                    }\n                    (class_list.clone(), include)\n                },\n            ),\n        )\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (name, mut f) = self;\n\n        let prev_name = state.name;\n        let prev_state = state.effect.take_value();\n        if let Some((list, prev_include)) = &prev_state {\n            if prev_name != name && *prev_include {\n                Rndr::remove_class(list, prev_name);\n            }\n        }\n\n        // Name might've updated:\n        state.name = name;\n        let mut first_run = true;\n        state.effect = RenderEffect::new_with_value(\n            move |prev| {\n                let include = *f.invoke().borrow();\n                match prev {\n                    Some((class_list, prev)) => {\n                        if include {\n                            if !prev || first_run {\n                                Rndr::add_class(&class_list, name);\n                            }\n                        } else if prev {\n                            Rndr::remove_class(&class_list, name);\n                        }\n                        first_run = false;\n                        (class_list.clone(), include)\n                    }\n                    None => {\n                        unreachable!()\n                    }\n                }\n            },\n            prev_state,\n        );\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        (self.0, self.1.into_shared())\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        (self.0, self.1.into_shared())\n    }\n\n    fn dry_resolve(&mut self) {\n        self.1.invoke();\n    }\n\n    async fn resolve(mut self) -> Self::AsyncOutput {\n        (self.0, *self.1.invoke().borrow())\n    }\n\n    fn reset(state: &mut Self::State) {\n        let name = state.name;\n        state.effect = RenderEffect::new_with_value(\n            move |prev| {\n                if let Some(mut state) = prev {\n                    let (class_list, prev) = &mut state;\n                    Rndr::remove_class(class_list, name);\n                    *prev = false;\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            state.effect.take_value(),\n        );\n    }\n}\n\n// TODO this needs a non-reactive form too to be restored\n/*\nimpl<F, T> IntoClass for (Vec<Cow<'static, str>>, F)\nwhere\n    F: ReactiveFunction<Output = T>,\n    T: Borrow<bool> + Send + 'static,\n\n{\n    type AsyncOutput = (Vec<Cow<'static, str>>, bool);\n    type State = RenderEffect<(crate::renderer::types::ClassList, bool)>;\n    type Cloneable = (Vec<Cow<'static, str>>, SharedReactiveFunction<T>);\n    type CloneableOwned = (Vec<Cow<'static, str>>, SharedReactiveFunction<T>);\n\n    fn html_len(&self) -> usize {\n        self.0.iter().map(|n| n.len()).sum()\n    }\n\n    fn to_html(self, class: &mut String) {\n        let (names, mut f) = self;\n        let include = *f.invoke().borrow();\n        if include {\n            for name in names {\n                <&str as IntoClass>::to_html(&name, class);\n            }\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {\n        // TODO FROM_SERVER vs template\n        let (names, mut f) = self;\n        let class_list = Rndr::class_list(el);\n\n        RenderEffect::new(move |prev: Option<(crate::renderer::types::ClassList, bool)>| {\n            let include = *f.invoke().borrow();\n            if let Some((class_list, prev)) = prev {\n                if include {\n                    if !prev {\n                        for name in &names {\n                            // TODO multi-class optimizations here\n                            Rndr::add_class(&class_list, name);\n                        }\n                    }\n                } else if prev {\n                    for name in &names {\n                        Rndr::remove_class(&class_list, name);\n                    }\n                }\n            }\n            (class_list.clone(), include)\n        })\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        let (names, mut f) = self;\n        let class_list = Rndr::class_list(el);\n\n        RenderEffect::new(move |prev: Option<(crate::renderer::types::ClassList, bool)>| {\n            let include = *f.invoke().borrow();\n            match prev {\n                Some((class_list, prev)) => {\n                    if include {\n                        for name in &names {\n                            if !prev {\n                                Rndr::add_class(&class_list, name);\n                            }\n                        }\n                    } else if prev {\n                        for name in &names {\n                            Rndr::remove_class(&class_list, name);\n                        }\n                    }\n                }\n                None => {\n                    if include {\n                        for name in &names {\n                            Rndr::add_class(&class_list, name);\n                        }\n                    }\n                }\n            }\n            (class_list.clone(), include)\n        })\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let (names, mut f) = self;\n        let prev_value = state.take_value();\n\n        *state = RenderEffect::new_with_value(\n            move |prev: Option<(crate::renderer::types::ClassList, bool)>| {\n                let include = *f.invoke().borrow();\n                match prev {\n                    Some((class_list, prev)) => {\n                        if include {\n                            for name in &names {\n                                if !prev {\n                                    Rndr::add_class(&class_list, name);\n                                }\n                            }\n                        } else if prev {\n                            for name in &names {\n                                Rndr::remove_class(&class_list, name);\n                            }\n                        }\n                        (class_list.clone(), include)\n                    }\n                    None => {\n                        unreachable!()\n                    }\n                }\n            },\n            prev_value,\n        );\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        (self.0.clone(), self.1.into_shared())\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        (self.0.clone(), self.1.into_shared())\n    }\n\n    fn dry_resolve(&mut self) {\n        self.1.invoke();\n    }\n\n    async fn resolve(mut self) -> Self::AsyncOutput {\n        (self.0, *self.1.invoke().borrow())\n    }\n}\n*/\n\n/*\nimpl<G> IntoClass for ReadGuard<String, G>\nwhere\n    G: Deref<Target = String> + Send,\n{\n    type AsyncOutput = Self;\n    type State = <String as IntoClass>::State;\n    type Cloneable = Arc<str>;\n    type CloneableOwned = Arc<str>;\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        <&str as IntoClass>::to_html(self.deref().as_str(), class);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        <String as IntoClass>::hydrate::<FROM_SERVER>(\n            self.deref().to_owned(),\n            el,\n        )\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        <String as IntoClass>::build(self.deref().to_owned(), el)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        <String as IntoClass>::rebuild(self.deref().to_owned(), state)\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.as_str().into()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.as_str().into()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl<G> IntoClass for (&'static str, ReadGuard<bool, G>)\nwhere\n    G: Deref<Target = bool> + Send,\n{\n    type AsyncOutput = Self;\n    type State = <(&'static str, bool) as IntoClass>::State;\n    type Cloneable = (&'static str, bool);\n    type CloneableOwned = (&'static str, bool);\n\n    fn html_len(&self) -> usize {\n        self.0.len()\n    }\n\n    fn to_html(self, class: &mut String) {\n        <(&'static str, bool) as IntoClass>::to_html(\n            (self.0, *self.1.deref()),\n            class,\n        );\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        <(&'static str, bool) as IntoClass>::hydrate::<FROM_SERVER>(\n            (self.0, *self.1.deref()),\n            el,\n        )\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        <(&'static str, bool) as IntoClass>::build(\n            (self.0, *self.1.deref()),\n            el,\n        )\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        <(&'static str, bool) as IntoClass>::rebuild(\n            (self.0, *self.1.deref()),\n            state,\n        )\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        (self.0, *self.1)\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        (self.0, *self.1)\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n*/\n\nmacro_rules!  tuple_class_reactive {\n    ($name:ident, <$($impl_gen:ident),*>, <$($gen:ident),*> , $( $where_clause:tt )*) =>\n    {\n        #[allow(deprecated)]\n        impl<$($impl_gen),*>  IntoClass for (&'static str, $name<$($gen),*>)\n        where\n            $($where_clause)*\n        {\n            type AsyncOutput = Self;\n            type State = RenderEffectWithClassName<(\n                crate::renderer::types::ClassList,\n                bool,\n            )>;\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn html_len(&self) -> usize {\n                self.0.len()\n            }\n\n            fn to_html(self, class: &mut String) {\n                let (name, f) = self;\n                let include = f.get();\n                if include {\n                    <&str as IntoClass>::to_html(name, class);\n                }\n            }\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                IntoClass::hydrate::<FROM_SERVER>(\n                    (self.0, move || self.1.get()),\n                    el,\n                )\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                IntoClass::build((self.0, move || self.1.get()), el)\n            }\n\n            fn rebuild(self, state: &mut Self::State) {\n                IntoClass::rebuild((self.0, move || self.1.get()), state)\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n\n            fn reset(state: &mut Self::State) {\n                let name = state.name;\n                *state = RenderEffectWithClassName::new(\n                    state.name,\n                    RenderEffect::new_with_value(\n                        move |prev| {\n                            if let Some(mut state) = prev {\n                                let (class_list, prev) = &mut state;\n                                Rndr::remove_class(class_list, name);\n                                *prev = false;\n                                state\n                            } else {\n                                unreachable!()\n                            }\n                        },\n                        state.effect.take_value(),\n                    ),\n                );\n            }\n        }\n    };\n}\n\nmacro_rules!  class_reactive {\n    ($name:ident, <$($gen:ident),*>, $v:ty, $( $where_clause:tt )*) =>\n    {\n        #[allow(deprecated)]\n        impl<$($gen),*> IntoClass for $name<$($gen),*>\n        where\n            $v: IntoClass + Clone + Send + Sync + 'static,\n            <$v as IntoClass>::State: 'static,\n            $($where_clause)*\n        {\n            type AsyncOutput = Self;\n            type State = RenderEffect<<$v as IntoClass>::State>;\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn html_len(&self) -> usize {\n                0\n            }\n\n            fn to_html(self, class: &mut String) {\n                let value = self.get();\n                value.to_html(class);\n            }\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                (move || self.get()).hydrate::<FROM_SERVER>(el)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                (move || self.get()).build(el)\n            }\n\n            fn rebuild(self, state: &mut Self::State) {\n                (move || self.get()).rebuild(state)\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n\n            fn reset(state: &mut Self::State) {\n                *state = RenderEffect::new_with_value(\n                    move |prev| {\n                        if let Some(mut state) = prev {\n                            <$v>::reset(&mut state);\n                            state\n                        } else {\n                            unreachable!()\n                        }\n                    },\n                    state.take_value(),\n                );\n            }\n        }\n    };\n}\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\nmod stable {\n    use super::{RenderEffect, RenderEffectWithClassName};\n    use crate::{html::class::IntoClass, renderer::Rndr};\n    #[allow(deprecated)]\n    use reactive_graph::wrappers::read::MaybeSignal;\n    use reactive_graph::{\n        computed::{ArcMemo, Memo},\n        owner::Storage,\n        signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},\n        traits::Get,\n        wrappers::read::{ArcSignal, Signal},\n    };\n    class_reactive!(\n        RwSignal,\n        <V, S>,\n        V,\n        RwSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    class_reactive!(\n        ReadSignal,\n        <V, S>,\n        V,\n        ReadSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    class_reactive!(\n        Memo,\n        <V, S>,\n        V,\n        Memo<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    class_reactive!(\n        Signal,\n        <V, S>,\n        V,\n        Signal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    class_reactive!(\n        MaybeSignal,\n        <V, S>,\n        V,\n        MaybeSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    class_reactive!(ArcRwSignal, <V>, V, ArcRwSignal<V>: Get<Value = V>);\n    class_reactive!(ArcReadSignal, <V>, V, ArcReadSignal<V>: Get<Value = V>);\n    class_reactive!(ArcMemo, <V>, V, ArcMemo<V>: Get<Value = V>);\n    class_reactive!(ArcSignal, <V>, V, ArcSignal<V>: Get<Value = V>);\n\n    tuple_class_reactive!(\n        RwSignal,\n        <S>,\n        <bool, S>,\n        RwSignal<bool, S>: Get<Value = bool>,\n        S: Storage<bool>,\n        S: Send  + 'static,\n    );\n    tuple_class_reactive!(\n        ReadSignal,\n        <S>,\n        <bool, S>,\n        ReadSignal<bool, S>: Get<Value = bool>,\n        S: Storage<bool>,\n        S: Send + 'static,\n    );\n    tuple_class_reactive!(\n        Memo,\n        <S>,\n        <bool, S>,\n        Memo<bool, S>: Get<Value = bool>,\n        S: Storage<bool>,\n        S: Send + 'static,\n    );\n    tuple_class_reactive!(\n        Signal,\n        <S>,\n        <bool, S>,\n        Signal<bool, S>: Get<Value = bool>,\n        S: Storage<bool>,\n        S: Send + 'static,\n    );\n    tuple_class_reactive!(\n        MaybeSignal,\n        <S>,\n        <bool, S>,\n        MaybeSignal<bool, S>: Get<Value = bool>,\n        S: Storage<bool>,\n        S: Send + 'static,\n    );\n    tuple_class_reactive!(ArcRwSignal,<>, <bool>, ArcRwSignal<bool>: Get<Value = bool>);\n    tuple_class_reactive!(ArcReadSignal,<>, <bool>, ArcReadSignal<bool>: Get<Value = bool>);\n    tuple_class_reactive!(ArcMemo,<>, <bool>, ArcMemo<bool>: Get<Value = bool>);\n    tuple_class_reactive!(ArcSignal,<>, <bool>, ArcSignal<bool>: Get<Value = bool>);\n}\n\n#[cfg(feature = \"reactive_stores\")]\nmod reactive_stores {\n    use super::{RenderEffect, RenderEffectWithClassName};\n    use crate::{html::class::IntoClass, renderer::Rndr};\n    #[allow(deprecated)]\n    use reactive_graph::{owner::Storage, traits::Get};\n    use reactive_stores::{\n        ArcField, ArcStore, AtIndex, AtKeyed, DerefedField, Field,\n        KeyedSubfield, Store, StoreField, Subfield,\n    };\n    use std::ops::{Deref, DerefMut, Index, IndexMut};\n\n    class_reactive!(\n        Subfield,\n        <Inner, Prev, V>,\n        V,\n        Subfield<Inner, Prev, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n\n    class_reactive!(\n        AtKeyed,\n        <Inner, Prev, K, V>,\n        V,\n        AtKeyed<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    class_reactive!(\n        KeyedSubfield,\n        <Inner, Prev, K, V>,\n        V,\n        KeyedSubfield<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    class_reactive!(\n        DerefedField,\n        <S>,\n        <S::Value as Deref>::Target,\n        S: Clone + StoreField + Send + Sync + 'static,\n        <S as StoreField>::Value: Deref + DerefMut\n    );\n\n    class_reactive!(\n        AtIndex,\n        <Inner, Prev>,\n        <Prev as Index<usize>>::Output,\n        AtIndex<Inner, Prev>: Get<Value = Prev::Output>,\n        Prev: Send + Sync + IndexMut<usize> + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n    class_reactive!(\n        Store,\n        <V, S>,\n        V,\n        Store<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    class_reactive!(\n        Field,\n        <V, S>,\n        V,\n        Field<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    class_reactive!(ArcStore, <V>, V, ArcStore<V>: Get<Value = V>);\n    class_reactive!(ArcField, <V>, V, ArcField<V>: Get<Value = V>);\n\n    tuple_class_reactive!(\n        Subfield,\n        <Inner, Prev>,\n        <Inner, Prev, bool>,\n        Subfield<Inner, Prev, bool>: Get<Value = bool>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n\n    tuple_class_reactive!(\n        AtKeyed,\n        <Inner, Prev, K>,\n        <Inner, Prev, K, bool>,\n        AtKeyed<Inner, Prev, K, bool>: Get<Value = bool>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a bool: IntoIterator,\n    );\n\n    tuple_class_reactive!(\n        KeyedSubfield,\n        <Inner, Prev, K>,\n        <Inner, Prev, K, bool>,\n        KeyedSubfield<Inner, Prev, K, bool>: Get<Value = bool>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a bool: IntoIterator,\n    );\n\n    tuple_class_reactive!(\n        DerefedField,\n        <S>,\n        <S>,\n        S: Clone + StoreField + Send + Sync + 'static,\n        <S as StoreField>::Value: Deref<Target = bool> + DerefMut\n    );\n\n    tuple_class_reactive!(\n        AtIndex,\n        <Inner, Prev>,\n        <Inner, Prev>,\n        AtIndex<Inner, Prev>: Get<Value = Prev::Output>,\n        Prev: Send + Sync + IndexMut<usize,Output = bool> + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n    tuple_class_reactive!(\n        Store,\n        <S>,\n        <bool, S>,\n        Store<bool, S>: Get<Value = bool>,\n        S: Storage<bool>,\n        S: Send  + 'static,\n    );\n    tuple_class_reactive!(\n        Field,\n        <S>,\n        <bool, S>,\n        Field<bool, S>: Get<Value = bool>,\n        S: Storage<bool>,\n        S: Send  + 'static,\n    );\n    tuple_class_reactive!(ArcStore,<>, <bool>, ArcStore<bool>: Get<Value = bool>);\n    tuple_class_reactive!(ArcField,<>, <bool>, ArcField<bool>: Get<Value = bool>);\n}\n\n/*\nimpl<Fut> IntoClass for Suspend<Fut>\nwhere\n    Fut: Clone + Future + Send + 'static,\n    Fut::Output: IntoClass,\n{\n    type AsyncOutput = Fut::Output;\n    type State = Rc<RefCell<Option<<Fut::Output as IntoClass>::State>>>;\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(self, style: &mut String) {\n        if let Some(inner) = self.inner.now_or_never() {\n            inner.to_html(style);\n        } else {\n            panic!(\"You cannot use Suspend on an attribute outside Suspense\");\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let el = el.to_owned();\n        let state = Rc::new(RefCell::new(None));\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(&state);\n            async move {\n                *state.borrow_mut() =\n                    Some(self.inner.await.hydrate::<FROM_SERVER>(&el));\n                self.subscriber.forward();\n            }\n        });\n        state\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        let el = el.to_owned();\n        let state = Rc::new(RefCell::new(None));\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(&state);\n            async move {\n                *state.borrow_mut() = Some(self.inner.await.build(&el));\n                self.subscriber.forward();\n            }\n        });\n        state\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(state);\n            async move {\n                let value = self.inner.await;\n                let mut state = state.borrow_mut();\n                if let Some(state) = state.as_mut() {\n                    value.rebuild(state);\n                }\n                self.subscriber.forward();\n            }\n        });\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self.inner.await\n    }\n}\n*/\n"
  },
  {
    "path": "tachys/src/reactive_graph/inner_html.rs",
    "content": "use super::{ReactiveFunction, SharedReactiveFunction};\nuse crate::html::element::InnerHtmlValue;\nuse reactive_graph::effect::RenderEffect;\n\nimpl<F, V> InnerHtmlValue for F\nwhere\n    F: ReactiveFunction<Output = V>,\n    V: InnerHtmlValue + 'static,\n    V::State: 'static,\n{\n    type AsyncOutput = V::AsyncOutput;\n    type State = RenderEffect<V::State>;\n    type Cloneable = SharedReactiveFunction<V>;\n    type CloneableOwned = SharedReactiveFunction<V>;\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(mut self, buf: &mut String) {\n        let value = self.invoke();\n        value.to_html(buf);\n    }\n\n    fn to_template(_buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        mut self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let el = el.to_owned();\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state);\n                state\n            } else {\n                value.hydrate::<FROM_SERVER>(&el)\n            }\n        })\n    }\n\n    fn build(mut self, el: &crate::renderer::types::Element) -> Self::State {\n        let el = el.to_owned();\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state);\n                state\n            } else {\n                value.build(&el)\n            }\n        })\n    }\n\n    fn rebuild(mut self, state: &mut Self::State) {\n        let prev_value = state.take_value();\n        *state = RenderEffect::new_with_value(\n            move |prev| {\n                let value = self.invoke();\n                if let Some(mut state) = prev {\n                    value.rebuild(&mut state);\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            prev_value,\n        );\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into_shared()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into_shared()\n    }\n\n    fn dry_resolve(&mut self) {\n        self.invoke();\n    }\n\n    async fn resolve(mut self) -> Self::AsyncOutput {\n        self.invoke().resolve().await\n    }\n}\n\nmacro_rules! inner_html_reactive {\n    ($name:ident, <$($gen:ident),*>, $v:ty, $( $where_clause:tt )*) =>\n    {\n        #[allow(deprecated)]\n        impl<$($gen),*> InnerHtmlValue for $name<$($gen),*>\n        where\n            $v: InnerHtmlValue + Clone + Send + Sync + 'static,\n            <$v as InnerHtmlValue>::State: 'static,\n            $($where_clause)*\n        {\n            type AsyncOutput = Self;\n            type State = RenderEffect<<$v as InnerHtmlValue>::State>;\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn html_len(&self) -> usize {\n                0\n            }\n\n            fn to_html(self, buf: &mut String) {\n                let value = self.get();\n                value.to_html(buf);\n            }\n\n            fn to_template(_buf: &mut String) {}\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                (move || self.get()).hydrate::<FROM_SERVER>(el)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                (move || self.get()).build(el)\n            }\n\n            fn rebuild(self, state: &mut Self::State) {\n                (move || self.get()).rebuild(state)\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n        }\n    };\n}\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\nmod stable {\n    use crate::html::element::InnerHtmlValue;\n    #[allow(deprecated)]\n    use reactive_graph::wrappers::read::MaybeSignal;\n    use reactive_graph::{\n        computed::{ArcMemo, Memo},\n        effect::RenderEffect,\n        owner::Storage,\n        signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},\n        traits::Get,\n        wrappers::read::{ArcSignal, Signal},\n    };\n\n    inner_html_reactive!(\n        RwSignal,\n        <V, S>,\n        V,\n        RwSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    inner_html_reactive!(\n        ReadSignal,\n        <V, S>,\n        V,\n        ReadSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    inner_html_reactive!(\n        Memo,\n        <V, S>,\n        V,\n        Memo<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    inner_html_reactive!(\n        Signal,\n        <V, S>,\n        V,\n        Signal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    inner_html_reactive!(\n        MaybeSignal,\n        <V, S>,\n        V,\n        MaybeSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    inner_html_reactive!(ArcRwSignal, <V>, V, ArcRwSignal<V>: Get<Value = V>);\n    inner_html_reactive!(ArcReadSignal, <V>, V, ArcReadSignal<V>: Get<Value = V>);\n    inner_html_reactive!(ArcMemo, <V>, V, ArcMemo<V>: Get<Value = V>);\n    inner_html_reactive!(ArcSignal, <V>, V, ArcSignal<V>: Get<Value = V>);\n}\n\n#[cfg(feature = \"reactive_stores\")]\nmod reactive_stores {\n    use crate::html::element::InnerHtmlValue;\n    #[allow(deprecated)]\n    use reactive_graph::{effect::RenderEffect, owner::Storage, traits::Get};\n    use reactive_stores::{\n        ArcField, ArcStore, AtIndex, AtKeyed, DerefedField, Field,\n        KeyedSubfield, Store, StoreField, Subfield,\n    };\n    use std::ops::{Deref, DerefMut, Index, IndexMut};\n\n    inner_html_reactive!(\n        Subfield,\n        <Inner, Prev, V>,\n        V,\n        Subfield<Inner, Prev, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n\n    inner_html_reactive!(\n        AtKeyed,\n        <Inner, Prev, K, V>,\n        V,\n        AtKeyed<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    inner_html_reactive!(\n        KeyedSubfield,\n        <Inner, Prev, K, V>,\n        V,\n        KeyedSubfield<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    inner_html_reactive!(\n        DerefedField,\n        <S>,\n        <S::Value as Deref>::Target,\n        S: Clone + StoreField + Send + Sync + 'static,\n        <S as StoreField>::Value: Deref + DerefMut\n    );\n\n    inner_html_reactive!(\n        AtIndex,\n        <Inner, Prev>,\n        <Prev as Index<usize>>::Output,\n        AtIndex<Inner, Prev>: Get<Value = Prev::Output>,\n        Prev: Send + Sync + IndexMut<usize> + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n    inner_html_reactive!(\n        Store,\n        <V, S>,\n        V,\n        Store<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    inner_html_reactive!(\n        Field,\n        <V, S>,\n        V,\n        Field<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    inner_html_reactive!(ArcStore, <V>, V, ArcStore<V>: Get<Value = V>);\n    inner_html_reactive!(ArcField, <V>, V, ArcField<V>: Get<Value = V>);\n}\n"
  },
  {
    "path": "tachys/src/reactive_graph/mod.rs",
    "content": "use crate::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute, AttributeValue},\n    hydration::Cursor,\n    renderer::Rndr,\n    ssr::StreamBuilder,\n    view::{\n        add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n        RenderHtml, ToTemplate,\n    },\n};\nuse reactive_graph::effect::RenderEffect;\nuse std::{\n    cell::RefCell,\n    rc::Rc,\n    sync::{Arc, Mutex},\n};\n\n/// Types for two way data binding.\npub mod bind;\nmod class;\nmod inner_html;\n/// Provides a reactive [`NodeRef`](node_ref::NodeRef) type.\npub mod node_ref;\nmod owned;\nmod property;\nmod style;\nmod suspense;\n\npub use owned::*;\npub use suspense::*;\n\nimpl<F, V> ToTemplate for F\nwhere\n    F: ReactiveFunction<Output = V>,\n    V: ToTemplate,\n{\n    const TEMPLATE: &'static str = V::TEMPLATE;\n\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        // FIXME this seems wrong\n        V::to_template(buf, class, style, inner_html, position)\n    }\n}\n\nimpl<F, V> Render for F\nwhere\n    F: ReactiveFunction<Output = V>,\n    V: Render,\n    V::State: 'static,\n{\n    type State = RenderEffectState<V::State>;\n\n    #[track_caller]\n    fn build(mut self) -> Self::State {\n        let hook = throw_error::get_error_hook();\n        RenderEffect::new(move |prev| {\n            let _guard = hook\n                .as_ref()\n                .map(|h| throw_error::set_error_hook(Arc::clone(h)));\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state);\n                state\n            } else {\n                value.build()\n            }\n        })\n        .into()\n    }\n\n    #[track_caller]\n    fn rebuild(self, state: &mut Self::State) {\n        let new = self.build();\n        let mut old = std::mem::replace(state, new);\n        old.insert_before_this(state);\n        old.unmount();\n    }\n}\n\n/// Retained view state for a [`RenderEffect`].\npub struct RenderEffectState<T: 'static>(Option<RenderEffect<T>>);\n\nimpl<T> From<RenderEffect<T>> for RenderEffectState<T> {\n    fn from(value: RenderEffect<T>) -> Self {\n        Self(Some(value))\n    }\n}\n\nimpl<T> Mountable for RenderEffectState<T>\nwhere\n    T: Mountable,\n{\n    fn unmount(&mut self) {\n        if let Some(ref mut inner) = self.0 {\n            inner.unmount();\n        }\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        if let Some(ref mut inner) = self.0 {\n            inner.mount(parent, marker);\n        }\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        if let Some(inner) = &self.0 {\n            inner.insert_before_this(child)\n        } else {\n            false\n        }\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.0\n            .as_ref()\n            .map(|inner| inner.elements())\n            .unwrap_or_default()\n    }\n}\n\nimpl<F, V> RenderHtml for F\nwhere\n    F: ReactiveFunction<Output = V>,\n    V: RenderHtml + 'static,\n    V::State: 'static,\n{\n    type AsyncOutput = V::AsyncOutput;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {\n        self.invoke().dry_resolve();\n    }\n\n    async fn resolve(mut self) -> Self::AsyncOutput {\n        self.invoke().resolve().await\n    }\n\n    fn html_len(&self) -> usize {\n        V::MIN_LENGTH\n    }\n\n    fn to_html_with_buf(\n        mut self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        let value = self.invoke();\n        value.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        mut self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let value = self.invoke();\n        value.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        mut self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        /// codegen optimisation:\n        fn prep(\n            cursor: &Cursor,\n            position: &PositionState,\n        ) -> (\n            Cursor,\n            PositionState,\n            Option<Arc<dyn throw_error::ErrorHook>>,\n        ) {\n            let cursor = cursor.clone();\n            let position = position.clone();\n            let hook = throw_error::get_error_hook();\n            (cursor, position, hook)\n        }\n        let (cursor, position, hook) = prep(cursor, position);\n\n        RenderEffect::new(move |prev| {\n            /// codegen optimisation:\n            fn get_guard(\n                hook: &Option<Arc<dyn throw_error::ErrorHook>>,\n            ) -> Option<throw_error::ResetErrorHookOnDrop> {\n                hook.as_ref()\n                    .map(|h| throw_error::set_error_hook(Arc::clone(h)))\n            }\n            let _guard = get_guard(&hook);\n\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state);\n                state\n            } else {\n                value.hydrate::<FROM_SERVER>(&cursor, &position)\n            }\n        })\n        .into()\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        /// codegen optimisation:\n        fn prep(\n            cursor: &Cursor,\n            position: &PositionState,\n        ) -> (\n            Cursor,\n            PositionState,\n            Option<Arc<dyn throw_error::ErrorHook>>,\n        ) {\n            let cursor = cursor.clone();\n            let position = position.clone();\n            let hook = throw_error::get_error_hook();\n            (cursor, position, hook)\n        }\n        let (cursor, position, hook) = prep(cursor, position);\n\n        let mut fun = self.into_shared();\n\n        RenderEffect::new_with_async_value(\n            {\n                let mut fun = fun.clone();\n                move |prev| {\n                    /// codegen optimisation:\n                    fn get_guard(\n                        hook: &Option<Arc<dyn throw_error::ErrorHook>>,\n                    ) -> Option<throw_error::ResetErrorHookOnDrop>\n                    {\n                        hook.as_ref()\n                            .map(|h| throw_error::set_error_hook(Arc::clone(h)))\n                    }\n                    let _guard = get_guard(&hook);\n\n                    let value = fun.invoke();\n                    if let Some(mut state) = prev {\n                        value.rebuild(&mut state);\n                        state\n                    } else {\n                        unreachable!()\n                    }\n                }\n            },\n            async move { fun.invoke().hydrate_async(&cursor, &position).await },\n        )\n        .await\n        .into()\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl<F, V> AddAnyAttr for F\nwhere\n    F: ReactiveFunction<Output = V>,\n    V: RenderHtml + 'static,\n{\n    type Output<SomeNewAttr: Attribute> =\n        Box<dyn FnMut() -> V::Output<SomeNewAttr::CloneableOwned> + Send>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        mut self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let attr = attr.into_cloneable_owned();\n        Box::new(move || self.invoke().add_any_attr(attr.clone()))\n    }\n}\n\nimpl<M> Mountable for RenderEffect<M>\nwhere\n    M: Mountable + 'static,\n{\n    fn unmount(&mut self) {\n        self.with_value_mut(|state| state.unmount());\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        self.with_value_mut(|state| {\n            state.mount(parent, marker);\n        });\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.with_value_mut(|value| value.insert_before_this(child))\n            .unwrap_or(false)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.with_value_mut(|inner| inner.elements())\n            .unwrap_or_default()\n    }\n}\n\nimpl<T> Drop for RenderEffectState<T> {\n    fn drop(&mut self) {\n        if let Some(effect) = self.0.take() {\n            drop(effect.take_value());\n            drop(effect);\n        }\n    }\n}\n\nimpl<M, E> Mountable for Result<M, E>\nwhere\n    M: Mountable,\n{\n    fn unmount(&mut self) {\n        if let Ok(ref mut inner) = self {\n            inner.unmount();\n        }\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        if let Ok(ref mut inner) = self {\n            inner.mount(parent, marker);\n        }\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        if let Ok(inner) = &self {\n            inner.insert_before_this(child)\n        } else {\n            false\n        }\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.as_ref()\n            .map(|inner| inner.elements())\n            .unwrap_or_default()\n    }\n}\n\n// Dynamic attributes\nimpl<F, V> AttributeValue for F\nwhere\n    F: ReactiveFunction<Output = V>,\n    V: AttributeValue + 'static,\n    V::State: 'static,\n{\n    type AsyncOutput = V::AsyncOutput;\n    type State = RenderEffect<V::State>;\n    type Cloneable = SharedReactiveFunction<V>;\n    type CloneableOwned = SharedReactiveFunction<V>;\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(mut self, key: &str, buf: &mut String) {\n        let value = self.invoke();\n        value.to_html(key, buf);\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        mut self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let key = Rndr::intern(key);\n        let key = key.to_owned();\n        let el = el.to_owned();\n\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&key, &mut state);\n                state\n            } else {\n                value.hydrate::<FROM_SERVER>(&key, &el)\n            }\n        })\n    }\n\n    fn build(\n        mut self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let key = Rndr::intern(key);\n        let key = key.to_owned();\n        let el = el.to_owned();\n\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&key, &mut state);\n                state\n            } else {\n                value.build(&el, &key)\n            }\n        })\n    }\n\n    fn rebuild(mut self, key: &str, state: &mut Self::State) {\n        let key = Rndr::intern(key);\n        let key = key.to_owned();\n        let prev_value = state.take_value();\n\n        *state = RenderEffect::new_with_value(\n            move |prev| {\n                let value = self.invoke();\n                if let Some(mut state) = prev {\n                    value.rebuild(&key, &mut state);\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            prev_value,\n        );\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into_shared()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into_shared()\n    }\n\n    fn dry_resolve(&mut self) {\n        self.invoke();\n    }\n\n    async fn resolve(mut self) -> Self::AsyncOutput {\n        self.invoke().resolve().await\n    }\n}\n\nimpl<V> AttributeValue for Suspend<V>\nwhere\n    V: AttributeValue + 'static,\n    V::State: 'static,\n{\n    type State = Rc<RefCell<Option<V::State>>>;\n    type AsyncOutput = V;\n    type Cloneable = ();\n    type CloneableOwned = ();\n\n    fn html_len(&self) -> usize {\n        0\n    }\n\n    fn to_html(self, _key: &str, _buf: &mut String) {\n        #[cfg(feature = \"tracing\")]\n        tracing::error!(\n            \"Suspended attributes cannot be used outside Suspense.\"\n        );\n    }\n\n    fn to_template(_key: &str, _buf: &mut String) {}\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        key: &str,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let key = key.to_owned();\n        let el = el.to_owned();\n        let state = Rc::new(RefCell::new(None));\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(&state);\n            async move {\n                *state.borrow_mut() =\n                    Some(self.inner.await.hydrate::<FROM_SERVER>(&key, &el));\n                self.subscriber.forward();\n            }\n        });\n        state\n    }\n\n    fn build(\n        self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let key = key.to_owned();\n        let el = el.to_owned();\n        let state = Rc::new(RefCell::new(None));\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(&state);\n            async move {\n                *state.borrow_mut() = Some(self.inner.await.build(&el, &key));\n                self.subscriber.forward();\n            }\n        });\n        state\n    }\n\n    fn rebuild(self, key: &str, state: &mut Self::State) {\n        let key = key.to_owned();\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(state);\n            async move {\n                let value = self.inner.await;\n                let mut state = state.borrow_mut();\n                if let Some(state) = state.as_mut() {\n                    value.rebuild(&key, state);\n                }\n                self.subscriber.forward();\n            }\n        });\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        #[cfg(feature = \"tracing\")]\n        tracing::error!(\"Suspended attributes cannot be spread\");\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        #[cfg(feature = \"tracing\")]\n        tracing::error!(\"Suspended attributes cannot be spread\");\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self.inner.await\n    }\n}\n\n/// A reactive function that can be shared across multiple locations and across threads.\npub type SharedReactiveFunction<T> = Arc<Mutex<dyn FnMut() -> T + Send>>;\n\n/// A reactive view function.\npub trait ReactiveFunction: Send + 'static {\n    /// The return type of the function.\n    type Output;\n\n    /// Call the function.\n    fn invoke(&mut self) -> Self::Output;\n\n    /// Converts the function into a cloneable, shared type.\n    fn into_shared(self) -> Arc<Mutex<dyn FnMut() -> Self::Output + Send>>;\n}\n\nimpl<T: 'static> ReactiveFunction for Arc<Mutex<dyn FnMut() -> T + Send>> {\n    type Output = T;\n\n    fn invoke(&mut self) -> Self::Output {\n        let mut fun = self.lock().expect(\"lock poisoned\");\n        fun()\n    }\n\n    fn into_shared(self) -> Arc<Mutex<dyn FnMut() -> Self::Output + Send>> {\n        self\n    }\n}\n\nimpl<T: Send + Sync + 'static> ReactiveFunction\n    for Arc<dyn Fn() -> T + Send + Sync>\n{\n    type Output = T;\n\n    fn invoke(&mut self) -> Self::Output {\n        self()\n    }\n\n    fn into_shared(self) -> Arc<Mutex<dyn FnMut() -> Self::Output + Send>> {\n        Arc::new(Mutex::new(move || self()))\n    }\n}\n\nimpl<F, T> ReactiveFunction for F\nwhere\n    F: FnMut() -> T + Send + 'static,\n{\n    type Output = T;\n\n    fn invoke(&mut self) -> Self::Output {\n        self()\n    }\n\n    fn into_shared(self) -> Arc<Mutex<dyn FnMut() -> Self::Output + Send>> {\n        Arc::new(Mutex::new(self))\n    }\n}\n\nmacro_rules! reactive_impl {\n    ($name:ident, <$($gen:ident),*>, $v:ty, $dry_resolve:literal, $( $where_clause:tt )*) =>\n    {\n        #[allow(deprecated)]\n        impl<$($gen),*> Render for $name<$($gen),*>\n        where\n            $v: Render + Clone + Send + Sync + 'static,\n            <$v as Render>::State: 'static,\n            $($where_clause)*\n        {\n            type State = RenderEffectState<<$v as Render>::State>;\n\n            #[track_caller]\n            fn build(self) -> Self::State {\n                (move || self.get()).build()\n            }\n\n            #[track_caller]\n            fn rebuild(self, state: &mut Self::State) {\n                let new = self.build();\n                let mut old = std::mem::replace(state, new);\n                old.insert_before_this(state);\n                old.unmount();\n            }\n        }\n\n        #[allow(deprecated)]\n        impl<$($gen),*> AddAnyAttr for $name<$($gen),*>\n        where\n            $v: RenderHtml + Clone + Send + Sync + 'static,\n            <$v as Render>::State: 'static,\n            $($where_clause)*\n        {\n            type Output<SomeNewAttr: Attribute> = Self;\n\n            fn add_any_attr<NewAttr: Attribute>(\n                self,\n                _attr: NewAttr,\n            ) -> Self::Output<NewAttr> {\n                todo!()\n            }\n        }\n\n        #[allow(deprecated)]\n        impl<$($gen),*> RenderHtml for $name<$($gen),*>\n        where\n            $v: RenderHtml + Clone + Send + Sync + 'static,\n            <$v as Render>::State: 'static,\n            $($where_clause)*\n        {\n            type AsyncOutput = Self;\n            type Owned = Self;\n\n            const MIN_LENGTH: usize = 0;\n\n            fn dry_resolve(&mut self) {\n                if $dry_resolve {\n                    _ = self.get();\n                }\n            }\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n\n            fn html_len(&self) -> usize {\n                <$v>::MIN_LENGTH\n            }\n\n            fn to_html_with_buf(\n                self,\n                buf: &mut String,\n                position: &mut Position,\n                escape: bool,\n                mark_branches: bool,\n                extra_attrs: Vec<AnyAttribute>,\n            ) {\n                let value = self.get();\n                value.to_html_with_buf(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                )\n            }\n\n            fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n                self,\n                buf: &mut StreamBuilder,\n                position: &mut Position,\n                escape: bool,\n                mark_branches: bool,\n                extra_attrs: Vec<AnyAttribute>,\n            ) where\n                Self: Sized,\n            {\n                let value = self.get();\n                value.to_html_async_with_buf::<OUT_OF_ORDER>(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n            }\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                cursor: &Cursor,\n                position: &PositionState,\n            ) -> Self::State {\n                (move || self.get())\n                    .hydrate::<FROM_SERVER>(cursor, position)\n            }\n\n            fn into_owned(self) -> Self::Owned {\n                self\n            }\n        }\n\n        #[allow(deprecated)]\n        impl<$($gen),*> AttributeValue for $name<$($gen),*>\n        where\n            $v: AttributeValue + Send + Sync + Clone + 'static,\n            <$v as AttributeValue>::State: 'static,\n            $($where_clause)*\n        {\n            type AsyncOutput = Self;\n            type State = RenderEffect<<$v as AttributeValue>::State>;\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn html_len(&self) -> usize {\n                0\n            }\n\n            fn to_html(self, key: &str, buf: &mut String) {\n                let value = self.get();\n                value.to_html(key, buf);\n            }\n\n            fn to_template(_key: &str, _buf: &mut String) {}\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                key: &str,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                (move || self.get()).hydrate::<FROM_SERVER>(key, el)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                (move || self.get()).build(el, key)\n            }\n\n            fn rebuild(self, key: &str, state: &mut Self::State) {\n                (move || self.get()).rebuild(key, state)\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n        }\n    };\n}\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\nmod stable {\n    use super::RenderEffectState;\n    use crate::{\n        html::attribute::{\n            any_attribute::AnyAttribute, Attribute, AttributeValue,\n        },\n        hydration::Cursor,\n        ssr::StreamBuilder,\n        view::{\n            add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n            RenderHtml,\n        },\n    };\n    #[allow(deprecated)]\n    use reactive_graph::wrappers::read::MaybeSignal;\n    use reactive_graph::{\n        computed::{ArcMemo, Memo},\n        effect::RenderEffect,\n        owner::Storage,\n        signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},\n        traits::Get,\n        wrappers::read::{ArcSignal, MaybeProp, Signal, SignalTypes},\n    };\n\n    reactive_impl!(\n        RwSignal,\n        <V, S>,\n        V,\n        false,\n        RwSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    reactive_impl!(\n        ReadSignal,\n        <V, S>,\n        V,\n        false,\n        ReadSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    reactive_impl!(\n        Memo,\n        <V, S>,\n        V,\n        true,\n        Memo<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    reactive_impl!(\n        Signal,\n        <V, S>,\n        V,\n        true,\n        Signal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    reactive_impl!(\n        MaybeSignal,\n        <V, S>,\n        V,\n        true,\n        MaybeSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    reactive_impl!(\n        MaybeProp,\n        <V, S>,\n        Option<V>,\n        true,\n        MaybeProp<V, S>: Get<Value = Option<V>>,\n        S: Storage<Option<V>> + Storage<SignalTypes<Option<V>, S>>,\n        S: Send + Sync + 'static,\n    );\n    reactive_impl!(ArcRwSignal, <V>, V, false, ArcRwSignal<V>: Get<Value = V>);\n    reactive_impl!(ArcReadSignal, <V>, V, false, ArcReadSignal<V>: Get<Value = V>);\n    reactive_impl!(ArcMemo, <V>, V, false, ArcMemo<V>: Get<Value = V>);\n    reactive_impl!(ArcSignal, <V>, V, true, ArcSignal<V>: Get<Value = V>);\n}\n\n#[cfg(feature = \"reactive_stores\")]\nmod reactive_stores {\n    use super::RenderEffectState;\n    use crate::{\n        html::attribute::{\n            any_attribute::AnyAttribute, Attribute, AttributeValue,\n        },\n        hydration::Cursor,\n        ssr::StreamBuilder,\n        view::{\n            add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n            RenderHtml,\n        },\n    };\n    #[allow(deprecated)]\n    use reactive_graph::{effect::RenderEffect, owner::Storage, traits::Get};\n    use reactive_stores::{\n        ArcField, ArcStore, AtIndex, AtKeyed, DerefedField, Field,\n        KeyedSubfield, Store, StoreField, Subfield,\n    };\n    use std::ops::{Deref, DerefMut, Index, IndexMut};\n\n    reactive_impl!(\n        Subfield,\n        <Inner, Prev, V>,\n        V,\n        false,\n        Subfield<Inner, Prev, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n\n    reactive_impl!(\n        AtKeyed,\n        <Inner, Prev, K, V>,\n        V,\n        false,\n        AtKeyed<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    reactive_impl!(\n        KeyedSubfield,\n        <Inner, Prev, K, V>,\n        V,\n        false,\n        KeyedSubfield<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    reactive_impl!(\n        DerefedField,\n        <S>,\n        <S::Value as Deref>::Target,\n        false,\n        S: Clone + StoreField + Send + Sync + 'static,\n        <S as StoreField>::Value: Deref + DerefMut\n    );\n\n    reactive_impl!(\n        AtIndex,\n        <Inner, Prev>,\n        <Prev as Index<usize>>::Output,\n        false,\n        AtIndex<Inner, Prev>: Get<Value = Prev::Output>,\n        Prev: Send + Sync + IndexMut<usize> + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n    reactive_impl!(\n        Store,\n        <V, S>,\n        V,\n        false,\n        Store<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    reactive_impl!(\n        Field,\n        <V, S>,\n        V,\n        false,\n        Field<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    reactive_impl!(ArcStore, <V>, V, false, ArcStore<V>: Get<Value = V>);\n    reactive_impl!(ArcField, <V>, V, false, ArcField<V>: Get<Value = V>);\n}\n\n/*\n#[cfg(test)]\nmod tests {\n    use crate::{\n        html::element::{button, main, HtmlElement},\n        renderer::mock_dom::MockDom,\n        view::Render,\n    };\n    use leptos_reactive::{create_runtime, RwSignal, SignalGet, SignalSet};\n\n    #[test]\n    fn create_dynamic_element() {\n        let rt = create_runtime();\n        let count = RwSignal::new(0);\n        let app: HtmlElement<_, _, _, MockDom> =\n            button((), move || count.get().to_string());\n        let el = app.build();\n        assert_eq!(el.el.to_debug_html(), \"<button>0</button>\");\n        rt.dispose();\n    }\n\n    #[test]\n    fn update_dynamic_element() {\n        let rt = create_runtime();\n        let count = RwSignal::new(0);\n        let app: HtmlElement<_, _, _, MockDom> =\n            button((), move || count.get().to_string());\n        let el = app.build();\n        assert_eq!(el.el.to_debug_html(), \"<button>0</button>\");\n        count.set(1);\n        assert_eq!(el.el.to_debug_html(), \"<button>1</button>\");\n        rt.dispose();\n    }\n\n    #[test]\n    fn update_dynamic_element_among_siblings() {\n        let rt = create_runtime();\n        let count = RwSignal::new(0);\n        let app: HtmlElement<_, _, _, MockDom> = main(\n            (),\n            button(\n                (),\n                (\"Hello, my \", move || count.get().to_string(), \" friends.\"),\n            ),\n        );\n        let el = app.build();\n        assert_eq!(\n            el.el.to_debug_html(),\n            \"<main><button>Hello, my 0 friends.</button></main>\"\n        );\n        count.set(42);\n        assert_eq!(\n            el.el.to_debug_html(),\n            \"<main><button>Hello, my 42 friends.</button></main>\"\n        );\n        rt.dispose();\n    }\n}\n */\n"
  },
  {
    "path": "tachys/src/reactive_graph/node_ref.rs",
    "content": "use crate::html::{element::ElementType, node_ref::NodeRefContainer};\nuse reactive_graph::{\n    effect::Effect,\n    graph::untrack,\n    signal::{\n        guards::{Derefable, ReadGuard},\n        RwSignal,\n    },\n    traits::{\n        DefinedAt, Get, IsDisposed, Notify, ReadUntracked, Set, Track,\n        UntrackableGuard, Write,\n    },\n};\nuse send_wrapper::SendWrapper;\nuse std::{cell::Cell, ops::DerefMut};\nuse wasm_bindgen::JsCast;\n\n/// A reactive reference to a DOM node that can be used with the `node_ref` attribute.\n#[derive(Debug)]\npub struct NodeRef<E>(RwSignal<Option<SendWrapper<E::Output>>>)\nwhere\n    E: ElementType,\n    E::Output: 'static;\n\nimpl<E> NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: 'static,\n{\n    /// Creates a new node reference.\n    #[track_caller]\n    pub fn new() -> Self {\n        Self(RwSignal::new(None))\n    }\n\n    /// Runs the provided closure when the `NodeRef` has been connected\n    /// with its element.\n    #[inline(always)]\n    pub fn on_load<F>(self, f: F)\n    where\n        E: 'static,\n        F: FnOnce(E::Output) + 'static,\n        E: ElementType,\n        E::Output: JsCast + Clone + 'static,\n    {\n        let f = Cell::new(Some(f));\n\n        Effect::new(move |_| {\n            if let Some(node_ref) = self.get() {\n                let f = f.take().unwrap();\n                untrack(move || {\n                    f(node_ref);\n                });\n            }\n        });\n    }\n}\n\nimpl<E> Default for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: 'static,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<E> Clone for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: 'static,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<E> Copy for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: 'static,\n{\n}\n\nimpl<E> NodeRefContainer<E> for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: JsCast + 'static,\n{\n    fn load(self, el: &crate::renderer::types::Element) {\n        // safe to construct SendWrapper here, because it will only run in the browser\n        // so it will always be accessed or dropped from the main thread\n        self.0\n            .set(Some(SendWrapper::new(el.clone().unchecked_into())));\n    }\n}\n\nimpl<E> DefinedAt for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: JsCast + 'static,\n{\n    fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {\n        self.0.defined_at()\n    }\n}\n\nimpl<E> Notify for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: JsCast + Clone + 'static,\n{\n    fn notify(&self) {\n        self.0.notify();\n    }\n}\n\nimpl<E> Write for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: JsCast + Clone + 'static,\n{\n    type Value = Option<SendWrapper<E::Output>>;\n\n    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {\n        self.0.try_write()\n    }\n\n    fn try_write_untracked(\n        &self,\n    ) -> Option<impl DerefMut<Target = Self::Value>> {\n        self.0.try_write_untracked()\n    }\n}\n\nimpl<E> ReadUntracked for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: JsCast + Clone + 'static,\n{\n    type Value = ReadGuard<Option<E::Output>, Derefable<Option<E::Output>>>;\n\n    fn try_read_untracked(&self) -> Option<Self::Value> {\n        Some(ReadGuard::new(Derefable(\n            self.0.try_read_untracked()?.as_deref().cloned(),\n        )))\n    }\n}\n\nimpl<E> Track for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: JsCast + 'static,\n{\n    fn track(&self) {\n        self.0.track();\n    }\n}\n\nimpl<E> IsDisposed for NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: 'static,\n{\n    fn is_disposed(&self) -> bool {\n        self.0.is_disposed()\n    }\n}\n\n/// Create a [NodeRef].\n#[inline(always)]\n#[track_caller]\n#[deprecated = \"This function is being removed to conform to Rust idioms. \\\n                Please use `NodeRef::new()` instead.\"]\npub fn create_node_ref<E>() -> NodeRef<E>\nwhere\n    E: ElementType,\n    E::Output: 'static,\n{\n    NodeRef::new()\n}\n"
  },
  {
    "path": "tachys/src/reactive_graph/owned.rs",
    "content": "use crate::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    prelude::Mountable,\n    ssr::StreamBuilder,\n    view::{add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml},\n};\nuse reactive_graph::{computed::ScopedFuture, owner::Owner};\n\n/// A view wrapper that sets the reactive [`Owner`] to a particular owner whenever it is rendered.\n#[derive(Debug, Clone)]\npub struct OwnedView<T> {\n    owner: Owner,\n    view: T,\n}\n\nimpl<T> OwnedView<T> {\n    /// Wraps a view with the current owner.\n    pub fn new(view: T) -> Self {\n        let owner = Owner::current().expect(\"no reactive owner\");\n        Self { owner, view }\n    }\n\n    /// Wraps a view with the given owner.\n    pub fn new_with_owner(view: T, owner: Owner) -> Self {\n        Self { owner, view }\n    }\n}\n\n/// Retained view state for an [`OwnedView`].\n#[derive(Debug, Clone)]\npub struct OwnedViewState<T>\nwhere\n    T: Mountable,\n{\n    owner: Owner,\n    state: T,\n}\n\nimpl<T> OwnedViewState<T>\nwhere\n    T: Mountable,\n{\n    /// Wraps a state with the given owner.\n    fn new(state: T, owner: Owner) -> Self {\n        Self { owner, state }\n    }\n}\n\nimpl<T> Render for OwnedView<T>\nwhere\n    T: Render,\n{\n    type State = OwnedViewState<T::State>;\n\n    fn build(self) -> Self::State {\n        let state = self.owner.with(|| self.view.build());\n        OwnedViewState::new(state, self.owner)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let OwnedView { owner, view, .. } = self;\n        owner.with(|| view.rebuild(&mut state.state));\n        state.owner = owner;\n    }\n}\n\nimpl<T> AddAnyAttr for OwnedView<T>\nwhere\n    T: AddAnyAttr,\n{\n    type Output<SomeNewAttr: Attribute> = OwnedView<T::Output<SomeNewAttr>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let OwnedView { owner, view } = self;\n        OwnedView {\n            owner,\n            view: view.add_any_attr(attr),\n        }\n    }\n}\n\nimpl<T> RenderHtml for OwnedView<T>\nwhere\n    T: RenderHtml,\n{\n    // TODO\n    type AsyncOutput = OwnedView<T::AsyncOutput>;\n    type Owned = OwnedView<T::Owned>;\n\n    const MIN_LENGTH: usize = T::MIN_LENGTH;\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        self.owner.with(|| {\n            self.view.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            )\n        });\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        self.owner.with(|| {\n            self.view.to_html_async_with_buf::<OUT_OF_ORDER>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            )\n        });\n\n        // if self.owner drops here, it can be disposed before the asynchronous rendering process\n        // has actually happened\n        // instead, we'll stuff it into the cleanups of its parent so that it will remain alive at\n        // least as long as the parent does\n        Owner::on_cleanup(move || drop(self.owner));\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let state = self\n            .owner\n            .with(|| self.view.hydrate::<FROM_SERVER>(cursor, position));\n        OwnedViewState::new(state, self.owner)\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let state = self\n            .owner\n            .with(|| {\n                ScopedFuture::new(self.view.hydrate_async(cursor, position))\n            })\n            .await;\n        OwnedViewState::new(state, self.owner)\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        let OwnedView { owner, view } = self;\n        let view = owner\n            .with(|| ScopedFuture::new(async move { view.resolve().await }))\n            .await;\n        OwnedView { owner, view }\n    }\n\n    fn dry_resolve(&mut self) {\n        self.owner.with(|| self.view.dry_resolve());\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        OwnedView {\n            owner: self.owner,\n            view: self.view.into_owned(),\n        }\n    }\n}\n\nimpl<T> Mountable for OwnedViewState<T>\nwhere\n    T: Mountable,\n{\n    fn unmount(&mut self) {\n        self.state.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        self.state.mount(parent, marker);\n    }\n\n    fn try_mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) -> bool {\n        self.state.try_mount(parent, marker)\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.state.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.state.elements()\n    }\n}\n"
  },
  {
    "path": "tachys/src/reactive_graph/property.rs",
    "content": "use super::{ReactiveFunction, SharedReactiveFunction};\nuse crate::{html::property::IntoProperty, renderer::Rndr};\nuse reactive_graph::effect::RenderEffect;\n\n// These do update during hydration because properties don't exist in the DOM\nimpl<F, V> IntoProperty for F\nwhere\n    F: ReactiveFunction<Output = V>,\n    V: IntoProperty + 'static,\n    V::State: 'static,\n{\n    type State = RenderEffect<V::State>;\n    type Cloneable = SharedReactiveFunction<V>;\n    type CloneableOwned = SharedReactiveFunction<V>;\n\n    fn hydrate<const FROM_SERVER: bool>(\n        mut self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let key = Rndr::intern(key);\n        let key = key.to_owned();\n        let el = el.to_owned();\n\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state, &key);\n                state\n            } else {\n                value.hydrate::<FROM_SERVER>(&el, &key)\n            }\n        })\n    }\n\n    fn build(\n        mut self,\n        el: &crate::renderer::types::Element,\n        key: &str,\n    ) -> Self::State {\n        let key = Rndr::intern(key);\n        let key = key.to_owned();\n        let el = el.to_owned();\n\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state, &key);\n                state\n            } else {\n                value.build(&el, &key)\n            }\n        })\n    }\n\n    fn rebuild(mut self, state: &mut Self::State, key: &str) {\n        let prev_value = state.take_value();\n        let key = key.to_owned();\n        *state = RenderEffect::new_with_value(\n            move |prev| {\n                let value = self.invoke();\n                if let Some(mut state) = prev {\n                    value.rebuild(&mut state, &key);\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            prev_value,\n        );\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into_shared()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into_shared()\n    }\n}\n\nmacro_rules! property_reactive {\n    ($name:ident, <$($gen:ident),*>, $v:ty, $( $where_clause:tt )*) =>\n    {\n        #[allow(deprecated)]\n        impl<$($gen),*> IntoProperty for $name<$($gen),*>\n        where\n            $v: IntoProperty + Clone + Send + Sync + 'static,\n            <$v as IntoProperty>::State: 'static,\n            $($where_clause)*\n        {\n            type State = RenderEffect<<$v as IntoProperty>::State>;\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                (move || self.get()).hydrate::<FROM_SERVER>(el, key)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n                key: &str,\n            ) -> Self::State {\n                (move || self.get()).build(el, key)\n            }\n\n            fn rebuild(self, state: &mut Self::State, key: &str) {\n                (move || self.get()).rebuild(state, key)\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n        }\n    };\n}\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\nmod stable {\n    use crate::html::property::IntoProperty;\n    #[allow(deprecated)]\n    use reactive_graph::wrappers::read::MaybeSignal;\n    use reactive_graph::{\n        computed::{ArcMemo, Memo},\n        effect::RenderEffect,\n        owner::Storage,\n        signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},\n        traits::Get,\n        wrappers::read::{ArcSignal, Signal},\n    };\n\n    property_reactive!(\n        RwSignal,\n        <V, S>,\n        V,\n        RwSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    property_reactive!(\n        ReadSignal,\n        <V, S>,\n        V,\n        ReadSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    property_reactive!(\n        Memo,\n        <V, S>,\n        V,\n        Memo<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    property_reactive!(\n        Signal,\n        <V, S>,\n        V,\n        Signal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    property_reactive!(\n        MaybeSignal,\n        <V, S>,\n        V,\n        MaybeSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    property_reactive!(ArcRwSignal, <V>, V, ArcRwSignal<V>: Get<Value = V>);\n    property_reactive!(ArcReadSignal, <V>, V, ArcReadSignal<V>: Get<Value = V>);\n    property_reactive!(ArcMemo, <V>, V, ArcMemo<V>: Get<Value = V>);\n    property_reactive!(ArcSignal, <V>, V, ArcSignal<V>: Get<Value = V>);\n}\n\n#[cfg(feature = \"reactive_stores\")]\nmod reactive_stores {\n    use crate::html::property::IntoProperty;\n    #[allow(deprecated)]\n    use reactive_graph::{effect::RenderEffect, owner::Storage, traits::Get};\n    use reactive_stores::{\n        ArcField, ArcStore, AtIndex, AtKeyed, DerefedField, Field,\n        KeyedSubfield, Store, StoreField, Subfield,\n    };\n    use std::ops::{Deref, DerefMut, Index, IndexMut};\n\n    property_reactive!(\n        Subfield,\n        <Inner, Prev, V>,\n        V,\n        Subfield<Inner, Prev, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n\n    property_reactive!(\n        AtKeyed,\n        <Inner, Prev, K, V>,\n        V,\n        AtKeyed<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    property_reactive!(\n        KeyedSubfield,\n        <Inner, Prev, K, V>,\n        V,\n        KeyedSubfield<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    property_reactive!(\n        DerefedField,\n        <S>,\n        <S::Value as Deref>::Target,\n        S: Clone + StoreField + Send + Sync + 'static,\n        <S as StoreField>::Value: Deref + DerefMut\n    );\n\n    property_reactive!(\n        AtIndex,\n        <Inner, Prev>,\n        <Prev as Index<usize>>::Output,\n        AtIndex<Inner, Prev>: Get<Value = Prev::Output>,\n        Prev: Send + Sync + IndexMut<usize> + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n    property_reactive!(\n        Store,\n        <V, S>,\n        V,\n        Store<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    property_reactive!(\n        Field,\n        <V, S>,\n        V,\n        Field<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    property_reactive!(ArcStore, <V>, V, ArcStore<V>: Get<Value = V>);\n    property_reactive!(ArcField, <V>, V, ArcField<V>: Get<Value = V>);\n}\n"
  },
  {
    "path": "tachys/src/reactive_graph/style.rs",
    "content": "use super::{ReactiveFunction, SharedReactiveFunction};\nuse crate::{\n    html::style::{IntoStyle, IntoStyleValue},\n    renderer::Rndr,\n};\nuse reactive_graph::effect::RenderEffect;\nuse std::sync::Arc;\n\nimpl<F, S> IntoStyleValue for F\nwhere\n    F: ReactiveFunction<Output = S>,\n    S: IntoStyleValue + 'static,\n{\n    type AsyncOutput = Self;\n    type State = (Arc<str>, RenderEffect<S::State>);\n    type Cloneable = SharedReactiveFunction<S>;\n    type CloneableOwned = SharedReactiveFunction<S>;\n\n    fn to_html(self, name: &str, style: &mut String) {\n        let mut f = self;\n        let value = f.invoke();\n        value.to_html(name, style);\n    }\n\n    fn build(\n        mut self,\n        style: &crate::renderer::dom::CssStyleDeclaration,\n        name: &str,\n    ) -> Self::State {\n        let name: Arc<str> = Rndr::intern(name).into();\n        let style = style.to_owned();\n        (\n            Arc::clone(&name),\n            RenderEffect::new(move |prev| {\n                let value = self.invoke();\n                if let Some(mut state) = prev {\n                    value.rebuild(&style, &name, &mut state);\n                    state\n                } else {\n                    value.build(&style, &name)\n                }\n            }),\n        )\n    }\n\n    fn rebuild(\n        mut self,\n        style: &crate::renderer::dom::CssStyleDeclaration,\n        name: &str,\n        state: &mut Self::State,\n    ) {\n        let (prev_name, prev_effect) = state;\n        let mut prev_value = prev_effect.take_value();\n        if name != prev_name.as_ref() {\n            Rndr::remove_css_property(style, prev_name.as_ref());\n            prev_value = None;\n        }\n        let name: Arc<str> = name.into();\n        let style = style.to_owned();\n\n        *state = (\n            Arc::clone(&name),\n            RenderEffect::new_with_value(\n                move |prev| {\n                    let value = self.invoke();\n                    if let Some(mut state) = prev {\n                        value.rebuild(&style, &name, &mut state);\n                        state\n                    } else {\n                        value.build(&style, &name)\n                    }\n                },\n                prev_value,\n            ),\n        );\n    }\n\n    fn hydrate(\n        mut self,\n        style: &crate::renderer::dom::CssStyleDeclaration,\n        name: &str,\n    ) -> Self::State {\n        let name: Arc<str> = Rndr::intern(name).into();\n        let style = style.to_owned();\n        (\n            Arc::clone(&name),\n            RenderEffect::new(move |prev| {\n                let value = self.invoke();\n                if let Some(mut state) = prev {\n                    value.rebuild(&style, &name, &mut state);\n                    state\n                } else {\n                    value.hydrate(&style, &name)\n                }\n            }),\n        )\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into_shared()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into_shared()\n    }\n\n    fn dry_resolve(&mut self) {\n        self.invoke();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n}\n\nimpl<F, C> IntoStyle for F\nwhere\n    F: ReactiveFunction<Output = C>,\n    C: IntoStyle + 'static,\n    C::State: 'static,\n{\n    type AsyncOutput = C::AsyncOutput;\n    type State = RenderEffect<C::State>;\n    type Cloneable = SharedReactiveFunction<C>;\n    type CloneableOwned = SharedReactiveFunction<C>;\n\n    fn to_html(mut self, style: &mut String) {\n        let value = self.invoke();\n        value.to_html(style);\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        mut self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        // TODO FROM_SERVER vs template\n        let el = el.clone();\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state);\n                state\n            } else {\n                value.hydrate::<FROM_SERVER>(&el)\n            }\n        })\n    }\n\n    fn build(mut self, el: &crate::renderer::types::Element) -> Self::State {\n        let el = el.clone();\n        RenderEffect::new(move |prev| {\n            let value = self.invoke();\n            if let Some(mut state) = prev {\n                value.rebuild(&mut state);\n                state\n            } else {\n                value.build(&el)\n            }\n        })\n    }\n\n    fn rebuild(mut self, state: &mut Self::State) {\n        let prev_value = state.take_value();\n        *state = RenderEffect::new_with_value(\n            move |prev| {\n                let value = self.invoke();\n                if let Some(mut state) = prev {\n                    value.rebuild(&mut state);\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            prev_value,\n        );\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self.into_shared()\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self.into_shared()\n    }\n\n    fn dry_resolve(&mut self) {\n        self.invoke();\n    }\n\n    async fn resolve(mut self) -> Self::AsyncOutput {\n        self.invoke().resolve().await\n    }\n\n    fn reset(state: &mut Self::State) {\n        *state = RenderEffect::new_with_value(\n            move |prev| {\n                if let Some(mut state) = prev {\n                    C::reset(&mut state);\n                    state\n                } else {\n                    unreachable!()\n                }\n            },\n            state.take_value(),\n        );\n    }\n}\n\nmacro_rules! style_reactive {\n    ($name:ident, <$($gen:ident),*>, $v:ty, $( $where_clause:tt )*) =>\n    {\n        #[allow(deprecated)]\n        impl<$($gen),*> IntoStyle for $name<$($gen),*>\n        where\n            $v: IntoStyle + Clone + Send + Sync + 'static,\n            <$v as IntoStyle>::State: 'static,\n            $($where_clause)*\n        {\n            type AsyncOutput = Self;\n            type State = RenderEffect<<$v as IntoStyle>::State>;\n            type Cloneable = Self;\n            type CloneableOwned = Self;\n\n            fn to_html(self, style: &mut String) {\n                let value = self.get();\n                value.to_html(style);\n            }\n\n            fn hydrate<const FROM_SERVER: bool>(\n                self,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                (move || self.get()).hydrate::<FROM_SERVER>(el)\n            }\n\n            fn build(\n                self,\n                el: &crate::renderer::types::Element,\n            ) -> Self::State {\n                (move || self.get()).build(el)\n            }\n\n            fn rebuild(self, state: &mut Self::State) {\n                (move || self.get()).rebuild(state)\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n\n            fn reset(state: &mut Self::State) {\n                *state = RenderEffect::new_with_value(\n                    move |prev| {\n                        if let Some(mut state) = prev {\n                            <$v>::reset(&mut state);\n                            state\n                        } else {\n                            unreachable!()\n                        }\n                    },\n                    state.take_value(),\n                );\n            }\n        }\n\n        #[allow(deprecated)]\n        impl<$($gen),*> IntoStyleValue for $name<$($gen),*>\n        where\n            $v: IntoStyleValue + Send + Sync + Clone + 'static,\n            $($where_clause)*\n        {\n            type AsyncOutput = Self;\n            type State = (Arc<str>, RenderEffect<<$v as IntoStyleValue>::State>);\n            type Cloneable = $name<$($gen),*>;\n            type CloneableOwned = $name<$($gen),*>;\n\n            fn to_html(self, name: &str, style: &mut String) {\n                IntoStyleValue::to_html(move || self.get(), name, style)\n            }\n\n            fn build(\n                self,\n                style: &crate::renderer::dom::CssStyleDeclaration,\n                name: &str,\n            ) -> Self::State {\n                IntoStyleValue::build(move || self.get(), style, name)\n            }\n\n            fn rebuild(\n                self,\n                style: &crate::renderer::dom::CssStyleDeclaration,\n                name: &str,\n                state: &mut Self::State,\n            ) {\n                IntoStyleValue::rebuild(\n                    move || self.get(),\n                    style,\n                    name,\n                    state,\n                )\n            }\n\n            fn hydrate(\n                self,\n                style: &crate::renderer::dom::CssStyleDeclaration,\n                name: &str,\n            ) -> Self::State {\n                IntoStyleValue::hydrate(move || self.get(), style, name)\n            }\n\n            fn into_cloneable(self) -> Self::Cloneable {\n                self\n            }\n\n            fn into_cloneable_owned(self) -> Self::CloneableOwned {\n                self\n            }\n\n            fn dry_resolve(&mut self) {}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                self\n            }\n        }\n    };\n}\n\n#[cfg(not(all(feature = \"nightly\", rustc_nightly)))]\nmod stable {\n    use super::RenderEffect;\n    use crate::html::style::{IntoStyle, IntoStyleValue};\n    #[allow(deprecated)]\n    use reactive_graph::wrappers::read::MaybeSignal;\n    use reactive_graph::{\n        computed::{ArcMemo, Memo},\n        owner::Storage,\n        signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},\n        traits::Get,\n        wrappers::read::{ArcSignal, Signal},\n    };\n    use std::sync::Arc;\n\n    style_reactive!(\n        RwSignal,\n        <V, S>,\n        V,\n        RwSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    style_reactive!(\n        ReadSignal,\n        <V, S>,\n        V,\n        ReadSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    style_reactive!(\n        Memo,\n        <V, S>,\n        V,\n        Memo<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    style_reactive!(\n        Signal,\n        <V, S>,\n        V,\n        Signal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    style_reactive!(\n        MaybeSignal,\n        <V, S>,\n        V,\n        MaybeSignal<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    style_reactive!(ArcRwSignal, <V>, V, ArcRwSignal<V>: Get<Value = V>);\n    style_reactive!(ArcReadSignal, <V>, V, ArcReadSignal<V>: Get<Value = V>);\n    style_reactive!(ArcMemo, <V>, V, ArcMemo<V>: Get<Value = V>);\n    style_reactive!(ArcSignal, <V>, V, ArcSignal<V>: Get<Value = V>);\n}\n\n#[cfg(feature = \"reactive_stores\")]\nmod reactive_stores {\n    use super::RenderEffect;\n    use crate::html::style::{IntoStyle, IntoStyleValue};\n    #[allow(deprecated)]\n    use reactive_graph::{owner::Storage, traits::Get};\n    use reactive_stores::{\n        ArcField, ArcStore, AtIndex, AtKeyed, DerefedField, Field,\n        KeyedSubfield, Store, StoreField, Subfield,\n    };\n    use std::{\n        ops::{Deref, DerefMut, Index, IndexMut},\n        sync::Arc,\n    };\n\n    style_reactive!(\n        Subfield,\n        <Inner, Prev, V>,\n        V,\n        Subfield<Inner, Prev, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n\n    style_reactive!(\n        AtKeyed,\n        <Inner, Prev, K, V>,\n        V,\n        AtKeyed<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    style_reactive!(\n        KeyedSubfield,\n        <Inner, Prev, K, V>,\n        V,\n        KeyedSubfield<Inner, Prev, K, V>: Get<Value = V>,\n        Prev: Send + Sync + 'static,\n        Inner: Send + Sync + Clone + 'static,\n        K: Send + Sync + std::fmt::Debug + Clone + 'static,\n        for<'a> &'a V: IntoIterator,\n    );\n\n    style_reactive!(\n        DerefedField,\n        <S>,\n        <S::Value as Deref>::Target,\n        S: Clone + StoreField + Send + Sync + 'static,\n        <S as StoreField>::Value: Deref + DerefMut\n    );\n\n    style_reactive!(\n        AtIndex,\n        <Inner, Prev>,\n        <Prev as Index<usize>>::Output,\n        AtIndex<Inner, Prev>: Get<Value = Prev::Output>,\n        Prev: Send + Sync + IndexMut<usize> + 'static,\n        Inner: Send + Sync + Clone + 'static,\n    );\n    style_reactive!(\n        Store,\n        <V, S>,\n        V,\n        Store<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    style_reactive!(\n        Field,\n        <V, S>,\n        V,\n        Field<V, S>: Get<Value = V>,\n        S: Storage<V> + Storage<Option<V>>,\n        S: Send + Sync + 'static,\n    );\n    style_reactive!(ArcStore, <V>, V, ArcStore<V>: Get<Value = V>);\n    style_reactive!(ArcField, <V>, V, ArcField<V>: Get<Value = V>);\n}\n/*\nimpl<Fut> IntoStyle for Suspend<Fut>\nwhere\n    Fut: Clone + Future + Send + 'static,\n    Fut::Output: IntoStyle,\n{\n    type AsyncOutput = Fut::Output;\n    type State = Rc<RefCell<Option<<Fut::Output as IntoStyle>::State>>>;\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    fn to_html(self, style: &mut String) {\n        if let Some(inner) = self.inner.now_or_never() {\n            inner.to_html(style);\n        } else {\n            panic!(\"You cannot use Suspend on an attribute outside Suspense\");\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        let el = el.to_owned();\n        let state = Rc::new(RefCell::new(None));\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(&state);\n            async move {\n                *state.borrow_mut() =\n                    Some(self.inner.await.hydrate::<FROM_SERVER>(&el));\n                self.subscriber.forward();\n            }\n        });\n        state\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        let el = el.to_owned();\n        let state = Rc::new(RefCell::new(None));\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(&state);\n            async move {\n                *state.borrow_mut() = Some(self.inner.await.build(&el));\n                self.subscriber.forward();\n            }\n        });\n        state\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(state);\n            async move {\n                let value = self.inner.await;\n                let mut state = state.borrow_mut();\n                if let Some(state) = state.as_mut() {\n                    value.rebuild(state);\n                }\n                self.subscriber.forward();\n            }\n        });\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self.inner.await\n    }\n}\n*/\n"
  },
  {
    "path": "tachys/src/reactive_graph/suspense.rs",
    "content": "use crate::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    ssr::StreamBuilder,\n    view::{\n        add_attr::AddAnyAttr, iterators::OptionState, Mountable, Position,\n        PositionState, Render, RenderHtml,\n    },\n};\nuse any_spawner::Executor;\nuse futures::{\n    future::{AbortHandle, Abortable},\n    select, FutureExt,\n};\nuse or_poisoned::OrPoisoned;\nuse reactive_graph::{\n    computed::{\n        suspense::{LocalResourceNotifier, SuspenseContext},\n        ScopedFuture,\n    },\n    graph::{\n        AnySource, AnySubscriber, Observer, ReactiveNode, Source, Subscriber,\n        ToAnySubscriber, WithObserver,\n    },\n    owner::{on_cleanup, provide_context, use_context},\n};\nuse std::{\n    cell::RefCell,\n    fmt::Debug,\n    future::{Future, IntoFuture},\n    mem,\n    pin::Pin,\n    rc::Rc,\n    sync::{Arc, Mutex, Weak},\n};\nuse throw_error::ErrorHook;\n\n/// A suspended `Future`, which can be used in the view.\npub struct Suspend<T> {\n    pub(crate) subscriber: SuspendSubscriber,\n    pub(crate) inner: Pin<Box<dyn Future<Output = T> + Send>>,\n}\n\n#[derive(Debug, Clone)]\npub(crate) struct SuspendSubscriber {\n    inner: Arc<SuspendSubscriberInner>,\n}\n\n#[derive(Debug)]\nstruct SuspendSubscriberInner {\n    outer_subscriber: Option<AnySubscriber>,\n    sources: Mutex<Vec<AnySource>>,\n}\n\nimpl SuspendSubscriber {\n    pub fn new() -> Self {\n        let outer_subscriber = Observer::get();\n        Self {\n            inner: Arc::new(SuspendSubscriberInner {\n                outer_subscriber,\n                sources: Default::default(),\n            }),\n        }\n    }\n\n    /// Re-links all reactive sources from this to another subscriber.\n    ///\n    /// This is used to collect reactive dependencies during the rendering phase, and only later\n    /// connect them to any outer effect, to prevent the completion of async resources from\n    /// triggering the render effect to run a second time.\n    pub fn forward(&self) {\n        if let Some(to) = &self.inner.outer_subscriber {\n            let sources =\n                mem::take(&mut *self.inner.sources.lock().or_poisoned());\n            for source in sources {\n                source.add_subscriber(to.clone());\n                to.add_source(source);\n            }\n        }\n    }\n}\n\nimpl ReactiveNode for SuspendSubscriberInner {\n    fn mark_dirty(&self) {}\n\n    fn mark_check(&self) {}\n\n    fn mark_subscribers_check(&self) {}\n\n    fn update_if_necessary(&self) -> bool {\n        false\n    }\n}\n\nimpl Subscriber for SuspendSubscriberInner {\n    fn add_source(&self, source: AnySource) {\n        self.sources.lock().or_poisoned().push(source);\n    }\n\n    fn clear_sources(&self, subscriber: &AnySubscriber) {\n        for source in mem::take(&mut *self.sources.lock().or_poisoned()) {\n            source.remove_subscriber(subscriber);\n        }\n    }\n}\n\nimpl ToAnySubscriber for SuspendSubscriber {\n    fn to_any_subscriber(&self) -> AnySubscriber {\n        AnySubscriber(\n            Arc::as_ptr(&self.inner) as usize,\n            Arc::downgrade(&self.inner) as Weak<dyn Subscriber + Send + Sync>,\n        )\n    }\n}\n\nimpl<T> Suspend<T> {\n    /// Creates a new suspended view.\n    pub fn new<Fut>(fut: Fut) -> Self\n    where\n        Fut: IntoFuture<Output = T>,\n        Fut::IntoFuture: Send + 'static,\n    {\n        let subscriber = SuspendSubscriber::new();\n        let any_subscriber = subscriber.to_any_subscriber();\n        let inner = any_subscriber\n            .with_observer(|| Box::pin(ScopedFuture::new(fut.into_future())));\n        Self { subscriber, inner }\n    }\n}\n\nimpl<T> Debug for Suspend<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Suspend\").finish()\n    }\n}\n\n/// Retained view state for [`Suspend`].\npub struct SuspendState<T>\nwhere\n    T: Render,\n{\n    inner: Rc<RefCell<OptionState<T>>>,\n}\n\nimpl<T> Mountable for SuspendState<T>\nwhere\n    T: Render,\n{\n    fn unmount(&mut self) {\n        self.inner.borrow_mut().unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        self.inner.borrow_mut().mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.inner.borrow_mut().insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.inner.borrow().elements()\n    }\n}\n\nimpl<T> Render for Suspend<T>\nwhere\n    T: Render + 'static,\n{\n    type State = SuspendState<T>;\n\n    fn build(self) -> Self::State {\n        let Self { subscriber, inner } = self;\n\n        // create a Future that will be aborted on on_cleanup\n        // this prevents trying to access signals or other resources inside the Suspend, after the\n        // await, if they have already been cleaned up\n        let (abort_handle, abort_registration) = AbortHandle::new_pair();\n        let mut fut = Box::pin(Abortable::new(inner, abort_registration));\n        on_cleanup(move || abort_handle.abort());\n\n        // poll the future once immediately\n        // if it's already available, start in the ready state\n        // otherwise, start with the fallback\n        let initial = fut.as_mut().now_or_never().and_then(Result::ok);\n        let initially_pending = initial.is_none();\n        let inner = Rc::new(RefCell::new(initial.build()));\n\n        // get a unique ID if there's a SuspenseContext\n        let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());\n        let error_hook = use_context::<Arc<dyn ErrorHook>>();\n\n        // if the initial state was pending, spawn a future to wait for it\n        // spawning immediately means that our now_or_never poll result isn't lost\n        // if it wasn't pending at first, we don't need to poll the Future again\n        if initially_pending {\n            reactive_graph::spawn_local_scoped({\n                let state = Rc::clone(&inner);\n                async move {\n                    let _guard = error_hook.as_ref().map(|hook| {\n                        throw_error::set_error_hook(Arc::clone(hook))\n                    });\n\n                    let value = fut.as_mut().await;\n                    drop(id);\n\n                    if let Ok(value) = value {\n                        Some(value).rebuild(&mut *state.borrow_mut());\n                    }\n\n                    subscriber.forward();\n                }\n            });\n        } else {\n            subscriber.forward();\n        }\n\n        SuspendState { inner }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let Self { subscriber, inner } = self;\n\n        // create a Future that will be aborted on on_cleanup\n        // this prevents trying to access signals or other resources inside the Suspend, after the\n        // await, if they have already been cleaned up\n        let (abort_handle, abort_registration) = AbortHandle::new_pair();\n        let fut = Abortable::new(inner, abort_registration);\n        on_cleanup(move || abort_handle.abort());\n\n        // get a unique ID if there's a SuspenseContext\n        let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());\n        let error_hook = use_context::<Arc<dyn ErrorHook>>();\n\n        // spawn the future, and rebuild the state when it resolves\n        reactive_graph::spawn_local_scoped({\n            let state = Rc::clone(&state.inner);\n            async move {\n                let _guard = error_hook\n                    .as_ref()\n                    .map(|hook| throw_error::set_error_hook(Arc::clone(hook)));\n\n                let value = fut.await;\n                drop(id);\n\n                // waiting a tick here allows Suspense to remount if necessary, which prevents some\n                // edge cases in which a rebuild can't happen while unmounted because the DOM node\n                // has no parent\n                Executor::tick().await;\n                if let Ok(value) = value {\n                    Some(value).rebuild(&mut *state.borrow_mut());\n                }\n\n                subscriber.forward();\n            }\n        });\n    }\n}\n\nimpl<T> AddAnyAttr for Suspend<T>\nwhere\n    T: Send + AddAnyAttr + 'static,\n{\n    type Output<SomeNewAttr: Attribute> =\n        Suspend<<T as AddAnyAttr>::Output<SomeNewAttr::CloneableOwned>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let attr = attr.into_cloneable_owned();\n        Suspend::new(async move {\n            let this = self.inner.await;\n            this.add_any_attr(attr)\n        })\n    }\n}\n\nimpl<T> RenderHtml for Suspend<T>\nwhere\n    T: RenderHtml + Sized + 'static,\n{\n    type AsyncOutput = Option<T>;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = T::MIN_LENGTH;\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // TODO wrap this with a Suspense as needed\n        // currently this is just used for Routes, which creates a Suspend but never actually needs\n        // it (because we don't lazy-load routes on the server)\n        if let Some(inner) = self.inner.now_or_never() {\n            inner.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let mut fut = Box::pin(self.inner);\n        match fut.as_mut().now_or_never() {\n            Some(inner) => inner.to_html_async_with_buf::<OUT_OF_ORDER>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            ),\n            None => {\n                if use_context::<SuspenseContext>().is_none() {\n                    buf.next_id();\n                    let (local_tx, mut local_rx) =\n                        futures::channel::oneshot::channel::<()>();\n                    provide_context(LocalResourceNotifier::from(local_tx));\n                    let mut fut = fut.fuse();\n                    let fut = async move {\n                        select! {\n                            _  = local_rx => None,\n                            value = fut => Some(value)\n                        }\n                    };\n                    let id = buf.clone_id();\n\n                    // out-of-order streams immediately push fallback,\n                    // wrapped by suspense markers\n                    if OUT_OF_ORDER {\n                        let mut fallback_position = *position;\n                        buf.push_fallback::<()>(\n                            (),\n                            &mut fallback_position,\n                            mark_branches,\n                            extra_attrs.clone(),\n                        );\n\n                        // TODO in 0.8: this should include a nonce\n                        // we do have access to nonces via context (because this is the `reactive_graph` module)\n                        // but unfortunately the Nonce type is defined in `leptos`, not in `tachys`\n                        //\n                        // missing it here only affects top-level Suspend, not Suspense components\n                        buf.push_async_out_of_order(\n                            fut,\n                            position,\n                            mark_branches,\n                            extra_attrs,\n                        );\n                    } else {\n                        buf.push_async({\n                            let mut position = *position;\n                            async move {\n                                let value = fut.await;\n                                let mut builder = StreamBuilder::new(id);\n                                value.to_html_async_with_buf::<OUT_OF_ORDER>(\n                                    &mut builder,\n                                    &mut position,\n                                    escape,\n                                    mark_branches,\n                                    extra_attrs,\n                                );\n                                builder.finish().take_chunks()\n                            }\n                        });\n                        *position = Position::NextChild;\n                    }\n                }\n            }\n        }\n    }\n\n    // TODO cancellation\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let Self { subscriber, inner } = self;\n\n        // create a Future that will be aborted on on_cleanup\n        // this prevents trying to access signals or other resources inside the Suspend, after the\n        // await, if they have already been cleaned up\n        let (abort_handle, abort_registration) = AbortHandle::new_pair();\n        let mut fut = Box::pin(Abortable::new(inner, abort_registration));\n        on_cleanup(move || abort_handle.abort());\n\n        // poll the future once immediately\n        // if it's already available, start in the ready state\n        // otherwise, start with the fallback\n        let initial = fut.as_mut().now_or_never().and_then(Result::ok);\n        let initially_pending = initial.is_none();\n        let inner = Rc::new(RefCell::new(\n            initial.hydrate::<FROM_SERVER>(cursor, position),\n        ));\n\n        // get a unique ID if there's a SuspenseContext\n        let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());\n        let error_hook = use_context::<Arc<dyn ErrorHook>>();\n\n        // if the initial state was pending, spawn a future to wait for it\n        // spawning immediately means that our now_or_never poll result isn't lost\n        // if it wasn't pending at first, we don't need to poll the Future again\n        if initially_pending {\n            reactive_graph::spawn_local_scoped({\n                let state = Rc::clone(&inner);\n                async move {\n                    let _guard = error_hook.as_ref().map(|hook| {\n                        throw_error::set_error_hook(Arc::clone(hook))\n                    });\n\n                    let value = fut.as_mut().await;\n                    drop(id);\n\n                    if let Ok(value) = value {\n                        Some(value).rebuild(&mut *state.borrow_mut());\n                    }\n\n                    subscriber.forward();\n                }\n            });\n        } else {\n            subscriber.forward();\n        }\n\n        SuspendState { inner }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        Some(self.inner.await)\n    }\n\n    fn dry_resolve(&mut self) {\n        // this is a little crazy, but if a Suspend is immediately available, we end up\n        // with a situation where polling it multiple times (here in dry_resolve and then in\n        // resolve) causes a runtime panic\n        // (see https://github.com/leptos-rs/leptos/issues/3113)\n        //\n        // at the same time, we do need to dry_resolve Suspend so that we can register synchronous\n        // resource reads that happen inside them\n        // (see https://github.com/leptos-rs/leptos/issues/2917)\n        //\n        // fuse()-ing the Future doesn't work, because that will cause the subsequent resolve()\n        // simply to be pending forever\n        //\n        // in this case, though, we can simply... discover that the data are already here, and then\n        // stuff them back into a new Future, which can safely be polled after its completion\n        if let Some(mut inner) = self.inner.as_mut().now_or_never() {\n            inner.dry_resolve();\n            self.inner = Box::pin(async move { inner })\n                as Pin<Box<dyn Future<Output = T> + Send>>;\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n"
  },
  {
    "path": "tachys/src/renderer/dom.rs",
    "content": "#![allow(missing_docs)]\n\n//! See [`Renderer`](crate::renderer::Renderer) and [`Rndr`](crate::renderer::Rndr) for additional information.\n\nuse super::{CastFrom, RemoveEventHandler};\nuse crate::{\n    dom::{document, window},\n    ok_or_debug, or_debug,\n    view::{Mountable, ToTemplate},\n};\nuse rustc_hash::FxHashSet;\nuse std::{\n    any::TypeId,\n    borrow::Cow,\n    cell::{LazyCell, RefCell},\n};\nuse wasm_bindgen::{intern, prelude::Closure, JsCast, JsValue};\nuse web_sys::{AddEventListenerOptions, Comment, HtmlTemplateElement};\n\n/// A [`Renderer`](crate::renderer::Renderer) that uses `web-sys` to manipulate DOM elements in the browser.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]\npub struct Dom;\n\nthread_local! {\n    pub(crate) static GLOBAL_EVENTS: RefCell<FxHashSet<Cow<'static, str>>> = Default::default();\n    pub static TEMPLATE_CACHE: RefCell<Vec<(Cow<'static, str>, web_sys::Element)>> = Default::default();\n}\n\npub type Node = web_sys::Node;\npub type Text = web_sys::Text;\npub type Element = web_sys::Element;\npub type Placeholder = web_sys::Comment;\npub type Event = wasm_bindgen::JsValue;\npub type ClassList = web_sys::DomTokenList;\npub type CssStyleDeclaration = web_sys::CssStyleDeclaration;\npub type TemplateElement = web_sys::HtmlTemplateElement;\n\n/// A microtask is a short function which will run after the current task has\n/// completed its work and when there is no other code waiting to be run before\n/// control of the execution context is returned to the browser's event loop.\n///\n/// Microtasks are especially useful for libraries and frameworks that need\n/// to perform final cleanup or other just-before-rendering tasks.\n///\n/// [MDN queueMicrotask](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)\npub fn queue_microtask(task: impl FnOnce() + 'static) {\n    use js_sys::{Function, Reflect};\n\n    let task = Closure::once_into_js(task);\n    let window = window();\n    let queue_microtask =\n        Reflect::get(&window, &JsValue::from_str(\"queueMicrotask\"))\n            .expect(\"queueMicrotask not available\");\n    let queue_microtask = queue_microtask.unchecked_into::<Function>();\n    _ = queue_microtask.call1(&JsValue::UNDEFINED, &task);\n}\n\nfn queue(fun: Box<dyn FnOnce()>) {\n    use std::cell::{Cell, RefCell};\n\n    thread_local! {\n        static PENDING: Cell<bool> = const { Cell::new(false) };\n        static QUEUE: RefCell<Vec<Box<dyn FnOnce()>>> = RefCell::new(Vec::new());\n    }\n\n    QUEUE.with_borrow_mut(|q| q.push(fun));\n    if !PENDING.replace(true) {\n        queue_microtask(|| {\n            let tasks = QUEUE.take();\n            for task in tasks {\n                task();\n            }\n            PENDING.set(false);\n        })\n    }\n}\n\nimpl Dom {\n    pub fn intern(text: &str) -> &str {\n        intern(text)\n    }\n\n    pub fn create_element(tag: &str, namespace: Option<&str>) -> Element {\n        if let Some(namespace) = namespace {\n            document()\n                .create_element_ns(\n                    Some(Self::intern(namespace)),\n                    Self::intern(tag),\n                )\n                .unwrap()\n        } else {\n            document().create_element(Self::intern(tag)).unwrap()\n        }\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn create_text_node(text: &str) -> Text {\n        document().create_text_node(text)\n    }\n\n    pub fn create_placeholder() -> Placeholder {\n        thread_local! {\n            static COMMENT: LazyCell<Comment> = LazyCell::new(|| {\n                document().create_comment(\"\")\n            });\n        }\n        COMMENT.with(|n| n.clone_node().unwrap().unchecked_into())\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn set_text(node: &Text, text: &str) {\n        node.set_node_value(Some(text));\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn set_attribute(node: &Element, name: &str, value: &str) {\n        or_debug!(node.set_attribute(name, value), node, \"setAttribute\");\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn remove_attribute(node: &Element, name: &str) {\n        or_debug!(node.remove_attribute(name), node, \"removeAttribute\");\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn insert_node(\n        parent: &Element,\n        new_child: &Node,\n        anchor: Option<&Node>,\n    ) {\n        ok_or_debug!(\n            parent.insert_before(new_child, anchor),\n            parent,\n            \"insertNode\"\n        );\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn try_insert_node(\n        parent: &Element,\n        new_child: &Node,\n        anchor: Option<&Node>,\n    ) -> bool {\n        parent.insert_before(new_child, anchor).is_ok()\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn remove_node(parent: &Element, child: &Node) -> Option<Node> {\n        ok_or_debug!(parent.remove_child(child), parent, \"removeNode\")\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn remove(node: &Node) {\n        node.unchecked_ref::<Element>().remove();\n    }\n\n    pub fn get_parent(node: &Node) -> Option<Node> {\n        node.parent_node()\n    }\n\n    pub fn first_child(node: &Node) -> Option<Node> {\n        #[cfg(debug_assertions)]\n        {\n            let node = node.first_child();\n            // if it's a comment node that starts with hot-reload, it's a marker that should be\n            // ignored\n            if let Some(node) = node.as_ref() {\n                if node.node_type() == 8\n                    && node\n                        .text_content()\n                        .unwrap_or_default()\n                        .starts_with(\"hot-reload\")\n                {\n                    return Self::next_sibling(node);\n                }\n            }\n\n            node\n        }\n        #[cfg(not(debug_assertions))]\n        {\n            node.first_child()\n        }\n    }\n\n    pub fn next_sibling(node: &Node) -> Option<Node> {\n        #[cfg(debug_assertions)]\n        {\n            let node = node.next_sibling();\n            // if it's a comment node that starts with hot-reload, it's a marker that should be\n            // ignored\n            if let Some(node) = node.as_ref() {\n                if node.node_type() == 8\n                    && node\n                        .text_content()\n                        .unwrap_or_default()\n                        .starts_with(\"hot-reload\")\n                {\n                    return Self::next_sibling(node);\n                }\n            }\n\n            node\n        }\n        #[cfg(not(debug_assertions))]\n        {\n            node.next_sibling()\n        }\n    }\n\n    pub fn log_node(node: &Node) {\n        web_sys::console::log_1(node);\n    }\n\n    #[cfg_attr(feature = \"tracing\", tracing::instrument(level = \"trace\"))]\n    pub fn clear_children(parent: &Element) {\n        parent.set_text_content(Some(\"\"));\n    }\n\n    /// Mounts the new child before the marker as its sibling.\n    ///\n    /// ## Panics\n    /// The default implementation panics if `before` does not have a parent [`crate::renderer::types::Element`].\n    pub fn mount_before<M>(new_child: &mut M, before: &Node)\n    where\n        M: Mountable,\n    {\n        let parent = Element::cast_from(\n            Self::get_parent(before).expect(\"could not find parent element\"),\n        )\n        .expect(\"placeholder parent should be Element\");\n        new_child.mount(&parent, Some(before));\n    }\n\n    /// Tries to mount the new child before the marker as its sibling.\n    ///\n    /// Returns `false` if the child did not have a valid parent.\n    #[track_caller]\n    pub fn try_mount_before<M>(new_child: &mut M, before: &Node) -> bool\n    where\n        M: Mountable,\n    {\n        if let Some(parent) =\n            Self::get_parent(before).and_then(Element::cast_from)\n        {\n            new_child.mount(&parent, Some(before));\n            true\n        } else {\n            false\n        }\n    }\n\n    pub fn set_property_or_value(el: &Element, key: &str, value: &JsValue) {\n        if key == \"value\" {\n            queue(Box::new({\n                let el = el.clone();\n                let value = value.clone();\n                move || {\n                    Self::set_property(&el, \"value\", &value);\n                }\n            }))\n        } else {\n            Self::set_property(el, key, value);\n        }\n    }\n\n    pub fn set_property(el: &Element, key: &str, value: &JsValue) {\n        or_debug!(\n            js_sys::Reflect::set(\n                el,\n                &wasm_bindgen::JsValue::from_str(key),\n                value,\n            ),\n            el,\n            \"setProperty\"\n        );\n    }\n\n    pub fn add_event_listener(\n        el: &Element,\n        name: &str,\n        cb: Box<dyn FnMut(Event)>,\n    ) -> RemoveEventHandler<Element> {\n        let cb = wasm_bindgen::closure::Closure::wrap(cb);\n        let name = intern(name);\n        or_debug!(\n            el.add_event_listener_with_callback(\n                name,\n                cb.as_ref().unchecked_ref()\n            ),\n            el,\n            \"addEventListener\"\n        );\n\n        // return the remover\n        RemoveEventHandler::new({\n            let name = name.to_owned();\n            let el = el.clone();\n            // safe to construct this here, because it will only run in the browser\n            // so it will always be accessed or dropped from the main thread\n            let cb = send_wrapper::SendWrapper::new(move || {\n                or_debug!(\n                    el.remove_event_listener_with_callback(\n                        intern(&name),\n                        cb.as_ref().unchecked_ref()\n                    ),\n                    &el,\n                    \"removeEventListener\"\n                )\n            });\n            move || cb()\n        })\n    }\n\n    pub fn add_event_listener_use_capture(\n        el: &Element,\n        name: &str,\n        cb: Box<dyn FnMut(Event)>,\n    ) -> RemoveEventHandler<Element> {\n        let cb = wasm_bindgen::closure::Closure::wrap(cb);\n        let name = intern(name);\n        let options = AddEventListenerOptions::new();\n        options.set_capture(true);\n        or_debug!(\n            el.add_event_listener_with_callback_and_add_event_listener_options(\n                name,\n                cb.as_ref().unchecked_ref(),\n                &options\n            ),\n            el,\n            \"addEventListenerUseCapture\"\n        );\n\n        // return the remover\n        RemoveEventHandler::new({\n            let name = name.to_owned();\n            let el = el.clone();\n            // safe to construct this here, because it will only run in the browser\n            // so it will always be accessed or dropped from the main thread\n            let cb = send_wrapper::SendWrapper::new(move || {\n                or_debug!(\n                    el.remove_event_listener_with_callback_and_bool(\n                        intern(&name),\n                        cb.as_ref().unchecked_ref(),\n                        true\n                    ),\n                    &el,\n                    \"removeEventListener\"\n                )\n            });\n            move || cb()\n        })\n    }\n\n    pub fn event_target<T>(ev: &Event) -> T\n    where\n        T: CastFrom<Element>,\n    {\n        let el = ev\n            .unchecked_ref::<web_sys::Event>()\n            .target()\n            .expect(\"event.target not found\")\n            .unchecked_into::<Element>();\n        T::cast_from(el).expect(\"incorrect element type\")\n    }\n\n    pub fn add_event_listener_delegated(\n        el: &Element,\n        name: Cow<'static, str>,\n        delegation_key: Cow<'static, str>,\n        cb: Box<dyn FnMut(Event)>,\n    ) -> RemoveEventHandler<Element> {\n        let cb = Closure::wrap(cb);\n        let key = intern(&delegation_key);\n        or_debug!(\n            js_sys::Reflect::set(el, &JsValue::from_str(key), cb.as_ref()),\n            el,\n            \"set property\"\n        );\n\n        GLOBAL_EVENTS.with_borrow_mut(|events| {\n            if !events.contains(&name) {\n                // create global handler\n                let key = JsValue::from_str(key);\n                let handler = move |ev: web_sys::Event| {\n                    let target = ev.target();\n                    let node = ev.composed_path().get(0);\n                    let mut node = if node.is_undefined() || node.is_null() {\n                        JsValue::from(target)\n                    } else {\n                        node\n                    };\n\n                    // TODO reverse Shadow DOM retargetting\n                    // TODO simulate currentTarget\n\n                    while !node.is_null() {\n                        let node_is_disabled = js_sys::Reflect::get(\n                            &node,\n                            &JsValue::from_str(\"disabled\"),\n                        )\n                        .unwrap()\n                        .is_truthy();\n                        if !node_is_disabled {\n                            let maybe_handler =\n                                js_sys::Reflect::get(&node, &key).unwrap();\n                            if !maybe_handler.is_undefined() {\n                                let f = maybe_handler\n                                    .unchecked_ref::<js_sys::Function>();\n                                let _ = f.call1(&node, &ev);\n\n                                if ev.cancel_bubble() {\n                                    return;\n                                }\n                            }\n                        }\n\n                        // navigate up tree\n                        if let Some(parent) =\n                            node.unchecked_ref::<web_sys::Node>().parent_node()\n                        {\n                            node = parent.into()\n                        } else if let Some(root) =\n                            node.dyn_ref::<web_sys::ShadowRoot>()\n                        {\n                            node = root.host().unchecked_into();\n                        } else {\n                            node = JsValue::null()\n                        }\n                    }\n                };\n\n                let handler =\n                    Box::new(handler) as Box<dyn FnMut(web_sys::Event)>;\n                let handler = Closure::wrap(handler).into_js_value();\n                window()\n                    .add_event_listener_with_callback(\n                        &name,\n                        handler.unchecked_ref(),\n                    )\n                    .unwrap();\n\n                // register that we've created handler\n                events.insert(name);\n            }\n        });\n\n        // return the remover\n        RemoveEventHandler::new({\n            let key = key.to_owned();\n            let el = el.clone();\n            // safe to construct this here, because it will only run in the browser\n            // so it will always be accessed or dropped from the main thread\n            let el_cb = send_wrapper::SendWrapper::new((el, cb));\n            move || {\n                let (el, cb) = el_cb.take();\n                drop(cb);\n                or_debug!(\n                    js_sys::Reflect::delete_property(\n                        &el,\n                        &JsValue::from_str(&key)\n                    ),\n                    &el,\n                    \"delete property\"\n                );\n            }\n        })\n    }\n\n    pub fn class_list(el: &Element) -> ClassList {\n        el.class_list()\n    }\n\n    pub fn add_class(list: &ClassList, name: &str) {\n        or_debug!(list.add_1(name), list.unchecked_ref(), \"add()\");\n    }\n\n    pub fn remove_class(list: &ClassList, name: &str) {\n        or_debug!(list.remove_1(name), list.unchecked_ref(), \"remove()\");\n    }\n\n    pub fn style(el: &Element) -> CssStyleDeclaration {\n        el.unchecked_ref::<web_sys::HtmlElement>().style()\n    }\n\n    pub fn set_css_property(\n        style: &CssStyleDeclaration,\n        name: &str,\n        value: &str,\n    ) {\n        or_debug!(\n            style.set_property(name, value),\n            style.unchecked_ref(),\n            \"setProperty\"\n        );\n    }\n\n    pub fn remove_css_property(style: &CssStyleDeclaration, name: &str) {\n        or_debug!(\n            style.remove_property(name),\n            style.unchecked_ref(),\n            \"removeProperty\"\n        );\n    }\n\n    pub fn set_inner_html(el: &Element, html: &str) {\n        el.set_inner_html(html);\n    }\n\n    pub fn get_template<V>() -> TemplateElement\n    where\n        V: ToTemplate + 'static,\n    {\n        thread_local! {\n            static TEMPLATE_ELEMENT: LazyCell<HtmlTemplateElement> =\n                LazyCell::new(|| document().create_element(Dom::intern(\"template\")).unwrap().unchecked_into());\n            static TEMPLATES: RefCell<Vec<(TypeId, HtmlTemplateElement)>> = Default::default();\n        }\n\n        TEMPLATES.with_borrow_mut(|t| {\n            let id = TypeId::of::<V>();\n            t.iter()\n                .find_map(|entry| (entry.0 == id).then(|| entry.1.clone()))\n                .unwrap_or_else(|| {\n                    let tpl = TEMPLATE_ELEMENT.with(|t| {\n                        t.clone_node()\n                            .unwrap()\n                            .unchecked_into::<HtmlTemplateElement>()\n                    });\n                    let mut buf = String::new();\n                    V::to_template(\n                        &mut buf,\n                        &mut String::new(),\n                        &mut String::new(),\n                        &mut String::new(),\n                        &mut Default::default(),\n                    );\n                    tpl.set_inner_html(&buf);\n                    t.push((id, tpl.clone()));\n                    tpl\n                })\n        })\n    }\n\n    pub fn clone_template(tpl: &TemplateElement) -> Element {\n        tpl.content()\n            .clone_node_with_deep(true)\n            .unwrap()\n            .unchecked_into()\n    }\n\n    pub fn create_element_from_html(html: Cow<'static, str>) -> Element {\n        let tpl = TEMPLATE_CACHE.with_borrow_mut(|cache| {\n            if let Some(tpl_content) = cache.iter().find_map(|(key, tpl)| {\n                (html == *key)\n                    .then_some(Self::clone_template(tpl.unchecked_ref()))\n            }) {\n                tpl_content\n            } else {\n                let tpl = document()\n                    .create_element(Self::intern(\"template\"))\n                    .unwrap();\n                tpl.set_inner_html(&html);\n                let tpl_content = Self::clone_template(tpl.unchecked_ref());\n                cache.push((html, tpl));\n                tpl_content\n            }\n        });\n        tpl.first_element_child().unwrap_or(tpl)\n    }\n\n    pub fn create_svg_element_from_html(html: Cow<'static, str>) -> Element {\n        let tpl = TEMPLATE_CACHE.with_borrow_mut(|cache| {\n            if let Some(tpl_content) = cache.iter().find_map(|(key, tpl)| {\n                (html == *key)\n                    .then_some(Self::clone_template(tpl.unchecked_ref()))\n            }) {\n                tpl_content\n            } else {\n                let tpl = document()\n                    .create_element(Self::intern(\"template\"))\n                    .unwrap();\n                let svg = document()\n                    .create_element_ns(\n                        Some(Self::intern(\"http://www.w3.org/2000/svg\")),\n                        Self::intern(\"svg\"),\n                    )\n                    .unwrap();\n                let g = document()\n                    .create_element_ns(\n                        Some(Self::intern(\"http://www.w3.org/2000/svg\")),\n                        Self::intern(\"g\"),\n                    )\n                    .unwrap();\n                g.set_inner_html(&html);\n                svg.append_child(&g).unwrap();\n                tpl.unchecked_ref::<TemplateElement>()\n                    .content()\n                    .append_child(&svg)\n                    .unwrap();\n                let tpl_content = Self::clone_template(tpl.unchecked_ref());\n                cache.push((html, tpl));\n                tpl_content\n            }\n        });\n\n        let svg = tpl.first_element_child().unwrap();\n        svg.first_element_child().unwrap_or(svg)\n    }\n}\n\nimpl Mountable for Node {\n    fn unmount(&mut self) {\n        todo!()\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        Dom::insert_node(parent, self, marker);\n    }\n\n    fn try_mount(&mut self, parent: &Element, marker: Option<&Node>) -> bool {\n        Dom::try_insert_node(parent, self, marker)\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        let parent = Dom::get_parent(self).and_then(Element::cast_from);\n        if let Some(parent) = parent {\n            child.mount(&parent, Some(self));\n            return true;\n        }\n        false\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\nimpl Mountable for Text {\n    fn unmount(&mut self) {\n        self.remove();\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        Dom::insert_node(parent, self, marker);\n    }\n\n    fn try_mount(&mut self, parent: &Element, marker: Option<&Node>) -> bool {\n        Dom::try_insert_node(parent, self, marker)\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        let parent =\n            Dom::get_parent(self.as_ref()).and_then(Element::cast_from);\n        if let Some(parent) = parent {\n            child.mount(&parent, Some(self));\n            return true;\n        }\n        false\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\nimpl Mountable for Comment {\n    fn unmount(&mut self) {\n        self.remove();\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        Dom::insert_node(parent, self, marker);\n    }\n\n    fn try_mount(&mut self, parent: &Element, marker: Option<&Node>) -> bool {\n        Dom::try_insert_node(parent, self, marker)\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        let parent =\n            Dom::get_parent(self.as_ref()).and_then(Element::cast_from);\n        if let Some(parent) = parent {\n            child.mount(&parent, Some(self));\n            return true;\n        }\n        false\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\nimpl Mountable for Element {\n    fn unmount(&mut self) {\n        self.remove();\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        Dom::insert_node(parent, self, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        let parent =\n            Dom::get_parent(self.as_ref()).and_then(Element::cast_from);\n        if let Some(parent) = parent {\n            child.mount(&parent, Some(self));\n            return true;\n        }\n        false\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![self.clone()]\n    }\n}\n\nimpl CastFrom<Node> for Text {\n    fn cast_from(node: Node) -> Option<Text> {\n        node.clone().dyn_into().ok()\n    }\n}\n\nimpl CastFrom<Node> for Comment {\n    fn cast_from(node: Node) -> Option<Comment> {\n        node.clone().dyn_into().ok()\n    }\n}\n\nimpl CastFrom<Node> for Element {\n    fn cast_from(node: Node) -> Option<Element> {\n        node.clone().dyn_into().ok()\n    }\n}\n\nimpl<T> CastFrom<JsValue> for T\nwhere\n    T: JsCast,\n{\n    fn cast_from(source: JsValue) -> Option<Self> {\n        source.dyn_into::<T>().ok()\n    }\n}\n\nimpl<T> CastFrom<Element> for T\nwhere\n    T: JsCast,\n{\n    fn cast_from(source: Element) -> Option<Self> {\n        source.dyn_into::<T>().ok()\n    }\n}\n"
  },
  {
    "path": "tachys/src/renderer/mock_dom.rs",
    "content": "#![allow(unused)]\n\n//! A stupidly-simple mock DOM implementation that can be used for testing.\n//!\n//! Do not use this for anything real.\n\nuse super::{CastFrom, DomRenderer, RemoveEventHandler, Renderer};\nuse crate::{\n    html::element::{CreateElement, ElementType},\n    view::Mountable,\n};\nuse indexmap::IndexMap;\nuse slotmap::{new_key_type, SlotMap};\nuse std::{borrow::Cow, cell::RefCell, rc::Rc};\nuse wasm_bindgen::JsValue;\n\n/// A [`Renderer`] that uses a mock DOM structure running in Rust code.\n///\n/// This is intended as a rendering background that can be used to test component logic, without\n/// running a browser.\n#[derive(Debug)]\npub struct MockDom;\n\nnew_key_type! {\n    /// A unique identifier for a mock DOM node.\n    pub struct NodeId;\n}\n\n/// A mock DOM node.\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\npub struct Node(NodeId);\n\n/// A mock element.\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\npub struct Element(Node);\n\n/// A mock text node.\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\npub struct Text(Node);\n\n/// A mock comment node.\n#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]\npub struct Placeholder(Node);\n\nimpl AsRef<Node> for Node {\n    fn as_ref(&self) -> &Node {\n        self\n    }\n}\n\nimpl AsRef<Node> for Element {\n    fn as_ref(&self) -> &Node {\n        &self.0\n    }\n}\n\nimpl AsRef<Node> for Text {\n    fn as_ref(&self) -> &Node {\n        &self.0\n    }\n}\n\nimpl AsRef<Node> for Placeholder {\n    fn as_ref(&self) -> &Node {\n        &self.0\n    }\n}\n\n/// Tests whether two nodes are references to the same underlying node.\npub fn node_eq(a: impl AsRef<Node>, b: impl AsRef<Node>) -> bool {\n    a.as_ref() == b.as_ref()\n}\n\nimpl From<Text> for Node {\n    fn from(value: Text) -> Self {\n        Node(value.0 .0)\n    }\n}\n\nimpl From<Element> for Node {\n    fn from(value: Element) -> Self {\n        Node(value.0 .0)\n    }\n}\n\nimpl From<Placeholder> for Node {\n    fn from(value: Placeholder) -> Self {\n        Node(value.0 .0)\n    }\n}\n\nimpl Element {\n    /// Outputs an HTML form of the element, for testing and debugging purposes.\n    pub fn to_debug_html(&self) -> String {\n        let mut buf = String::new();\n        self.debug_html(&mut buf);\n        buf\n    }\n}\n\n/// The DOM data associated with a particular node.\n#[derive(Debug, PartialEq, Eq)]\npub struct NodeData {\n    /// The node's parent.\n    pub parent: Option<NodeId>,\n    /// The node itself.\n    pub ty: NodeType,\n}\n\ntrait DebugHtml {\n    fn debug_html(&self, buf: &mut String);\n}\n\nimpl DebugHtml for Element {\n    fn debug_html(&self, buf: &mut String) {\n        Document::with_node(self.0 .0, |node| {\n            node.debug_html(buf);\n        });\n    }\n}\n\nimpl DebugHtml for Text {\n    fn debug_html(&self, buf: &mut String) {\n        Document::with_node(self.0 .0, |node| {\n            node.debug_html(buf);\n        });\n    }\n}\n\nimpl DebugHtml for Node {\n    fn debug_html(&self, buf: &mut String) {\n        Document::with_node(self.0, |node| {\n            node.debug_html(buf);\n        });\n    }\n}\n\nimpl DebugHtml for NodeData {\n    fn debug_html(&self, buf: &mut String) {\n        match &self.ty {\n            NodeType::Text(text) => buf.push_str(text),\n            NodeType::Element {\n                tag,\n                attrs,\n                children,\n            } => {\n                buf.push('<');\n                buf.push_str(tag);\n                for (k, v) in attrs {\n                    buf.push(' ');\n                    buf.push_str(k);\n                    buf.push_str(\"=\\\"\");\n                    buf.push_str(v);\n                    buf.push('\"');\n                }\n                buf.push('>');\n\n                for child in children {\n                    child.debug_html(buf);\n                }\n\n                buf.push_str(\"</\");\n                buf.push_str(tag);\n                buf.push('>');\n            }\n            NodeType::Placeholder => buf.push_str(\"<!>\"),\n        }\n    }\n}\n\n/// The mock DOM document.\n#[derive(Clone)]\npub struct Document(Rc<RefCell<SlotMap<NodeId, NodeData>>>);\n\nimpl Document {\n    /// Creates a new document.\n    pub fn new() -> Self {\n        Document(Default::default())\n    }\n\n    fn with_node<U>(id: NodeId, f: impl FnOnce(&NodeData) -> U) -> Option<U> {\n        DOCUMENT.with(|d| {\n            let data = d.0.borrow();\n            let data = data.get(id);\n            data.map(f)\n        })\n    }\n\n    fn with_node_mut<U>(\n        id: NodeId,\n        f: impl FnOnce(&mut NodeData) -> U,\n    ) -> Option<U> {\n        DOCUMENT.with(|d| {\n            let mut data = d.0.borrow_mut();\n            let data = data.get_mut(id);\n            data.map(f)\n        })\n    }\n\n    /// Resets the document's contents.\n    pub fn reset(&self) {\n        self.0.borrow_mut().clear();\n    }\n\n    fn create_element(&self, tag: &str) -> Element {\n        Element(Node(self.0.borrow_mut().insert(NodeData {\n            parent: None,\n            ty: NodeType::Element {\n                tag: tag.to_string().into(),\n                attrs: IndexMap::new(),\n                children: Vec::new(),\n            },\n        })))\n    }\n\n    fn create_text_node(&self, data: &str) -> Text {\n        Text(Node(self.0.borrow_mut().insert(NodeData {\n            parent: None,\n            ty: NodeType::Text(data.to_string()),\n        })))\n    }\n\n    fn create_placeholder(&self) -> Placeholder {\n        Placeholder(Node(self.0.borrow_mut().insert(NodeData {\n            parent: None,\n            ty: NodeType::Placeholder,\n        })))\n    }\n}\n\n// TODO!\nimpl DomRenderer for MockDom {\n    type Event = ();\n    type ClassList = ();\n    type CssStyleDeclaration = ();\n    type TemplateElement = ();\n\n    fn set_property(el: &Self::Element, key: &str, value: &JsValue) {\n        todo!()\n    }\n\n    fn add_event_listener(\n        el: &Self::Element,\n        name: &str,\n        cb: Box<dyn FnMut(Self::Event)>,\n    ) -> RemoveEventHandler<Self::Element> {\n        todo!()\n    }\n\n    fn add_event_listener_delegated(\n        el: &Self::Element,\n        name: Cow<'static, str>,\n        delegation_key: Cow<'static, str>,\n        cb: Box<dyn FnMut(Self::Event)>,\n    ) -> RemoveEventHandler<Self::Element> {\n        todo!()\n    }\n\n    fn class_list(el: &Self::Element) -> Self::ClassList {\n        todo!()\n    }\n\n    fn add_class(class_list: &Self::ClassList, name: &str) {\n        todo!()\n    }\n\n    fn remove_class(class_list: &Self::ClassList, name: &str) {\n        todo!()\n    }\n\n    fn style(el: &Self::Element) -> Self::CssStyleDeclaration {\n        todo!()\n    }\n\n    fn set_css_property(\n        style: &Self::CssStyleDeclaration,\n        name: &str,\n        value: &str,\n    ) {\n        todo!()\n    }\n\n    fn set_inner_html(el: &Self::Element, html: &str) {\n        todo!()\n    }\n\n    fn event_target<T>(ev: &Self::Event) -> T\n    where\n        T: CastFrom<Self::Element>,\n    {\n        todo!()\n    }\n\n    fn get_template<V>() -> Self::TemplateElement\n    where\n        V: crate::view::ToTemplate + 'static,\n    {\n        todo!()\n    }\n\n    fn clone_template(tpl: &Self::TemplateElement) -> Self::Element {\n        todo!()\n    }\n\n    fn create_element_from_html(html: &str) -> Self::Element {\n        todo!()\n    }\n}\n\nimpl Default for Document {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nthread_local! {\n    static DOCUMENT: Document = Document::new();\n}\n\n/// Returns the global document.\npub fn document() -> Document {\n    DOCUMENT.with(Clone::clone)\n}\n\n/// The type of mock DOM node.\n#[derive(Debug, PartialEq, Eq)]\npub enum NodeType {\n    /// A text node.\n    Text(String),\n    /// An element.\n    Element {\n        /// The HTML tag name.\n        tag: Cow<'static, str>,\n        /// The attributes.\n        attrs: IndexMap<String, String>,\n        /// The element's children.\n        children: Vec<Node>,\n    },\n    /// A placeholder.\n    Placeholder,\n}\n\nimpl Mountable<MockDom> for Node {\n    fn unmount(&mut self) {\n        todo!()\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        MockDom::insert_node(parent, self, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable<MockDom>) -> bool {\n        let parent = MockDom::get_parent(self).and_then(Element::cast_from);\n        if let Some(parent) = parent {\n            child.mount(&parent, Some(self));\n            return true;\n        }\n        false\n    }\n}\n\nimpl Mountable<MockDom> for Text {\n    fn unmount(&mut self) {\n        todo!()\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        MockDom::insert_node(parent, self.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable<MockDom>) -> bool {\n        let parent =\n            MockDom::get_parent(self.as_ref()).and_then(Element::cast_from);\n        if let Some(parent) = parent {\n            child.mount(&parent, Some(self.as_ref()));\n            return true;\n        }\n        false\n    }\n}\n\nimpl Mountable<MockDom> for Element {\n    fn unmount(&mut self) {\n        todo!()\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        MockDom::insert_node(parent, self.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable<MockDom>) -> bool {\n        let parent =\n            MockDom::get_parent(self.as_ref()).and_then(Element::cast_from);\n        if let Some(parent) = parent {\n            child.mount(&parent, Some(self.as_ref()));\n            return true;\n        }\n        false\n    }\n}\n\nimpl Mountable<MockDom> for Placeholder {\n    fn unmount(&mut self) {\n        todo!()\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        MockDom::insert_node(parent, self.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable<MockDom>) -> bool {\n        let parent =\n            MockDom::get_parent(self.as_ref()).and_then(Element::cast_from);\n        if let Some(parent) = parent {\n            child.mount(&parent, Some(self.as_ref()));\n            return true;\n        }\n        false\n    }\n}\n\nimpl<E: ElementType> CreateElement<MockDom> for E {\n    fn create_element(&self) -> crate::renderer::types::Element {\n        document().create_element(E::TAG)\n    }\n}\n\nimpl Renderer for MockDom {\n    type Node = Node;\n    type Text = Text;\n    type Element = Element;\n    type Placeholder = Placeholder;\n\n    fn intern(text: &str) -> &str {\n        text\n    }\n\n    fn create_text_node(data: &str) -> Self::Text {\n        document().create_text_node(data)\n    }\n\n    fn create_placeholder() -> Self::Placeholder {\n        document().create_placeholder()\n    }\n\n    fn set_text(node: &Self::Text, text: &str) {\n        Document::with_node_mut(node.0 .0, |node| {\n            if let NodeType::Text(ref mut node) = node.ty {\n                *node = text.to_string();\n            }\n        });\n    }\n\n    fn set_attribute(node: &Self::Element, name: &str, value: &str) {\n        Document::with_node_mut(node.0 .0, |node| {\n            if let NodeType::Element { ref mut attrs, .. } = node.ty {\n                attrs.insert(name.to_string(), value.to_string());\n            }\n        });\n    }\n\n    fn remove_attribute(node: &Self::Element, name: &str) {\n        Document::with_node_mut(node.0 .0, |node| {\n            if let NodeType::Element { ref mut attrs, .. } = node.ty {\n                attrs.shift_remove(name);\n            }\n        });\n    }\n\n    fn insert_node(\n        parent: &Self::Element,\n        new_child: &Self::Node,\n        anchor: Option<&Self::Node>,\n    ) {\n        debug_assert!(&parent.0 != new_child);\n        // remove if already mounted\n        if let Some(parent) = MockDom::get_parent(new_child) {\n            let parent = Element(parent);\n            MockDom::remove_node(&parent, new_child);\n        }\n        // mount on new parent\n        Document::with_node_mut(parent.0 .0, |parent| {\n            if let NodeType::Element {\n                ref mut children, ..\n            } = parent.ty\n            {\n                match anchor {\n                    None => children.push(new_child.clone()),\n                    Some(anchor) => {\n                        let anchor_pos = children\n                            .iter()\n                            .position(|item| item.0 == anchor.0)\n                            .expect(\"anchor is not a child of the parent\");\n                        children.insert(anchor_pos, new_child.clone());\n                    }\n                }\n            } else {\n                panic!(\"parent is not an element\");\n            }\n        });\n        // set parent on child node\n        Document::with_node_mut(new_child.0, |node| {\n            node.parent = Some(parent.0 .0)\n        });\n    }\n\n    fn remove_node(\n        parent: &Self::Element,\n        child: &Self::Node,\n    ) -> Option<Self::Node> {\n        let child = Document::with_node_mut(parent.0 .0, |parent| {\n            if let NodeType::Element {\n                ref mut children, ..\n            } = parent.ty\n            {\n                let current_pos = children\n                    .iter()\n                    .position(|item| item.0 == child.0)\n                    .expect(\"anchor is not a child of the parent\");\n                Some(children.remove(current_pos))\n            } else {\n                None\n            }\n        })\n        .flatten()?;\n        Document::with_node_mut(child.0, |node| {\n            node.parent = None;\n        });\n        Some(child)\n    }\n\n    fn remove(node: &Self::Node) {\n        let parent = Element(Node(\n            Self::get_parent(node)\n                .expect(\"tried to remove a parentless node\")\n                .0,\n        ));\n        Self::remove_node(&parent, node);\n    }\n\n    fn get_parent(node: &Self::Node) -> Option<Self::Node> {\n        Document::with_node(node.0, |node| node.parent)\n            .flatten()\n            .map(Node)\n    }\n\n    fn first_child(node: &Self::Node) -> Option<Self::Node> {\n        Document::with_node(node.0, |node| match &node.ty {\n            NodeType::Text(_) => None,\n            NodeType::Element { children, .. } => children.first().cloned(),\n            NodeType::Placeholder => None,\n        })\n        .flatten()\n    }\n\n    fn next_sibling(node: &Self::Node) -> Option<Self::Node> {\n        let node_id = node.0;\n        Document::with_node(node_id, |node| {\n            node.parent.and_then(|parent| {\n                Document::with_node(parent, |parent| match &parent.ty {\n                    NodeType::Element { children, .. } => {\n                        let this = children\n                            .iter()\n                            .position(|check| check == &Node(node_id))?;\n                        children.get(this + 1).cloned()\n                    }\n                    _ => panic!(\n                        \"Called next_sibling with parent as a node that's not \\\n                         an Element.\"\n                    ),\n                })\n            })\n        })\n        .flatten()\n        .flatten()\n    }\n\n    fn log_node(node: &Self::Node) {\n        eprintln!(\"{node:?}\");\n    }\n\n    fn clear_children(parent: &Self::Element) {\n        let prev_children =\n            Document::with_node_mut(parent.0 .0, |node| match node.ty {\n                NodeType::Element {\n                    ref mut children, ..\n                } => std::mem::take(children),\n                _ => panic!(\"Called clear_children on a non-Element node.\"),\n            })\n            .unwrap_or_default();\n        for child in prev_children {\n            Document::with_node_mut(child.0, |node| {\n                node.parent = None;\n            });\n        }\n    }\n}\n\nimpl CastFrom<Node> for Text {\n    fn cast_from(source: Node) -> Option<Self> {\n        Document::with_node(source.0, |node| {\n            matches!(node.ty, NodeType::Text(_))\n        })\n        .and_then(|matches| matches.then_some(Text(Node(source.0))))\n    }\n}\n\nimpl CastFrom<Node> for Element {\n    fn cast_from(source: Node) -> Option<Self> {\n        Document::with_node(source.0, |node| {\n            matches!(node.ty, NodeType::Element { .. })\n        })\n        .and_then(|matches| matches.then_some(Element(Node(source.0))))\n    }\n}\n\nimpl CastFrom<Node> for Placeholder {\n    fn cast_from(source: Node) -> Option<Self> {\n        Document::with_node(source.0, |node| {\n            matches!(node.ty, NodeType::Placeholder)\n        })\n        .and_then(|matches| matches.then_some(Placeholder(Node(source.0))))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::MockDom;\n    use crate::{\n        html::element,\n        renderer::{mock_dom::node_eq, Renderer},\n    };\n\n    #[test]\n    fn html_debugging_works() {\n        let main = MockDom::create_element(element::Main);\n        let p = MockDom::create_element(element::P);\n        MockDom::set_attribute(&p, \"id\", \"foo\");\n        let text = MockDom::create_text_node(\"Hello, world!\");\n        MockDom::insert_node(&main, p.as_ref(), None);\n        MockDom::insert_node(&p, text.as_ref(), None);\n        assert_eq!(\n            main.to_debug_html(),\n            \"<main><p id=\\\"foo\\\">Hello, world!</p></main>\"\n        );\n    }\n\n    #[test]\n    fn remove_attribute_works() {\n        let main = MockDom::create_element(element::Main);\n        let p = MockDom::create_element(element::P);\n        MockDom::set_attribute(&p, \"id\", \"foo\");\n        let text = MockDom::create_text_node(\"Hello, world!\");\n        MockDom::insert_node(&main, p.as_ref(), None);\n        MockDom::insert_node(&p, text.as_ref(), None);\n        MockDom::remove_attribute(&p, \"id\");\n        assert_eq!(main.to_debug_html(), \"<main><p>Hello, world!</p></main>\");\n    }\n\n    #[test]\n    fn remove_node_works() {\n        let main = MockDom::create_element(element::Main);\n        let p = MockDom::create_element(element::P);\n        MockDom::set_attribute(&p, \"id\", \"foo\");\n        let text = MockDom::create_text_node(\"Hello, world!\");\n        MockDom::insert_node(&main, p.as_ref(), None);\n        MockDom::insert_node(&p, text.as_ref(), None);\n        MockDom::remove_node(&main, p.as_ref());\n        assert_eq!(main.to_debug_html(), \"<main></main>\");\n    }\n\n    #[test]\n    fn insert_before_works() {\n        let main = MockDom::create_element(element::Main);\n        let p = MockDom::create_element(element::P);\n        let span = MockDom::create_element(element::Span);\n        let text = MockDom::create_text_node(\"Hello, world!\");\n        MockDom::insert_node(&main, p.as_ref(), None);\n        MockDom::insert_node(&span, text.as_ref(), None);\n        MockDom::insert_node(&main, span.as_ref(), Some(p.as_ref()));\n        assert_eq!(\n            main.to_debug_html(),\n            \"<main><span>Hello, world!</span><p></p></main>\"\n        );\n    }\n\n    #[test]\n    fn insert_before_sets_parent() {\n        let main = MockDom::create_element(element::Main);\n        let p = MockDom::create_element(element::P);\n        MockDom::insert_node(&main, p.as_ref(), None);\n        let parent =\n            MockDom::get_parent(p.as_ref()).expect(\"p should have parent set\");\n        assert!(node_eq(parent, main));\n    }\n\n    #[test]\n    fn insert_before_moves_node() {\n        let main = MockDom::create_element(element::Main);\n        let p = MockDom::create_element(element::P);\n        let span = MockDom::create_element(element::Span);\n        let text = MockDom::create_text_node(\"Hello, world!\");\n        MockDom::insert_node(&main, p.as_ref(), None);\n        MockDom::insert_node(&span, text.as_ref(), None);\n        MockDom::insert_node(&main, span.as_ref(), Some(p.as_ref()));\n        MockDom::insert_node(&main, p.as_ref(), Some(span.as_ref()));\n        assert_eq!(\n            main.to_debug_html(),\n            \"<main><p></p><span>Hello, world!</span></main>\"\n        );\n    }\n\n    #[test]\n    fn first_child_gets_first_child() {\n        let main = MockDom::create_element(element::Main);\n        let p = MockDom::create_element(element::P);\n        let span = MockDom::create_element(element::Span);\n        MockDom::insert_node(&main, p.as_ref(), None);\n        MockDom::insert_node(&p, span.as_ref(), None);\n        assert_eq!(\n            MockDom::first_child(main.as_ref()).as_ref(),\n            Some(p.as_ref())\n        );\n        assert_eq!(\n            MockDom::first_child(&MockDom::first_child(main.as_ref()).unwrap())\n                .as_ref(),\n            Some(span.as_ref())\n        );\n    }\n\n    #[test]\n    fn next_sibling_gets_next_sibling() {\n        let main = MockDom::create_element(element::Main);\n        let p = MockDom::create_element(element::P);\n        let span = MockDom::create_element(element::Span);\n        let text = MockDom::create_text_node(\"foo\");\n        MockDom::insert_node(&main, p.as_ref(), None);\n        MockDom::insert_node(&main, span.as_ref(), None);\n        MockDom::insert_node(&main, text.as_ref(), None);\n        assert_eq!(\n            MockDom::next_sibling(p.as_ref()).as_ref(),\n            Some(span.as_ref())\n        );\n        assert_eq!(\n            MockDom::next_sibling(span.as_ref()).as_ref(),\n            Some(text.as_ref())\n        );\n    }\n}\n"
  },
  {
    "path": "tachys/src/renderer/mod.rs",
    "content": "use crate::view::{Mountable, ToTemplate};\nuse std::{borrow::Cow, fmt::Debug, marker::PhantomData};\nuse wasm_bindgen::JsValue;\n\n/// A DOM renderer.\npub mod dom;\n\n/// The renderer being used for the application.\n///\n/// ### Note\n/// This was designed to be included as a generic on view types, to support different rendering\n/// backends using the same view tree structure. However, adding the number of generics that was\n/// required to make this work caused catastrophic compile times and linker errors on larger\n/// applications, so this \"generic rendering\" approach was removed before 0.7.0 release.\n///\n/// It is possible that we will try a different approach to achieve the same functionality in the\n/// future, so to the extent possible the rest of the crate tries to stick to using\n/// [`Renderer`].\n/// methods rather than directly manipulating the DOM inline.\npub type Rndr = dom::Dom;\n\n/// Types used by the renderer.\n///\n/// See [`Rndr`] for additional information on this rendering approach.\npub mod types {\n    pub use super::dom::{\n        ClassList, CssStyleDeclaration, Element, Event, Node, Placeholder,\n        TemplateElement, Text,\n    };\n}\n\n/* #[cfg(feature = \"testing\")]\n/// A renderer based on a mock DOM.\npub mod mock_dom;\n/// A DOM renderer optimized for element creation.\n#[cfg(feature = \"sledgehammer\")]\npub mod sledgehammer; */\n\n/// Implements the instructions necessary to render an interface on some platform.\n///\n/// By default, this is implemented for the Document Object Model (DOM) in a Web\n/// browser, but implementing this trait for some other platform allows you to use\n/// the library to render any tree-based UI.\npub trait Renderer: Send + Sized + Debug + 'static {\n    /// The basic type of node in the view tree.\n    type Node: Mountable + Clone + 'static;\n    /// A visible element in the view tree.\n    type Element: AsRef<Self::Node>\n        + CastFrom<Self::Node>\n        + Mountable\n        + Clone\n        + 'static;\n    /// A text node in the view tree.\n    type Text: AsRef<Self::Node>\n        + CastFrom<Self::Node>\n        + Mountable\n        + Clone\n        + 'static;\n    /// A placeholder node, which can be inserted into the tree but does not\n    /// appear (e.g., a comment node in the DOM).\n    type Placeholder: AsRef<Self::Node>\n        + CastFrom<Self::Node>\n        + Mountable\n        + Clone\n        + 'static;\n\n    /// Interns a string slice, if that is available on this platform and useful as an optimization.\n    fn intern(text: &str) -> &str;\n\n    /// Creates a new text node.\n    fn create_text_node(text: &str) -> Self::Text;\n\n    /// Creates a new placeholder node.\n    fn create_placeholder() -> Self::Placeholder;\n\n    /// Sets the text content of the node. If it's not a text node, this does nothing.\n    fn set_text(node: &Self::Text, text: &str);\n\n    /// Sets the given attribute on the given node by key and value.\n    fn set_attribute(node: &Self::Element, name: &str, value: &str);\n\n    /// Removes the given attribute on the given node.\n    fn remove_attribute(node: &Self::Element, name: &str);\n\n    /// Appends the new child to the parent, before the anchor node. If `anchor` is `None`,\n    /// append to the end of the parent's children.\n    fn insert_node(\n        parent: &Self::Element,\n        new_child: &Self::Node,\n        marker: Option<&Self::Node>,\n    );\n\n    /// Removes the child node from the parents, and returns the removed node.\n    fn remove_node(\n        parent: &Self::Element,\n        child: &Self::Node,\n    ) -> Option<Self::Node>;\n\n    /// Removes all children from the parent element.\n    fn clear_children(parent: &Self::Element);\n\n    /// Removes the node.\n    fn remove(node: &Self::Node);\n\n    /// Gets the parent of the given node, if any.\n    fn get_parent(node: &Self::Node) -> Option<Self::Node>;\n\n    /// Returns the first child node of the given node, if any.\n    fn first_child(node: &Self::Node) -> Option<Self::Node>;\n\n    /// Returns the next sibling of the given node, if any.\n    fn next_sibling(node: &Self::Node) -> Option<Self::Node>;\n\n    /// Logs the given node in a platform-appropriate way.\n    fn log_node(node: &Self::Node);\n}\n\n/// A function that can be called to remove an event handler from an element after it has been added.\n#[must_use = \"This will invalidate the event handler when it is dropped. You \\\n              should store it in some other data structure to clean it up \\\n              later to avoid dropping it immediately, or leak it with \\\n              std::mem::forget() to never drop it.\"]\n#[allow(clippy::type_complexity)]\npub struct RemoveEventHandler<T>(\n    Option<Box<dyn FnOnce() + Send + Sync>>,\n    // only here to keep the generic, removing which would be a breaking change\n    // TODO remove generic in 0.9\n    PhantomData<fn() -> T>,\n);\n\nimpl<T> RemoveEventHandler<T> {\n    /// Creates a new container with a function that will be called when it is dropped.\n    pub(crate) fn new(remove: impl FnOnce() + Send + Sync + 'static) -> Self {\n        Self(Some(Box::new(remove)), PhantomData)\n    }\n\n    #[allow(clippy::type_complexity)]\n    pub(crate) fn into_inner(\n        mut self,\n    ) -> Option<Box<dyn FnOnce() + Send + Sync>> {\n        self.0.take()\n    }\n}\n\nimpl<T> Drop for RemoveEventHandler<T> {\n    fn drop(&mut self) {\n        if let Some(cb) = self.0.take() {\n            cb()\n        }\n    }\n}\n\n/// Additional rendering behavior that applies only to DOM nodes.\npub trait DomRenderer: Renderer {\n    /// Generic event type, from which any specific event can be converted.\n    type Event;\n    /// The list of CSS classes for an element.\n    type ClassList: Clone + 'static;\n    /// The CSS styles for an element.\n    type CssStyleDeclaration: Clone + 'static;\n    /// The type of a `<template>` element.\n    type TemplateElement;\n\n    /// Sets a JavaScript object property on a DOM element.\n    fn set_property(el: &Self::Element, key: &str, value: &JsValue);\n\n    /// Adds an event listener to an element.\n    ///\n    /// Returns a function to remove the listener.\n    fn add_event_listener(\n        el: &Self::Element,\n        name: &str,\n        cb: Box<dyn FnMut(Self::Event)>,\n    ) -> RemoveEventHandler<Self::Element>;\n\n    /// Adds an event listener to an element, delegated to the window if possible.\n    ///\n    /// Returns a function to remove the listener.\n    fn add_event_listener_delegated(\n        el: &Self::Element,\n        name: Cow<'static, str>,\n        delegation_key: Cow<'static, str>,\n        cb: Box<dyn FnMut(Self::Event)>,\n    ) -> RemoveEventHandler<Self::Element>;\n\n    /// Return the `event.target`, cast to the given type.\n    fn event_target<T>(ev: &Self::Event) -> T\n    where\n        T: CastFrom<Self::Element>;\n\n    /// The list of CSS classes for an element.\n    fn class_list(el: &Self::Element) -> Self::ClassList;\n\n    /// Add a class to the list.\n    fn add_class(class_list: &Self::ClassList, name: &str);\n\n    /// Remove a class from the list.\n    fn remove_class(class_list: &Self::ClassList, name: &str);\n\n    /// The set of styles for an element.\n    fn style(el: &Self::Element) -> Self::CssStyleDeclaration;\n\n    /// Sets a CSS property.\n    fn set_css_property(\n        style: &Self::CssStyleDeclaration,\n        name: &str,\n        value: &str,\n    );\n\n    /// Sets the `innerHTML` of a DOM element, without escaping any values.\n    fn set_inner_html(el: &Self::Element, html: &str);\n\n    /// Returns a cached template element created from the given type.\n    fn get_template<V>() -> Self::TemplateElement\n    where\n        V: ToTemplate + 'static;\n\n    /// Deeply clones a template.\n    fn clone_template(tpl: &Self::TemplateElement) -> Self::Element;\n\n    /// Creates a single element from a string of HTML.\n    fn create_element_from_html(html: &str) -> Self::Element;\n}\n\n/// Attempts to cast from one type to another.\n///\n/// This works in a similar way to `TryFrom`. We implement it as a separate trait\n/// simply so we don't have to create wrappers for the `web_sys` types; it can't be\n/// implemented on them directly because of the orphan rules.\npub trait CastFrom<T>\nwhere\n    Self: Sized,\n{\n    /// Casts a node from one type to another.\n    fn cast_from(source: T) -> Option<Self>;\n}\n"
  },
  {
    "path": "tachys/src/renderer/sledgehammer.rs",
    "content": "#![allow(missing_docs)] // Allow missing docs for experimental backend\n\nuse super::{CastFrom, DomRenderer, RemoveEventHandler, Renderer};\nuse crate::{\n    dom::window,\n    view::{Mountable, ToTemplate},\n};\nuse linear_map::LinearMap;\nuse rustc_hash::FxHashSet;\nuse sledgehammer_bindgen::bindgen;\nuse std::{\n    any::TypeId,\n    borrow::Cow,\n    cell::{Cell, RefCell},\n    rc::Rc,\n};\nuse wasm_bindgen::{\n    prelude::{wasm_bindgen, Closure},\n    JsCast, JsValue,\n};\nuse web_sys::Node;\n\n#[wasm_bindgen]\nextern \"C\" {\n    #[wasm_bindgen]\n    fn queueMicrotask(closure: &Closure<dyn Fn() -> ()>);\n\n    type Global;\n}\n\n#[bindgen]\nmod js {\n    //#[extends(NodeInterpreter)]\n    struct Channel;\n\n    const JS: &str = r#\"\n        function Queue() {\n            var head, tail;\n            return Object.freeze({     \n                enqueue(value) { \n                    const link = {value, next: undefined};\n                    tail = head ? tail.next = link : head = link;\n                },\n                dequeue() {\n                    if (head) {\n                        const value = head.value;\n                        head = head.next;\n                        return value;\n                    }\n                },\n                peek() { return head?.value }\n            });\n        }\n        this.nodes = [null];\n        this.jsvalues = Queue();\n    \"#;\n\n    fn drop_node(id: u32) {\n        \"this.nodes[$id$]=null;\"\n    }\n\n    fn store_body(id: u32) {\n        \"this.nodes[$id$]=document.body;\"\n    }\n\n    fn create_text_node(id: u32, data: &str) {\n        \"this.nodes[$id$]=document.createTextNode($data$);\"\n    }\n\n    fn create_comment(id: u32) {\n        \"this.nodes[$id$]=document.createComment();\"\n    }\n\n    fn create_element(id: u32, name: &'static str<u8, name_cache>) {\n        \"this.nodes[$id$]=document.createElement($name$);\"\n    }\n\n    fn set_attribute(\n        id: u32,\n        name: &str<u8, name_cache>,\n        val: impl Writable<u8>,\n    ) {\n        \"this.nodes[$id$].setAttribute($name$,$val$);\"\n    }\n\n    fn remove_child(parent: u32, child: u32) {\n        \"this.nodes[$parent$].removeChild(this.nodes[$child$]);\"\n    }\n\n    fn remove_attribute(id: u32, name: &str<u8, name_cache>) {\n        \"this.nodes[$id$].removeAttribute($name$);\"\n    }\n\n    fn append_child(id: u32, id2: u32) {\n        \"this.nodes[$id$].appendChild(nodes[$id2$]);\"\n    }\n\n    fn insert_before(parent: u32, child: u32, marker: u32) {\n        \"this.nodes[$parent$].insertBefore(this.nodes[$child$],this.\\\n         nodes[$marker$]);\"\n    }\n\n    fn set_text(id: u32, text: impl Writable<u8>) {\n        \"this.nodes[$id$].textContent=$text$;\"\n    }\n\n    fn remove(id: u32) {\n        \"this.nodes[$id$].remove();\"\n    }\n\n    fn replace(id: u32, id2: u32) {\n        \"this.nodes[$id$].replaceWith(this.nodes[$id2$]);\"\n    }\n\n    fn first_child(parent: u32, child: u32) {\n        \"this.nodes[$child$]=this.nodes[$parent$].firstChild;\"\n    }\n\n    fn next_sibling(anchor: u32, sibling: u32) {\n        \"this.nodes[$sibling$]=this.nodes[$anchor$].nextSibling;\"\n    }\n\n    fn class_list(el: u32, class_list: u32) {\n        \"this.nodes[$class_list$]=this.nodes[$el$].classList;\"\n    }\n\n    fn add_class(class_list: u32, name: &str<u8, class_cache>) {\n        \"this.nodes[$class_list$].add($name$);\"\n    }\n\n    fn remove_class(class_list: u32, name: &str<u8, class_cache>) {\n        \"this.nodes[$class_list$].remove($name$);\"\n    }\n\n    fn set_inner_html(node: u32, html: &str) {\n        \"this.nodes[$node$].innerHTML = $html$;\"\n    }\n\n    fn clone_template(tpl_node: u32, into_node: u32) {\n        \"this.nodes[$into_node$]=this.nodes[$tpl_node$].content.\\\n         cloneNode(true);\"\n    }\n\n    fn set_property(node: u32, name: &str<u8, name_cache>) {\n        \"{let jsv=this.jsvalues.dequeue();this.nodes[$node$][$name$]=jsv;}\"\n    }\n\n    fn add_listener(node: u32, name: &str<u8, name_cache>) {\n        \"{let jsv=this.jsvalues.dequeue();this.nodes[$node$].\\\n         addEventListener($name$, jsv);}\"\n    }\n}\n\n#[wasm_bindgen(inline_js = \"\n    export function get_node(channel, id){\n        return channel.nodes[id];\n    }\n\n    export function store_node(channel, id, node){\n        channel.nodes[id] = node;\n    }\n\n    export function store_jsvalue(channel, value) {\n        channel.jsvalues.enqueue(value);\n    }\n\")]\nextern \"C\" {\n    fn get_node(channel: &JSChannel, id: u32) -> Node;\n\n    fn store_node(channel: &JSChannel, id: u32, node: Node);\n\n    fn store_jsvalue(channel: &JSChannel, value: JsValue);\n}\n\n#[derive(Debug)]\npub struct Sledgehammer;\n\nimpl Sledgehammer {\n    pub fn body() -> SNode {\n        let node = SNode::new();\n        with(|channel| channel.store_body(node.0 .0));\n        node\n    }\n\n    pub fn store(node: Node) -> SNode {\n        let snode = SNode::new();\n        with(|channel| store_node(channel.js_channel(), snode.0 .0, node));\n        snode\n    }\n\n    pub fn element(tag_name: &'static str) -> SNode {\n        let node = SNode::new();\n        with(|channel| channel.create_element(node.0 .0, tag_name));\n        node\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\npub struct SNode(Rc<SNodeInner>);\n\n#[derive(Debug, PartialEq, Eq, Hash)]\nstruct SNodeInner(u32);\n\nimpl SNode {\n    fn new() -> Self {\n        let id = if let Some(id) = RECYCLE_IDS.with_borrow_mut(Vec::pop) {\n            id\n        } else {\n            let new_id = NEXT_ID.get();\n            NEXT_ID.set(new_id + 1);\n            new_id\n        };\n        Self(Rc::new(SNodeInner(id)))\n    }\n\n    pub fn to_node(&self) -> Node {\n        CHANNEL.with_borrow(|channel| get_node(channel.js_channel(), self.0 .0))\n    }\n}\n\nimpl Drop for SNodeInner {\n    fn drop(&mut self) {\n        RECYCLE_IDS.with_borrow_mut(|ids| ids.push(self.0));\n        with(|channel| channel.drop_node(self.0));\n    }\n}\n\nimpl AsRef<SNode> for SNode {\n    fn as_ref(&self) -> &SNode {\n        self\n    }\n}\n\nimpl CastFrom<SNode> for SNode {\n    fn cast_from(source: SNode) -> Option<Self> {\n        Some(source)\n    }\n}\n\nthread_local! {\n    static CHANNEL: RefCell<Channel> = RefCell::new(Channel::default());\n    static FLUSH_PENDING: Cell<bool> = const { Cell::new(false) };\n    static FLUSH_CLOSURE: Closure<dyn Fn()> = Closure::new(|| {\n        FLUSH_PENDING.set(false);\n        CHANNEL.with_borrow_mut(|channel| {\n            channel.flush();\n        });\n    });\n    static NEXT_ID: Cell<u32> = const { Cell::new(1) };\n    static RECYCLE_IDS: RefCell<Vec<u32>> = const { RefCell::new(Vec::new()) };\n\n    pub(crate) static GLOBAL_EVENTS: RefCell<FxHashSet<Cow<'static, str>>> = Default::default();\n}\n\nfn with(fun: impl FnOnce(&mut Channel)) {\n    CHANNEL.with_borrow_mut(fun);\n    flush();\n}\n\n#[allow(unused)] // might be handy at some point!\nfn flush_sync() {\n    FLUSH_PENDING.set(false);\n    CHANNEL.with_borrow_mut(|channel| channel.flush());\n}\n\nfn flush() {\n    let was_pending = FLUSH_PENDING.replace(true);\n    if !was_pending {\n        FLUSH_CLOSURE.with(queueMicrotask);\n    }\n}\n\nimpl Renderer for Sledgehammer {\n    type Node = SNode;\n    type Text = SNode;\n    type Element = SNode;\n    type Placeholder = SNode;\n\n    fn intern(text: &str) -> &str {\n        text\n    }\n\n    fn create_text_node(text: &str) -> Self::Text {\n        let node = SNode::new();\n        with(|channel| channel.create_text_node(node.0 .0, text));\n        node\n    }\n\n    fn create_placeholder() -> Self::Placeholder {\n        let node = SNode::new();\n        with(|channel| channel.create_comment(node.0 .0));\n        node\n    }\n\n    fn set_text(node: &Self::Text, text: &str) {\n        with(|channel| channel.set_text(node.0 .0, text));\n    }\n\n    fn set_attribute(node: &Self::Element, name: &str, value: &str) {\n        with(|channel| channel.set_attribute(node.0 .0, name, value));\n    }\n\n    fn remove_attribute(node: &Self::Element, name: &str) {\n        with(|channel| channel.remove_attribute(node.0 .0, name));\n    }\n\n    fn insert_node(\n        parent: &Self::Element,\n        new_child: &Self::Node,\n        anchor: Option<&Self::Node>,\n    ) {\n        with(|channel| {\n            channel.insert_before(\n                parent.0 .0,\n                new_child.0 .0,\n                anchor.map(|n| n.0 .0).unwrap_or(0),\n            )\n        });\n    }\n\n    fn remove_node(\n        parent: &Self::Element,\n        child: &Self::Node,\n    ) -> Option<Self::Node> {\n        with(|channel| channel.remove_child(parent.0 .0, child.0 .0));\n        Some(child.clone())\n    }\n\n    fn remove(node: &Self::Node) {\n        with(|channel| channel.remove(node.0 .0));\n    }\n\n    fn get_parent(_node: &Self::Node) -> Option<Self::Node> {\n        todo!() // node.parent_node()\n    }\n\n    fn first_child(node: &Self::Node) -> Option<Self::Node> {\n        let child = SNode::new();\n        with(|channel| channel.first_child(node.0 .0, child.0 .0));\n        Some(child)\n    }\n\n    fn next_sibling(node: &Self::Node) -> Option<Self::Node> {\n        let sibling = SNode::new();\n        with(|channel| channel.next_sibling(node.0 .0, sibling.0 .0));\n        Some(sibling)\n    }\n\n    fn log_node(_node: &Self::Node) {\n        todo!()\n    }\n\n    fn clear_children(parent: &Self::Element) {\n        with(|channel| channel.set_text(parent.0 .0, \"\"));\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct ClassList(SNode);\n\n#[derive(Debug, Clone)]\n#[allow(dead_code)] // this will be used, it's just all unimplemented\npub struct CssStyle(SNode);\n\nimpl DomRenderer for Sledgehammer {\n    type Event = JsValue;\n    type ClassList = ClassList;\n    type CssStyleDeclaration = CssStyle;\n    type TemplateElement = SNode;\n\n    fn set_property(_el: &Self::Element, _key: &str, _value: &JsValue) {\n        todo!()\n    }\n\n    fn add_event_listener(\n        el: &Self::Element,\n        name: &str,\n        cb: Box<dyn FnMut(Self::Event)>,\n    ) -> RemoveEventHandler<Self::Element> {\n        let cb = wasm_bindgen::closure::Closure::wrap(cb).into_js_value();\n        CHANNEL.with_borrow_mut(|channel| {\n            channel.add_listener(el.0 .0, name);\n            let channel = channel.js_channel();\n            store_jsvalue(channel, cb);\n        });\n\n        // return the remover\n        RemoveEventHandler(Box::new(move |_el| todo!()))\n    }\n\n    fn event_target<T>(_ev: &Self::Event) -> T\n    where\n        T: CastFrom<Self::Element>,\n    {\n        todo!()\n        /*let el = ev\n            .unchecked_ref::<Event>()\n            .target()\n            .expect(\"event.target not found\")\n            .unchecked_into::<Element>();\n        T::cast_from(el).expect(\"incorrect element type\")*/\n    }\n\n    fn add_event_listener_delegated(\n        el: &Self::Element,\n        name: Cow<'static, str>,\n        delegation_key: Cow<'static, str>,\n        cb: Box<dyn FnMut(Self::Event)>,\n    ) -> RemoveEventHandler<Self::Element> {\n        let cb = Closure::wrap(cb).into_js_value();\n        CHANNEL.with_borrow_mut(|channel| {\n            channel.set_property(el.0 .0, &delegation_key);\n            let channel = channel.js_channel();\n            store_jsvalue(channel, cb);\n        });\n\n        GLOBAL_EVENTS.with(|global_events| {\n            let mut events = global_events.borrow_mut();\n            if !events.contains(&name) {\n                // create global handler\n                let key = JsValue::from_str(&delegation_key);\n                let handler = move |ev: web_sys::Event| {\n                    let target = ev.target();\n                    let node = ev.composed_path().get(0);\n                    let mut node = if node.is_undefined() || node.is_null() {\n                        JsValue::from(target)\n                    } else {\n                        node\n                    };\n\n                    // TODO reverse Shadow DOM retargetting\n                    // TODO simulate currentTarget\n\n                    while !node.is_null() {\n                        let node_is_disabled = js_sys::Reflect::get(\n                            &node,\n                            &JsValue::from_str(\"disabled\"),\n                        )\n                        .unwrap()\n                        .is_truthy();\n                        if !node_is_disabled {\n                            let maybe_handler =\n                                js_sys::Reflect::get(&node, &key).unwrap();\n                            if !maybe_handler.is_undefined() {\n                                let f = maybe_handler\n                                    .unchecked_ref::<js_sys::Function>();\n                                let _ = f.call1(&node, &ev);\n\n                                if ev.cancel_bubble() {\n                                    return;\n                                }\n                            }\n                        }\n\n                        // navigate up tree\n                        if let Some(parent) =\n                            node.unchecked_ref::<web_sys::Node>().parent_node()\n                        {\n                            node = parent.into()\n                        } else if let Some(root) =\n                            node.dyn_ref::<web_sys::ShadowRoot>()\n                        {\n                            node = root.host().unchecked_into();\n                        } else {\n                            node = JsValue::null()\n                        }\n                    }\n                };\n\n                let handler =\n                    Box::new(handler) as Box<dyn FnMut(web_sys::Event)>;\n                let handler = Closure::wrap(handler).into_js_value();\n                window()\n                    .add_event_listener_with_callback(\n                        &name,\n                        handler.unchecked_ref(),\n                    )\n                    .unwrap();\n\n                // register that we've created handler\n                events.insert(name);\n            }\n        });\n\n        // return the remover\n        RemoveEventHandler(Box::new(move |_el| todo!()))\n    }\n\n    fn class_list(el: &Self::Element) -> Self::ClassList {\n        let class_list = SNode::new();\n        with(|channel| channel.class_list(el.0 .0, class_list.0 .0));\n        ClassList(class_list)\n    }\n\n    fn add_class(list: &Self::ClassList, name: &str) {\n        with(|channel| channel.add_class(list.0 .0 .0, name));\n    }\n\n    fn remove_class(list: &Self::ClassList, name: &str) {\n        with(|channel| channel.remove_class(list.0 .0 .0, name));\n    }\n\n    fn style(_el: &Self::Element) -> Self::CssStyleDeclaration {\n        todo!()\n        //el.unchecked_ref::<HtmlElement>().style()\n    }\n\n    fn set_css_property(\n        _style: &Self::CssStyleDeclaration,\n        _name: &str,\n        _value: &str,\n    ) {\n        todo!()\n        /*or_debug!(\n            style.set_property(name, value),\n            style.unchecked_ref(),\n            \"setProperty\"\n        );*/\n    }\n\n    fn set_inner_html(el: &Self::Element, html: &str) {\n        with(|channel| channel.set_inner_html(el.0 .0, html))\n    }\n\n    fn get_template<V>() -> Self::TemplateElement\n    where\n        V: ToTemplate + 'static,\n    {\n        thread_local! {\n            static TEMPLATES: RefCell<Vec<(TypeId, SNode)>> = Default::default();\n        }\n\n        TEMPLATES.with_borrow_mut(|t| {\n            let id = TypeId::of::<V>();\n            t.iter()\n                .find_map(|entry| (entry.0 == id).then(|| entry.1.clone()))\n                .unwrap_or_else(|| {\n                    let mut buf = String::new();\n                    V::to_template(\n                        &mut buf,\n                        &mut String::new(),\n                        &mut String::new(),\n                        &mut String::new(),\n                        &mut Default::default(),\n                    );\n                    let node = SNode::new();\n                    with(|channel| {\n                        channel.create_element(node.0 .0, \"template\");\n                        channel.set_inner_html(node.0 .0, &buf)\n                    });\n                    t.push((id, node.clone()));\n                    node\n                })\n        })\n    }\n\n    fn clone_template(tpl: &Self::TemplateElement) -> Self::Element {\n        let node = SNode::new();\n        with(|channel| {\n            channel.clone_template(tpl.0 .0, node.0 .0);\n        });\n        node\n    }\n\n    fn create_element_from_html(_html: &str) -> Self::Element {\n        todo!()\n    }\n}\n\nimpl Mountable<Sledgehammer> for SNode {\n    fn unmount(&mut self) {\n        with(|channel| channel.remove(self.0 .0));\n    }\n\n    fn mount(&mut self, parent: &SNode, marker: Option<&SNode>) {\n        Sledgehammer::insert_node(parent, self, marker);\n    }\n\n    fn insert_before_this(\n        &self,\n        _child: &mut dyn Mountable<Sledgehammer>,\n    ) -> bool {\n        todo!()\n    }\n}\n"
  },
  {
    "path": "tachys/src/ssr/mod.rs",
    "content": "use crate::{\n    html::attribute::any_attribute::AnyAttribute,\n    view::{Position, RenderHtml},\n};\nuse futures::Stream;\nuse std::{\n    collections::VecDeque,\n    fmt::{Debug, Write},\n    future::Future,\n    mem,\n    pin::Pin,\n    sync::Arc,\n    task::{Context, Poll},\n};\n\n/// Manages streaming HTML rendering for the response to a single request.\n#[derive(Default)]\npub struct StreamBuilder {\n    pub(crate) sync_buf: String,\n    pub(crate) chunks: VecDeque<StreamChunk>,\n    pending: Option<ChunkFuture>,\n    pending_ooo: VecDeque<PinnedFuture<OooChunk>>,\n    id: Option<Vec<u16>>,\n}\n\ntype PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;\ntype ChunkFuture = PinnedFuture<VecDeque<StreamChunk>>;\n\nimpl StreamBuilder {\n    /// Creates a new HTML stream.\n    pub fn new(id: Option<Vec<u16>>) -> Self {\n        Self::with_capacity(0, id)\n    }\n\n    /// Creates a new stream with a given capacity in the synchronous buffer and an identifier.\n    pub fn with_capacity(capacity: usize, id: Option<Vec<u16>>) -> Self {\n        Self {\n            id,\n            sync_buf: String::with_capacity(capacity),\n            ..Default::default()\n        }\n    }\n\n    /// Reserves additional space in the synchronous buffer.\n    pub fn reserve(&mut self, additional: usize) {\n        self.sync_buf.reserve(additional);\n    }\n\n    /// Pushes text into the synchronous buffer.\n    pub fn push_sync(&mut self, string: &str) {\n        self.sync_buf.push_str(string);\n    }\n\n    /// Pushes an async block into the stream.\n    pub fn push_async(\n        &mut self,\n        fut: impl Future<Output = VecDeque<StreamChunk>> + Send + 'static,\n    ) {\n        // flush sync chunk\n        let sync = mem::take(&mut self.sync_buf);\n        if !sync.is_empty() {\n            self.chunks.push_back(StreamChunk::Sync(sync));\n        }\n        self.chunks.push_back(StreamChunk::Async {\n            chunks: Box::pin(fut) as PinnedFuture<VecDeque<StreamChunk>>,\n        });\n    }\n\n    /// Mutates the synchronous buffer.\n    pub fn with_buf(&mut self, fun: impl FnOnce(&mut String)) {\n        fun(&mut self.sync_buf)\n    }\n\n    /// Takes all chunks currently available in the stream, including the synchronous buffer.\n    pub fn take_chunks(&mut self) -> VecDeque<StreamChunk> {\n        let sync = mem::take(&mut self.sync_buf);\n        if !sync.is_empty() {\n            self.chunks.push_back(StreamChunk::Sync(sync));\n        }\n        mem::take(&mut self.chunks)\n    }\n\n    /// Appends another stream to this one.\n    pub fn append(&mut self, mut other: StreamBuilder) {\n        if !self.sync_buf.is_empty() {\n            self.chunks\n                .push_back(StreamChunk::Sync(mem::take(&mut self.sync_buf)));\n        }\n        self.chunks.append(&mut other.chunks);\n        self.sync_buf.push_str(&other.sync_buf);\n    }\n\n    /// Completes the stream.\n    pub fn finish(mut self) -> Self {\n        let sync_buf_remaining = mem::take(&mut self.sync_buf);\n        if sync_buf_remaining.is_empty() {\n            return self;\n        } else if let Some(StreamChunk::Sync(buf)) = self.chunks.back_mut() {\n            buf.push_str(&sync_buf_remaining);\n        } else {\n            self.chunks.push_back(StreamChunk::Sync(sync_buf_remaining));\n        }\n        self\n    }\n\n    // Out-of-Order Streaming\n    /// Pushes a fallback for out-of-order streaming.\n    pub fn push_fallback<View>(\n        &mut self,\n        fallback: View,\n        position: &mut Position,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        View: RenderHtml,\n    {\n        self.write_chunk_marker(true);\n        fallback.to_html_with_buf(\n            &mut self.sync_buf,\n            position,\n            true,\n            mark_branches,\n            extra_attrs,\n        );\n        self.write_chunk_marker(false);\n        *position = Position::NextChild;\n    }\n\n    /// Increments the chunk ID.\n    pub fn next_id(&mut self) {\n        if let Some(last) = self.id.as_mut().and_then(|ids| ids.last_mut()) {\n            *last += 1;\n        }\n    }\n\n    /// Returns the current ID.\n    pub fn clone_id(&self) -> Option<Vec<u16>> {\n        self.id.clone()\n    }\n\n    /// Returns an ID that is a child of the current one.\n    pub fn child_id(&self) -> Option<Vec<u16>> {\n        let mut child = self.id.clone();\n        if let Some(child) = child.as_mut() {\n            child.push(0);\n        }\n        child\n    }\n\n    /// Inserts a marker for the current out-of-order chunk.\n    pub fn write_chunk_marker(&mut self, opening: bool) {\n        if let Some(id) = &self.id {\n            self.sync_buf.reserve(11 + (id.len() * 2));\n            self.sync_buf.push_str(\"<!--s-\");\n            for piece in id {\n                write!(&mut self.sync_buf, \"{piece}-\").unwrap();\n            }\n            if opening {\n                self.sync_buf.push_str(\"o-->\");\n            } else {\n                self.sync_buf.push_str(\"c-->\");\n            }\n        }\n    }\n\n    /// Injects an out-of-order chunk into the stream.\n    pub fn push_async_out_of_order<View>(\n        &mut self,\n        view: impl Future<Output = Option<View>> + Send + 'static,\n        position: &mut Position,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        View: RenderHtml,\n    {\n        self.push_async_out_of_order_with_nonce(\n            view,\n            position,\n            mark_branches,\n            None,\n            extra_attrs,\n        );\n    }\n\n    /// Injects an out-of-order chunk into the stream, using the given nonce for `<script>` tags.\n    pub fn push_async_out_of_order_with_nonce<View>(\n        &mut self,\n        view: impl Future<Output = Option<View>> + Send + 'static,\n        position: &mut Position,\n        mark_branches: bool,\n        nonce: Option<Arc<str>>,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        View: RenderHtml,\n    {\n        let id = self.clone_id();\n        // copy so it's not updated by additional iterations\n        // i.e., restart in the same position we were at when we suspended\n        let mut position = *position;\n\n        self.chunks.push_back(StreamChunk::OutOfOrder {\n            chunks: Box::pin(async move {\n                let view = view.await;\n\n                let mut subbuilder = StreamBuilder::new(id);\n                let mut id = String::new();\n                if let Some(ids) = &subbuilder.id {\n                    for piece in ids {\n                        write!(&mut id, \"{piece}-\").unwrap();\n                    }\n                }\n                if let Some(id) = subbuilder.id.as_mut() {\n                    id.push(0);\n                }\n                let replace = view.is_some();\n                view.to_html_async_with_buf::<true>(\n                    &mut subbuilder,\n                    &mut position,\n                    true,\n                    mark_branches,\n                    extra_attrs,\n                );\n                let chunks = subbuilder.finish().take_chunks();\n                let mut flattened_chunks =\n                    VecDeque::with_capacity(chunks.len());\n                for chunk in chunks {\n                    // this will wait for any ErrorBoundary async nodes and flatten them out\n                    if let StreamChunk::Async { chunks } = chunk {\n                        flattened_chunks.extend(chunks.await);\n                    } else {\n                        flattened_chunks.push_back(chunk);\n                    }\n                }\n\n                OooChunk {\n                    id,\n                    chunks: flattened_chunks,\n                    replace,\n                    nonce,\n                }\n            }),\n        });\n    }\n}\n\nimpl Debug for StreamBuilder {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"StreamBuilderInner\")\n            .field(\"sync_buf\", &self.sync_buf)\n            .field(\"chunks\", &self.chunks)\n            .field(\"pending\", &self.pending.is_some())\n            .finish()\n    }\n}\n\n/// A chunk of the HTML stream.\npub enum StreamChunk {\n    /// Some synchronously-available HTML.\n    Sync(String),\n    /// The chunk can be rendered asynchronously in order.\n    Async {\n        /// A collection of in-order chunks.\n        chunks: PinnedFuture<VecDeque<StreamChunk>>,\n    },\n    /// The chunk can be rendered asynchronously out of order.\n    OutOfOrder {\n        /// A collection of out-of-order chunks\n        chunks: PinnedFuture<OooChunk>,\n    },\n}\n\n/// A chunk of the out-of-order stream.\n#[derive(Debug)]\npub struct OooChunk {\n    id: String,\n    chunks: VecDeque<StreamChunk>,\n    replace: bool,\n    nonce: Option<Arc<str>>,\n}\n\nimpl OooChunk {\n    /// Pushes an opening `<template>` tag into the buffer.\n    pub fn push_start(id: &str, buf: &mut String) {\n        buf.push_str(\"<template id=\\\"\");\n        buf.push_str(id);\n        buf.push('f');\n        buf.push_str(\"\\\">\");\n    }\n\n    /// Pushes a closing `</template>` and update script into the buffer.\n    pub fn push_end(replace: bool, id: &str, buf: &mut String) {\n        Self::push_end_with_nonce(replace, id, buf, None);\n    }\n\n    /// Pushes a closing `</template>` and update script with the given nonce into the buffer.\n    pub fn push_end_with_nonce(\n        replace: bool,\n        id: &str,\n        buf: &mut String,\n        nonce: Option<&str>,\n    ) {\n        buf.push_str(\"</template>\");\n\n        if let Some(nonce) = nonce {\n            buf.push_str(\"<script nonce=\\\"\");\n            buf.push_str(nonce);\n            buf.push_str(r#\"\">(function() { let id = \"\"#);\n        } else {\n            buf.push_str(r#\"<script>(function() { let id = \"\"#);\n        }\n        buf.push_str(id);\n        buf.push_str(\n            \"\\\";let open = undefined;let close = undefined;let walker = \\\n             document.createTreeWalker(document.body, \\\n             NodeFilter.SHOW_COMMENT);while(walker.nextNode()) \\\n             {if(walker.currentNode.textContent == `s-${id}o`){ \\\n             open=walker.currentNode; } else \\\n             if(walker.currentNode.textContent == `s-${id}c`) { close = \\\n             walker.currentNode;}}let range = new Range(); \\\n             range.setStartBefore(open); range.setEndBefore(close);\",\n        );\n        if replace {\n            buf.push_str(\n                \"range.deleteContents(); let tpl = \\\n                 document.getElementById(`${id}f`); \\\n                 close.parentNode.insertBefore(tpl.content.cloneNode(true), \\\n                 close);close.remove();\",\n            );\n        } else {\n            buf.push_str(\"close.remove();open.remove();\");\n        }\n        buf.push_str(\"})()</script>\");\n    }\n\n    /// Consumes this structure and returns its inner chunks of the stream.\n    pub fn take_chunks(self) -> VecDeque<StreamChunk> {\n        self.chunks\n    }\n}\n\nimpl Debug for StreamChunk {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Sync(arg0) => f.debug_tuple(\"Sync\").field(arg0).finish(),\n            Self::Async { .. } => {\n                f.debug_struct(\"Async\").finish_non_exhaustive()\n            }\n            Self::OutOfOrder { .. } => {\n                f.debug_struct(\"OutOfOrder\").finish_non_exhaustive()\n            }\n        }\n    }\n}\n\nimpl Stream for StreamBuilder {\n    type Item = String;\n\n    fn poll_next(\n        mut self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n    ) -> Poll<Option<Self::Item>> {\n        let mut this = self.as_mut();\n        let pending = this.pending.take();\n        if let Some(mut pending) = pending {\n            match pending.as_mut().poll(cx) {\n                Poll::Pending => {\n                    this.pending = Some(pending);\n                    Poll::Pending\n                }\n                Poll::Ready(chunks) => {\n                    for chunk in chunks.into_iter().rev() {\n                        this.chunks.push_front(chunk);\n                    }\n                    self.poll_next(cx)\n                }\n            }\n        } else {\n            let next_chunk = this.chunks.pop_front();\n            match next_chunk {\n                None => {\n                    if this.pending_ooo.is_empty() {\n                        if this.sync_buf.is_empty() {\n                            Poll::Ready(None)\n                        } else {\n                            Poll::Ready(Some(mem::take(&mut this.sync_buf)))\n                        }\n                    } else {\n                        // check if *any* pending out-of-order chunk is ready\n                        for mut chunk in mem::take(&mut this.pending_ooo) {\n                            match chunk.as_mut().poll(cx) {\n                                Poll::Ready(OooChunk {\n                                    id,\n                                    chunks,\n                                    replace,\n                                    nonce,\n                                }) => {\n                                    let opening = format!(\"<!--s-{id}o-->\");\n                                    let placeholder_at =\n                                        this.sync_buf.find(&opening);\n                                    if let Some(start) = placeholder_at {\n                                        let closing = format!(\"<!--s-{id}c-->\");\n                                        let end = this\n                                            .sync_buf\n                                            .find(&closing)\n                                            .unwrap();\n                                        let chunks_iter =\n                                            chunks.into_iter().rev();\n\n                                        // TODO can probably make this more efficient\n                                        let (before, replaced) =\n                                            this.sync_buf.split_at(start);\n                                        let (_, after) = replaced.split_at(\n                                            end - start + closing.len(),\n                                        );\n                                        let mut buf = String::new();\n                                        buf.push_str(before);\n\n                                        let mut held_chunks = VecDeque::new();\n                                        for chunk in chunks_iter {\n                                            if let StreamChunk::Sync(ready) =\n                                                chunk\n                                            {\n                                                buf.push_str(&ready);\n                                            } else {\n                                                held_chunks.push_front(chunk);\n                                            }\n                                        }\n                                        buf.push_str(after);\n                                        this.sync_buf = buf;\n                                        for chunk in held_chunks {\n                                            this.chunks.push_front(chunk);\n                                        }\n                                    } else {\n                                        OooChunk::push_start(\n                                            &id,\n                                            &mut this.sync_buf,\n                                        );\n                                        for chunk in chunks.into_iter().rev() {\n                                            if let StreamChunk::Sync(ready) =\n                                                chunk\n                                            {\n                                                this.sync_buf.push_str(&ready);\n                                            } else {\n                                                this.chunks.push_front(chunk);\n                                            }\n                                        }\n                                        OooChunk::push_end_with_nonce(\n                                            replace,\n                                            &id,\n                                            &mut this.sync_buf,\n                                            nonce.as_deref(),\n                                        );\n                                    }\n                                }\n                                Poll::Pending => {\n                                    this.pending_ooo.push_back(chunk);\n                                }\n                            }\n                        }\n\n                        if this.sync_buf.is_empty() {\n                            Poll::Pending\n                        } else {\n                            Poll::Ready(Some(mem::take(&mut this.sync_buf)))\n                        }\n                    }\n                }\n                Some(StreamChunk::Sync(value)) => {\n                    this.sync_buf.push_str(&value);\n                    loop {\n                        match this.chunks.pop_front() {\n                            None => break,\n                            Some(StreamChunk::Async { chunks }) => {\n                                this.chunks\n                                    .push_front(StreamChunk::Async { chunks });\n                                break;\n                            }\n                            Some(StreamChunk::OutOfOrder {\n                                chunks, ..\n                            }) => {\n                                this.pending_ooo.push_back(chunks);\n                                break;\n                            }\n                            Some(StreamChunk::Sync(next)) => {\n                                this.sync_buf.push_str(&next);\n                            }\n                        }\n                    }\n\n                    this.poll_next(cx)\n                }\n                Some(StreamChunk::Async { chunks, .. }) => {\n                    this.pending = Some(chunks);\n                    if this.sync_buf.is_empty() {\n                        self.poll_next(cx)\n                    } else {\n                        Poll::Ready(Some(mem::take(&mut this.sync_buf)))\n                    }\n                }\n                Some(StreamChunk::OutOfOrder { chunks, .. }) => {\n                    this.pending_ooo.push_back(chunks);\n                    if this.sync_buf.is_empty() {\n                        self.poll_next(cx)\n                    } else {\n                        Poll::Ready(Some(mem::take(&mut this.sync_buf)))\n                    }\n                }\n            }\n        }\n    }\n}\n\n/*\n#[cfg(test)]\nmod tests {\n    use crate::{\n        async_views::{FutureViewExt, Suspend},\n        html::element::{em, main, p, ElementChild, HtmlElement, Main},\n        renderer::dom::Dom,\n        view::RenderHtml,\n    };\n    use futures::StreamExt;\n    use std::time::Duration;\n    use tokio::time::sleep;\n\n    #[tokio::test]\n    async fn in_order_stream_of_sync_content_ready_immediately() {\n        let el: HtmlElement<Main, _, _, Dom> = main().child(p().child((\n            \"Hello, \",\n            em().child(\"beautiful\"),\n            \" world!\",\n        )));\n        let mut stream = el.to_html_stream_in_order();\n\n        let html = stream.next().await.unwrap();\n        assert_eq!(\n            html,\n            \"<main><p>Hello, <em>beautiful</em> world!</p></main>\"\n        );\n    }\n\n    #[tokio::test]\n    async fn in_order_single_async_block_in_stream() {\n        let el = async {\n            sleep(Duration::from_millis(250)).await;\n            \"Suspended\"\n        }\n        .suspend();\n        let mut stream =\n            <Suspend<false, _, _> as RenderHtml<Dom>>::to_html_stream_in_order(\n                el,\n            );\n\n        let html = stream.next().await.unwrap();\n        assert_eq!(html, \"Suspended<!>\");\n    }\n\n    #[tokio::test]\n    async fn in_order_async_with_siblings_in_stream() {\n        let el = (\n            \"Before Suspense\",\n            async {\n                sleep(Duration::from_millis(250)).await;\n                \"Suspended\"\n            }\n            .suspend(),\n        );\n        let mut stream =\n            <(&str, Suspend<false, _, _>) as RenderHtml<Dom>>::to_html_stream_in_order(\n                el,\n            );\n\n        assert_eq!(stream.next().await.unwrap(), \"Before Suspense\");\n        assert_eq!(stream.next().await.unwrap(), \"<!>Suspended\");\n        assert!(stream.next().await.is_none());\n    }\n\n    #[tokio::test]\n    async fn in_order_async_inside_element_in_stream() {\n        let el: HtmlElement<_, _, _, Dom> = p().child((\n            \"Before Suspense\",\n            async {\n                sleep(Duration::from_millis(250)).await;\n                \"Suspended\"\n            }\n            .suspend(),\n        ));\n        let mut stream = el.to_html_stream_in_order();\n\n        assert_eq!(stream.next().await.unwrap(), \"<p>Before Suspense\");\n        assert_eq!(stream.next().await.unwrap(), \"<!>Suspended</p>\");\n        assert!(stream.next().await.is_none());\n    }\n\n    #[tokio::test]\n    async fn in_order_nested_async_blocks() {\n        let el: HtmlElement<_, _, _, Dom> = main().child((\n            \"Before Suspense\",\n            async {\n                sleep(Duration::from_millis(250)).await;\n                p().child((\n                    \"Before inner Suspense\",\n                    async {\n                        sleep(Duration::from_millis(250)).await;\n                        \"Inner Suspense\"\n                    }\n                    .suspend(),\n                ))\n            }\n            .suspend(),\n        ));\n        let mut stream = el.to_html_stream_in_order();\n\n        assert_eq!(stream.next().await.unwrap(), \"<main>Before Suspense\");\n        assert_eq!(stream.next().await.unwrap(), \"<p>Before inner Suspense\");\n        assert_eq!(\n            stream.next().await.unwrap(),\n            \"<!>Inner Suspense</p></main>\"\n        );\n    }\n\n    #[tokio::test]\n    async fn out_of_order_stream_of_sync_content_ready_immediately() {\n        let el: HtmlElement<Main, _, _, Dom> = main().child(p().child((\n            \"Hello, \",\n            em().child(\"beautiful\"),\n            \" world!\",\n        )));\n        let mut stream = el.to_html_stream_out_of_order();\n\n        let html = stream.next().await.unwrap();\n        assert_eq!(\n            html,\n            \"<main><p>Hello, <em>beautiful</em> world!</p></main>\"\n        );\n    }\n\n    #[tokio::test]\n    async fn out_of_order_single_async_block_in_stream() {\n        let el = async {\n            sleep(Duration::from_millis(250)).await;\n            \"Suspended\"\n        }\n        .suspend()\n        .with_fallback(\"Loading...\");\n        let mut stream =\n            <Suspend<false, _, _> as RenderHtml<Dom>>::to_html_stream_out_of_order(\n                el,\n            );\n\n        assert_eq!(\n            stream.next().await.unwrap(),\n            \"<!--s-1-o-->Loading...<!--s-1-c-->\"\n        );\n        assert_eq!(\n            stream.next().await.unwrap(),\n            \"<template id=\\\"1-f\\\">Suspended</template><script>(function() { \\\n             let id = \\\"1-\\\";let open = undefined;let close = undefined;let \\\n             walker = document.createTreeWalker(document.body, \\\n             NodeFilter.SHOW_COMMENT);while(walker.nextNode()) \\\n             {if(walker.currentNode.textContent == `s-${id}o`){ \\\n             open=walker.currentNode; } else \\\n             if(walker.currentNode.textContent == `s-${id}c`) { close = \\\n             walker.currentNode;}}let range = new Range(); \\\n             range.setStartAfter(open); range.setEndBefore(close); \\\n             range.deleteContents(); let tpl = \\\n             document.getElementById(`${id}f`); \\\n             close.parentNode.insertBefore(tpl.content.cloneNode(true), \\\n             close);})()</script>\"\n        );\n    }\n\n    #[tokio::test]\n    async fn out_of_order_inside_element_in_stream() {\n        let el: HtmlElement<_, _, _, Dom> = p().child((\n            \"Before Suspense\",\n            async {\n                sleep(Duration::from_millis(250)).await;\n                \"Suspended\"\n            }\n            .suspend()\n            .with_fallback(\"Loading...\"),\n            \"After Suspense\",\n        ));\n        let mut stream = el.to_html_stream_out_of_order();\n\n        assert_eq!(\n            stream.next().await.unwrap(),\n            \"<p>Before Suspense<!--s-1-o--><!>Loading...<!--s-1-c-->After \\\n             Suspense</p>\"\n        );\n        assert!(stream.next().await.unwrap().contains(\"Suspended\"));\n        assert!(stream.next().await.is_none());\n    }\n\n    #[tokio::test]\n    async fn out_of_order_nested_async_blocks() {\n        let el: HtmlElement<_, _, _, Dom> = main().child((\n            \"Before Suspense\",\n            async {\n                sleep(Duration::from_millis(250)).await;\n                p().child((\n                    \"Before inner Suspense\",\n                    async {\n                        sleep(Duration::from_millis(250)).await;\n                        \"Inner Suspense\"\n                    }\n                    .suspend()\n                    .with_fallback(\"Loading Inner...\"),\n                    \"After inner Suspense\",\n                ))\n            }\n            .suspend()\n            .with_fallback(\"Loading...\"),\n            \"After Suspense\",\n        ));\n        let mut stream = el.to_html_stream_out_of_order();\n\n        assert_eq!(\n            stream.next().await.unwrap(),\n            \"<main>Before Suspense<!--s-1-o--><!>Loading...<!--s-1-c-->After \\\n             Suspense</main>\"\n        );\n        let loading_inner = stream.next().await.unwrap();\n        assert!(loading_inner.contains(\n            \"<p>Before inner Suspense<!--s-1-1-o--><!>Loading \\\n             Inner...<!--s-1-1-c-->After inner Suspense</p>\"\n        ));\n        assert!(loading_inner.contains(\"let id = \\\"1-\\\";\"));\n\n        let inner = stream.next().await.unwrap();\n        assert!(inner.contains(\"Inner Suspense\"));\n        assert!(inner.contains(\"let id = \\\"1-1-\\\";\"));\n\n        assert!(stream.next().await.is_none());\n    }\n}\n*/\n"
  },
  {
    "path": "tachys/src/svg/mod.rs",
    "content": "use crate::{\n    html::{\n        attribute::{any_attribute::AnyAttribute, Attribute},\n        element::{ElementType, ElementWithChildren, HtmlElement},\n    },\n    hydration::Cursor,\n    prelude::{AddAnyAttr, Mountable},\n    renderer::{\n        dom::{Element, Node},\n        CastFrom, Rndr,\n    },\n    view::{Position, PositionState, Render, RenderHtml},\n};\nuse std::{borrow::Cow, fmt::Debug};\n\nmacro_rules! svg_elements {\n\t($($tag:ident  [$($attr:ty),*]),* $(,)?) => {\n        paste::paste! {\n            $(\n                /// An SVG element.\n                // `tag()` function\n                #[allow(non_snake_case)]\n                #[track_caller]\n                pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>\n                where\n                {\n                    HtmlElement {\n                        #[cfg(any(debug_assertions, leptos_debuginfo))]\n                        defined_at: std::panic::Location::caller(),\n                        tag: [<$tag:camel>],\n                        attributes: (),\n                        children: (),\n                    }\n                }\n\n                /// An SVG element.\n                #[derive(Debug, Copy, Clone, PartialEq, Eq)]\n                pub struct [<$tag:camel>];\n\n\t\t\t\timpl<At, Ch> HtmlElement<[<$tag:camel>], At, Ch>\n\t\t\t\twhere\n\t\t\t\t\tAt: Attribute,\n\t\t\t\t\tCh: Render,\n\n\t\t\t\t{\n\t\t\t\t\t$(\n                        pub fn $attr<V>(self, value: V) -> HtmlElement <\n                            [<$tag:camel>],\n                            <At as $crate::html::attribute::NextAttribute<Attr<$crate::html::attribute::[<$attr:camel>], V>>>::Output,\n                            Ch\n                        >\n                        where\n                            V: AttributeValue,\n                            At: $crate::html::attribute::NextAttribute<Attr<$crate::html::attribute::[<$attr:camel>], V>>,\n                            <At as $crate::html::attribute::NextAttribute<Attr<$crate::html::attribute::[<$attr:camel>], V>>>::Output: Attribute,\n                        {\n                            let HtmlElement { tag, children, attributes,\n                                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                                defined_at\n                            } = self;\n                            HtmlElement {\n                                tag,\n\n                                children,\n                                attributes: attributes.add_any_attr($crate::html::attribute::$attr(value)),\n                                #[cfg(any(debug_assertions, leptos_debuginfo))]\n                                defined_at\n                            }\n                        }\n\t\t\t\t\t)*\n\t\t\t\t}\n\n                impl ElementType for [<$tag:camel>] {\n                    type Output = web_sys::SvgElement;\n\n                    const TAG: &'static str = stringify!($tag);\n                    const SELF_CLOSING: bool = false;\n                    const ESCAPE_CHILDREN: bool = true;\n                    const NAMESPACE: Option<&'static str> = Some(\"http://www.w3.org/2000/svg\");\n\n                    #[inline(always)]\n                    fn tag(&self) -> &str {\n                        Self::TAG\n                    }\n                }\n\n                impl ElementWithChildren for [<$tag:camel>] {}\n            )*\n\t\t}\n    }\n}\n\nsvg_elements![\n  a [],\n  animate [],\n  animateMotion [],\n  animateTransform [],\n  circle [],\n  clipPath [],\n  defs [],\n  desc [],\n  discard [],\n  ellipse [],\n  feBlend [],\n  feColorMatrix [],\n  feComponentTransfer [],\n  feComposite [],\n  feConvolveMatrix [],\n  feDiffuseLighting [],\n  feDisplacementMap [],\n  feDistantLight [],\n  feDropShadow [],\n  feFlood [],\n  feFuncA [],\n  feFuncB [],\n  feFuncG [],\n  feFuncR [],\n  feGaussianBlur [],\n  feImage [],\n  feMerge [],\n  feMergeNode [],\n  feMorphology [],\n  feOffset [],\n  fePointLight [],\n  feSpecularLighting [],\n  feSpotLight [],\n  feTile [],\n  feTurbulence [],\n  filter [],\n  foreignObject [],\n  g [],\n  hatch [],\n  hatchpath [],\n  image [],\n  line [],\n  linearGradient [],\n  marker [],\n  mask [],\n  metadata [],\n  mpath [],\n  path [],\n  pattern [],\n  polygon [],\n  polyline [],\n  radialGradient [],\n  rect [],\n  script [],\n  set [],\n  stop [],\n  style [],\n  svg [],\n  switch [],\n  symbol [],\n  text [],\n  textPath [],\n  title [],\n  tspan [],\n  view [],\n];\n\n/// An SVG element.\n#[allow(non_snake_case)]\n#[track_caller]\npub fn r#use() -> HtmlElement<Use, (), ()>\nwhere {\n    HtmlElement {\n        #[cfg(any(debug_assertions, leptos_debuginfo))]\n        defined_at: std::panic::Location::caller(),\n        tag: Use,\n        attributes: (),\n        children: (),\n    }\n}\n\n/// An SVG element.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub struct Use;\n\nimpl ElementType for Use {\n    type Output = web_sys::SvgElement;\n\n    const TAG: &'static str = \"use\";\n    const SELF_CLOSING: bool = false;\n    const ESCAPE_CHILDREN: bool = true;\n    const NAMESPACE: Option<&'static str> = Some(\"http://www.w3.org/2000/svg\");\n\n    #[inline(always)]\n    fn tag(&self) -> &str {\n        Self::TAG\n    }\n}\n\nimpl ElementWithChildren for Use {}\n\n/// An element that contains no interactivity, and whose contents can be known at compile time.\npub struct InertElement {\n    html: Cow<'static, str>,\n}\n\nimpl InertElement {\n    /// Creates a new inert svg element.\n    pub fn new(html: impl Into<Cow<'static, str>>) -> Self {\n        Self { html: html.into() }\n    }\n}\n\n/// Retained view state for [`InertElement`].\npub struct InertElementState(Cow<'static, str>, Element);\n\nimpl Mountable for InertElementState {\n    fn unmount(&mut self) {\n        self.1.unmount();\n    }\n\n    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {\n        self.1.mount(parent, marker)\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.1.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![self.1.clone()]\n    }\n}\n\nimpl Render for InertElement {\n    type State = InertElementState;\n\n    fn build(self) -> Self::State {\n        let el = Rndr::create_svg_element_from_html(self.html.clone());\n        InertElementState(self.html, el)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let InertElementState(prev, el) = state;\n        if &self.html != prev {\n            let mut new_el =\n                Rndr::create_svg_element_from_html(self.html.clone());\n            el.insert_before_this(&mut new_el);\n            el.unmount();\n            *el = new_el;\n            *prev = self.html;\n        }\n    }\n}\n\nimpl AddAnyAttr for InertElement {\n    type Output<SomeNewAttr: Attribute> = Self;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        panic!(\n            \"InertElement does not support adding attributes. It should only \\\n             be used as a child, and not returned at the top level.\"\n        )\n    }\n}\n\nimpl RenderHtml for InertElement {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn html_len(&self) -> usize {\n        self.html.len()\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self {\n        self\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        _escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        buf.push_str(&self.html);\n        *position = Position::NextChild;\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let curr_position = position.get();\n        if curr_position == Position::FirstChild {\n            cursor.child();\n        } else if curr_position != Position::Current {\n            cursor.sibling();\n        }\n        let el = crate::renderer::types::Element::cast_from(cursor.current())\n            .unwrap();\n        position.set(Position::NextChild);\n        InertElementState(self.html, el)\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n"
  },
  {
    "path": "tachys/src/view/add_attr.rs",
    "content": "use super::RenderHtml;\nuse crate::html::attribute::Attribute;\n\n/// Allows adding a new attribute to some type, before it is rendered.\n/// This takes place at compile time as part of the builder syntax for creating a statically typed\n/// view tree.\n///\n/// Normally, this is used to add an attribute to an HTML element. But it is required to be\n/// implemented for all types that implement [`RenderHtml`], so that attributes can be spread onto\n/// other structures like the return type of a component.\npub trait AddAnyAttr {\n    /// The new type once the attribute has been added.\n    type Output<SomeNewAttr: Attribute>: RenderHtml;\n\n    /// Adds an attribute to the view.\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml;\n}\n\n/// Declares that spreading attributes onto a particular type has no effect.\n#[macro_export]\nmacro_rules! no_attrs {\n    ($ty_name:ty) => {\n        impl<'a> $crate::view::add_attr::AddAnyAttr for $ty_name {\n            type Output<SomeNewAttr: $crate::html::attribute::Attribute> =\n                $ty_name;\n\n            fn add_any_attr<NewAttr: $crate::html::attribute::Attribute>(\n                self,\n                _attr: NewAttr,\n            ) -> Self::Output<NewAttr> {\n                self\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "tachys/src/view/any_view.rs",
    "content": "#![allow(clippy::type_complexity)]\n#[cfg(feature = \"ssr\")]\nuse super::MarkBranch;\nuse super::{\n    add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n    RenderHtml,\n};\nuse crate::{\n    erased::{Erased, ErasedLocal},\n    html::attribute::{\n        any_attribute::{AnyAttribute, AnyAttributeState, IntoAnyAttribute},\n        Attribute,\n    },\n    hydration::Cursor,\n    renderer::Rndr,\n    ssr::StreamBuilder,\n};\nuse futures::future::{join, join_all};\nuse std::{any::TypeId, fmt::Debug};\n#[cfg(any(feature = \"ssr\", feature = \"hydrate\"))]\nuse std::{future::Future, pin::Pin};\n\n/// A type-erased view. This can be used if control flow requires that multiple different types of\n/// view must be received, and it is either impossible or too cumbersome to use the `EitherOf___`\n/// enums.\n///\n/// It can also be used to create recursive components, which otherwise cannot return themselves\n/// due to the static typing of the view tree.\n///\n/// Generally speaking, using `AnyView` restricts the amount of information available to the\n/// compiler and should be limited to situations in which it is necessary to preserve the maximum\n/// amount of type information possible.\npub struct AnyView {\n    type_id: TypeId,\n    value: Erased,\n    build: fn(Erased) -> AnyViewState,\n    rebuild: fn(Erased, &mut AnyViewState),\n    // The fields below are cfg-gated so they will not be included in WASM bundles if not needed.\n    // Ordinarily, the compiler can simply omit this dead code because the methods are not called.\n    // With this type-erased wrapper, however, the compiler is not *always* able to correctly\n    // eliminate that code.\n    #[cfg(feature = \"ssr\")]\n    html_len: usize,\n    #[cfg(feature = \"ssr\")]\n    to_html:\n        fn(Erased, &mut String, &mut Position, bool, bool, Vec<AnyAttribute>),\n    #[cfg(feature = \"ssr\")]\n    to_html_async: fn(\n        Erased,\n        &mut StreamBuilder,\n        &mut Position,\n        bool,\n        bool,\n        Vec<AnyAttribute>,\n    ),\n    #[cfg(feature = \"ssr\")]\n    to_html_async_ooo: fn(\n        Erased,\n        &mut StreamBuilder,\n        &mut Position,\n        bool,\n        bool,\n        Vec<AnyAttribute>,\n    ),\n    #[cfg(feature = \"ssr\")]\n    #[allow(clippy::type_complexity)]\n    resolve: fn(Erased) -> Pin<Box<dyn Future<Output = AnyView> + Send>>,\n    #[cfg(feature = \"ssr\")]\n    dry_resolve: fn(&mut Erased),\n    #[cfg(feature = \"hydrate\")]\n    #[allow(clippy::type_complexity)]\n    hydrate_from_server: fn(Erased, &Cursor, &PositionState) -> AnyViewState,\n    #[cfg(feature = \"hydrate\")]\n    #[allow(clippy::type_complexity)]\n    hydrate_async: fn(\n        Erased,\n        &Cursor,\n        &PositionState,\n    ) -> Pin<Box<dyn Future<Output = AnyViewState>>>,\n}\n\nimpl AnyView {\n    #[doc(hidden)]\n    pub fn as_type_id(&self) -> TypeId {\n        self.type_id\n    }\n}\n\nimpl Debug for AnyView {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AnyView\")\n            .field(\"type_id\", &self.type_id)\n            .finish_non_exhaustive()\n    }\n}\n/// Retained view state for [`AnyView`].\npub struct AnyViewState {\n    type_id: TypeId,\n    state: ErasedLocal,\n    unmount: fn(&mut ErasedLocal),\n    mount: fn(\n        &mut ErasedLocal,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ),\n    insert_before_this: fn(&ErasedLocal, child: &mut dyn Mountable) -> bool,\n    elements: fn(&ErasedLocal) -> Vec<crate::renderer::types::Element>,\n    placeholder: Option<crate::renderer::types::Placeholder>,\n}\n\nimpl Debug for AnyViewState {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AnyViewState\")\n            .field(\"type_id\", &self.type_id)\n            .field(\"state\", &\"\")\n            .field(\"unmount\", &self.unmount)\n            .field(\"mount\", &self.mount)\n            .field(\"insert_before_this\", &self.insert_before_this)\n            .finish()\n    }\n}\n\n/// Allows converting some view into [`AnyView`].\npub trait IntoAny {\n    /// Converts the view into a type-erased [`AnyView`].\n    fn into_any(self) -> AnyView;\n}\n\n/// A more general version of [`IntoAny`] that allows into [`AnyView`],\n/// but also erasing other types that don't implement [`RenderHtml`] like routing.\npub trait IntoMaybeErased {\n    /// The type of the output.\n    type Output: IntoMaybeErased;\n\n    /// Converts the view into a type-erased view if in erased mode.\n    fn into_maybe_erased(self) -> Self::Output;\n}\n\nimpl<T> IntoMaybeErased for T\nwhere\n    T: RenderHtml,\n{\n    #[cfg(not(erase_components))]\n    type Output = Self;\n\n    #[cfg(erase_components)]\n    type Output = AnyView;\n\n    fn into_maybe_erased(self) -> Self::Output {\n        #[cfg(not(erase_components))]\n        {\n            self\n        }\n        #[cfg(erase_components)]\n        {\n            self.into_owned().into_any()\n        }\n    }\n}\n\nfn mount_any<T>(\n    state: &mut ErasedLocal,\n    parent: &crate::renderer::types::Element,\n    marker: Option<&crate::renderer::types::Node>,\n) where\n    T: Render,\n    T::State: 'static,\n{\n    state.get_mut::<T::State>().mount(parent, marker)\n}\n\nfn unmount_any<T>(state: &mut ErasedLocal)\nwhere\n    T: Render,\n    T::State: 'static,\n{\n    state.get_mut::<T::State>().unmount();\n}\n\nfn insert_before_this<T>(state: &ErasedLocal, child: &mut dyn Mountable) -> bool\nwhere\n    T: Render,\n    T::State: 'static,\n{\n    state.get_ref::<T::State>().insert_before_this(child)\n}\n\nfn elements<T>(state: &ErasedLocal) -> Vec<crate::renderer::types::Element>\nwhere\n    T: Render,\n    T::State: 'static,\n{\n    state.get_ref::<T::State>().elements()\n}\n\nimpl<T> IntoAny for T\nwhere\n    T: Send,\n    T: RenderHtml,\n{\n    fn into_any(self) -> AnyView {\n        #[cfg(feature = \"ssr\")]\n        fn dry_resolve<T: RenderHtml + 'static>(value: &mut Erased) {\n            value.get_mut::<T>().dry_resolve();\n        }\n\n        #[cfg(feature = \"ssr\")]\n        fn resolve<T: RenderHtml + 'static>(\n            value: Erased,\n        ) -> Pin<Box<dyn Future<Output = AnyView> + Send>> {\n            use futures::FutureExt;\n\n            async move { value.into_inner::<T>().resolve().await.into_any() }\n                .boxed()\n        }\n\n        #[cfg(feature = \"ssr\")]\n        fn to_html<T: RenderHtml + 'static>(\n            value: Erased,\n            buf: &mut String,\n            position: &mut Position,\n            escape: bool,\n            mark_branches: bool,\n            extra_attrs: Vec<AnyAttribute>,\n        ) {\n            value.into_inner::<T>().to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n            if !T::EXISTS {\n                buf.push_str(\"<!--<() />-->\");\n            }\n        }\n\n        #[cfg(feature = \"ssr\")]\n        fn to_html_async<T: RenderHtml + 'static>(\n            value: Erased,\n            buf: &mut StreamBuilder,\n            position: &mut Position,\n            escape: bool,\n            mark_branches: bool,\n            extra_attrs: Vec<AnyAttribute>,\n        ) {\n            value.into_inner::<T>().to_html_async_with_buf::<false>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n            if !T::EXISTS {\n                buf.push_sync(\"<!--<() />-->\");\n            }\n        }\n\n        #[cfg(feature = \"ssr\")]\n        fn to_html_async_ooo<T: RenderHtml + 'static>(\n            value: Erased,\n            buf: &mut StreamBuilder,\n            position: &mut Position,\n            escape: bool,\n            mark_branches: bool,\n            extra_attrs: Vec<AnyAttribute>,\n        ) {\n            value.into_inner::<T>().to_html_async_with_buf::<true>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n            if !T::EXISTS {\n                buf.push_sync(\"<!--<() />-->\");\n            }\n        }\n\n        fn build<T: RenderHtml + 'static>(value: Erased) -> AnyViewState {\n            let state = ErasedLocal::new(value.into_inner::<T>().build());\n            let placeholder = (!T::EXISTS).then(Rndr::create_placeholder);\n            AnyViewState {\n                type_id: TypeId::of::<T>(),\n                state,\n                mount: mount_any::<T>,\n                unmount: unmount_any::<T>,\n                insert_before_this: insert_before_this::<T>,\n                elements: elements::<T>,\n                placeholder,\n            }\n        }\n\n        #[cfg(feature = \"hydrate\")]\n        fn hydrate_from_server<T: RenderHtml + 'static>(\n            value: Erased,\n            cursor: &Cursor,\n            position: &PositionState,\n        ) -> AnyViewState {\n            let state = ErasedLocal::new(\n                value.into_inner::<T>().hydrate::<true>(cursor, position),\n            );\n            let placeholder =\n                (!T::EXISTS).then(|| cursor.next_placeholder(position));\n            AnyViewState {\n                type_id: TypeId::of::<T>(),\n                state,\n                mount: mount_any::<T>,\n                unmount: unmount_any::<T>,\n                insert_before_this: insert_before_this::<T>,\n                elements: elements::<T>,\n                placeholder,\n            }\n        }\n\n        #[cfg(feature = \"hydrate\")]\n        fn hydrate_async<T: RenderHtml + 'static>(\n            value: Erased,\n            cursor: &Cursor,\n            position: &PositionState,\n        ) -> Pin<Box<dyn Future<Output = AnyViewState>>> {\n            let cursor = cursor.clone();\n            let position = position.clone();\n            Box::pin(async move {\n                let state = ErasedLocal::new(\n                    value\n                        .into_inner::<T>()\n                        .hydrate_async(&cursor, &position)\n                        .await,\n                );\n                let placeholder =\n                    (!T::EXISTS).then(|| cursor.next_placeholder(&position));\n                AnyViewState {\n                    type_id: TypeId::of::<T>(),\n                    state,\n                    mount: mount_any::<T>,\n                    unmount: unmount_any::<T>,\n                    insert_before_this: insert_before_this::<T>,\n                    elements: elements::<T>,\n                    placeholder,\n                }\n            })\n        }\n\n        fn rebuild<T: RenderHtml + 'static>(\n            value: Erased,\n            state: &mut AnyViewState,\n        ) {\n            let state = state.state.get_mut::<<T as Render>::State>();\n            value.into_inner::<T>().rebuild(state);\n        }\n\n        let value = self.into_owned();\n        AnyView {\n            type_id: TypeId::of::<T::Owned>(),\n            build: build::<T::Owned>,\n            rebuild: rebuild::<T::Owned>,\n            #[cfg(feature = \"ssr\")]\n            resolve: resolve::<T::Owned>,\n            #[cfg(feature = \"ssr\")]\n            dry_resolve: dry_resolve::<T::Owned>,\n            #[cfg(feature = \"ssr\")]\n            html_len: value.html_len(),\n            #[cfg(feature = \"ssr\")]\n            to_html: to_html::<T::Owned>,\n            #[cfg(feature = \"ssr\")]\n            to_html_async: to_html_async::<T::Owned>,\n            #[cfg(feature = \"ssr\")]\n            to_html_async_ooo: to_html_async_ooo::<T::Owned>,\n            #[cfg(feature = \"hydrate\")]\n            hydrate_from_server: hydrate_from_server::<T::Owned>,\n            #[cfg(feature = \"hydrate\")]\n            hydrate_async: hydrate_async::<T::Owned>,\n            value: Erased::new(value),\n        }\n    }\n}\n\nimpl Render for AnyView {\n    type State = AnyViewState;\n\n    fn build(self) -> Self::State {\n        (self.build)(self.value)\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        if self.type_id == state.type_id {\n            (self.rebuild)(self.value, state)\n        } else {\n            let mut new = self.build();\n            if let Some(placeholder) = &mut state.placeholder {\n                placeholder.insert_before_this(&mut new);\n                placeholder.unmount();\n            } else {\n                state.insert_before_this(&mut new);\n            }\n            state.unmount();\n            *state = new;\n        }\n    }\n}\n\nimpl AddAnyAttr for AnyView {\n    type Output<SomeNewAttr: Attribute> = AnyViewWithAttrs;\n\n    #[allow(unused_variables)]\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        AnyViewWithAttrs {\n            view: self,\n            attrs: vec![attr.into_cloneable_owned().into_any_attr()],\n        }\n    }\n}\n\nimpl RenderHtml for AnyView {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    fn dry_resolve(&mut self) {\n        #[cfg(feature = \"ssr\")]\n        {\n            (self.dry_resolve)(&mut self.value)\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        panic!(\n            \"You are rendering AnyView to HTML without the `ssr` feature \\\n             enabled.\"\n        );\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        #[cfg(feature = \"ssr\")]\n        {\n            (self.resolve)(self.value).await\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        panic!(\n            \"You are rendering AnyView to HTML without the `ssr` feature \\\n             enabled.\"\n        );\n    }\n\n    const MIN_LENGTH: usize = 0;\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        #[cfg(feature = \"ssr\")]\n        {\n            let type_id = if mark_branches && escape {\n                format!(\"{:?}\", self.type_id)\n            } else {\n                Default::default()\n            };\n            if mark_branches && escape {\n                buf.open_branch(&type_id);\n            }\n            (self.to_html)(\n                self.value,\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n            if mark_branches && escape {\n                buf.close_branch(&type_id);\n                if *position == Position::NextChildAfterText {\n                    *position = Position::NextChild;\n                }\n            }\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        {\n            _ = mark_branches;\n            _ = buf;\n            _ = position;\n            _ = escape;\n            _ = extra_attrs;\n            panic!(\n                \"You are rendering AnyView to HTML without the `ssr` feature \\\n                 enabled.\"\n            );\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        #[cfg(feature = \"ssr\")]\n        if OUT_OF_ORDER {\n            let type_id = if mark_branches && escape {\n                format!(\"{:?}\", self.type_id)\n            } else {\n                Default::default()\n            };\n            if mark_branches && escape {\n                buf.open_branch(&type_id);\n            }\n            (self.to_html_async_ooo)(\n                self.value,\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n            if mark_branches && escape {\n                buf.close_branch(&type_id);\n                if *position == Position::NextChildAfterText {\n                    *position = Position::NextChild;\n                }\n            }\n        } else {\n            let type_id = if mark_branches && escape {\n                format!(\"{:?}\", self.type_id)\n            } else {\n                Default::default()\n            };\n            if mark_branches && escape {\n                buf.open_branch(&type_id);\n            }\n            (self.to_html_async)(\n                self.value,\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            );\n            if mark_branches && escape {\n                buf.close_branch(&type_id);\n                if *position == Position::NextChildAfterText {\n                    *position = Position::NextChild;\n                }\n            }\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        {\n            _ = buf;\n            _ = position;\n            _ = escape;\n            _ = mark_branches;\n            _ = extra_attrs;\n            panic!(\n                \"You are rendering AnyView to HTML without the `ssr` feature \\\n                 enabled.\"\n            );\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        #[cfg(feature = \"hydrate\")]\n        {\n            if FROM_SERVER {\n                (self.hydrate_from_server)(self.value, cursor, position)\n            } else {\n                panic!(\n                    \"hydrating AnyView from inside a ViewTemplate is not \\\n                     supported.\"\n                );\n            }\n        }\n        #[cfg(not(feature = \"hydrate\"))]\n        {\n            _ = cursor;\n            _ = position;\n            panic!(\n                \"You are trying to hydrate AnyView without the `hydrate` \\\n                 feature enabled.\"\n            );\n        }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        #[cfg(feature = \"hydrate\")]\n        {\n            let state =\n                (self.hydrate_async)(self.value, cursor, position).await;\n            state\n        }\n        #[cfg(not(feature = \"hydrate\"))]\n        {\n            _ = cursor;\n            _ = position;\n            panic!(\n                \"You are trying to hydrate AnyView without the `hydrate` \\\n                 feature enabled.\"\n            );\n        }\n    }\n\n    fn html_len(&self) -> usize {\n        #[cfg(feature = \"ssr\")]\n        {\n            self.html_len\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        {\n            0\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl Mountable for AnyViewState {\n    fn unmount(&mut self) {\n        (self.unmount)(&mut self.state);\n        if let Some(placeholder) = &mut self.placeholder {\n            placeholder.unmount();\n        }\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        (self.mount)(&mut self.state, parent, marker);\n        if let Some(placeholder) = &mut self.placeholder {\n            placeholder.mount(parent, marker);\n        }\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        let before_view = (self.insert_before_this)(&self.state, child);\n        if before_view {\n            return true;\n        }\n\n        if let Some(placeholder) = &self.placeholder {\n            placeholder.insert_before_this(child)\n        } else {\n            false\n        }\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        (self.elements)(&self.state)\n    }\n}\n\n/// wip\npub struct AnyViewWithAttrs {\n    view: AnyView,\n    attrs: Vec<AnyAttribute>,\n}\n\nimpl Render for AnyViewWithAttrs {\n    type State = AnyViewWithAttrsState;\n\n    fn build(self) -> Self::State {\n        let view = self.view.build();\n        let elements = view.elements();\n        let mut attrs = Vec::with_capacity(elements.len() * self.attrs.len());\n        for attr in self.attrs {\n            for el in &elements {\n                attrs.push(attr.clone().build(el))\n            }\n        }\n        AnyViewWithAttrsState { view, attrs }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.view.rebuild(&mut state.view);\n\n        // at this point, we have rebuilt the inner view\n        // now we need to update attributes that were spread onto this\n        // this approach is not ideal, but it avoids two edge cases:\n        // 1) merging attributes from two unrelated views (https://github.com/leptos-rs/leptos/issues/4268)\n        // 2) failing to re-create attributes from the same view (https://github.com/leptos-rs/leptos/issues/4512)\n        for element in state.elements() {\n            // first, remove the previous set of attributes\n            self.attrs\n                .clone()\n                .rebuild(&mut (element.clone(), Vec::new()));\n            // then, add the new set of attributes\n            self.attrs.clone().build(&element);\n        }\n    }\n}\n\nimpl RenderHtml for AnyViewWithAttrs {\n    type AsyncOutput = Self;\n    type Owned = Self;\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {\n        self.view.dry_resolve();\n        for attr in &mut self.attrs {\n            attr.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        let resolve_view = self.view.resolve();\n        let resolve_attrs =\n            join_all(self.attrs.into_iter().map(|attr| attr.resolve()));\n        let (view, attrs) = join(resolve_view, resolve_attrs).await;\n        Self { view, attrs }\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        mut extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // `extra_attrs` will be empty here in most cases, but it will have\n        // attributes in it already if this is, itself, receiving additional attrs\n        extra_attrs.extend(self.attrs);\n        self.view.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        mut extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        extra_attrs.extend(self.attrs);\n        self.view.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let view = self.view.hydrate::<FROM_SERVER>(cursor, position);\n        let elements = view.elements();\n        let mut attrs = Vec::with_capacity(elements.len() * self.attrs.len());\n        for attr in self.attrs {\n            for el in &elements {\n                attrs.push(attr.clone().hydrate::<FROM_SERVER>(el));\n            }\n        }\n        AnyViewWithAttrsState { view, attrs }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let view = self.view.hydrate_async(cursor, position).await;\n        let elements = view.elements();\n        let mut attrs = Vec::with_capacity(elements.len() * self.attrs.len());\n        for attr in self.attrs {\n            for el in &elements {\n                attrs.push(attr.clone().hydrate::<true>(el));\n            }\n        }\n        AnyViewWithAttrsState { view, attrs }\n    }\n\n    fn html_len(&self) -> usize {\n        self.view.html_len()\n            + self.attrs.iter().map(|attr| attr.html_len()).sum::<usize>()\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl AddAnyAttr for AnyViewWithAttrs {\n    type Output<SomeNewAttr: Attribute> = AnyViewWithAttrs;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        mut self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        self.attrs.push(attr.into_cloneable_owned().into_any_attr());\n        self\n    }\n}\n\n/// State for any view with attributes spread onto it.\npub struct AnyViewWithAttrsState {\n    view: AnyViewState,\n    #[allow(dead_code)] // keeps attribute states alive until dropped\n    attrs: Vec<AnyAttributeState>,\n}\n\nimpl Mountable for AnyViewWithAttrsState {\n    fn unmount(&mut self) {\n        self.view.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        self.view.mount(parent, marker)\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.view.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.view.elements()\n    }\n}\n\n/*\n#[cfg(test)]\nmod tests {\n    use super::IntoAny;\n    use crate::{\n        html::element::{p, span},\n        renderer::mock_dom::MockDom,\n        view::{any_view::AnyView, RenderHtml},\n    };\n\n    #[test]\n    fn should_handle_html_creation() {\n        let x = 1;\n        let mut buf = String::new();\n        let view: AnyView<MockDom> = if x == 0 {\n            p((), \"foo\").into_any()\n        } else {\n            span((), \"bar\").into_any()\n        };\n        view.to_html(&mut buf, &Default::default());\n        assert_eq!(buf, \"<span>bar</span><!>\");\n    }\n}\n */\n"
  },
  {
    "path": "tachys/src/view/either.rs",
    "content": "use super::{\n    add_attr::AddAnyAttr, MarkBranch, Mountable, Position, PositionState,\n    Render, RenderHtml,\n};\nuse crate::{\n    html::attribute::{\n        any_attribute::AnyAttribute, Attribute, NamedAttributeKey,\n        NextAttribute,\n    },\n    hydration::Cursor,\n    ssr::StreamBuilder,\n};\nuse either_of::*;\nuse futures::future::join;\n\nimpl<A, B> Render for Either<A, B>\nwhere\n    A: Render,\n    B: Render,\n{\n    type State = Either<A::State, B::State>;\n\n    fn build(self) -> Self::State {\n        match self {\n            Either::Left(left) => Either::Left(left.build()),\n            Either::Right(right) => Either::Right(right.build()),\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        match (self, &mut *state) {\n            (Either::Left(new), Either::Left(old)) => {\n                new.rebuild(old);\n            }\n            (Either::Right(new), Either::Right(old)) => {\n                new.rebuild(old);\n            }\n            (Either::Right(new), Either::Left(old)) => {\n                let mut new_state = new.build();\n                old.insert_before_this(&mut new_state);\n                old.unmount();\n                *state = Either::Right(new_state);\n            }\n            (Either::Left(new), Either::Right(old)) => {\n                let mut new_state = new.build();\n                old.insert_before_this(&mut new_state);\n                old.unmount();\n                *state = Either::Left(new_state);\n            }\n        }\n    }\n}\n\nimpl<A, B> Mountable for Either<A, B>\nwhere\n    A: Mountable,\n    B: Mountable,\n{\n    fn unmount(&mut self) {\n        match self {\n            Either::Left(left) => left.unmount(),\n            Either::Right(right) => right.unmount(),\n        }\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        match self {\n            Either::Left(left) => left.mount(parent, marker),\n            Either::Right(right) => right.mount(parent, marker),\n        }\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        match &self {\n            Either::Left(left) => left.insert_before_this(child),\n            Either::Right(right) => right.insert_before_this(child),\n        }\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        match &self {\n            Either::Left(left) => left.elements(),\n            Either::Right(right) => right.elements(),\n        }\n    }\n}\n\nimpl<A, B> AddAnyAttr for Either<A, B>\nwhere\n    A: RenderHtml,\n    B: RenderHtml,\n{\n    type Output<SomeNewAttr: Attribute> = Either<\n        <A as AddAnyAttr>::Output<SomeNewAttr>,\n        <B as AddAnyAttr>::Output<SomeNewAttr>,\n    >;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        match self {\n            Either::Left(i) => Either::Left(i.add_any_attr(attr)),\n            Either::Right(i) => Either::Right(i.add_any_attr(attr)),\n        }\n    }\n}\n\nconst fn max_usize(vals: &[usize]) -> usize {\n    let mut max = 0;\n    let len = vals.len();\n    let mut i = 0;\n    while i < len {\n        if vals[i] > max {\n            max = vals[i];\n        }\n        i += 1;\n    }\n    max\n}\n\n#[cfg(not(erase_components))]\nimpl<A, B> NextAttribute for Either<A, B>\nwhere\n    B: NextAttribute,\n    A: NextAttribute,\n{\n    type Output<NewAttr: Attribute> = Either<\n        <A as NextAttribute>::Output<NewAttr>,\n        <B as NextAttribute>::Output<NewAttr>,\n    >;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        match self {\n            Either::Left(left) => Either::Left(left.add_any_attr(new_attr)),\n            Either::Right(right) => Either::Right(right.add_any_attr(new_attr)),\n        }\n    }\n}\n\n#[cfg(erase_components)]\nimpl<A, B> NextAttribute for Either<A, B>\nwhere\n    B: crate::html::attribute::any_attribute::IntoAnyAttribute,\n    A: crate::html::attribute::any_attribute::IntoAnyAttribute,\n{\n    type Output<NewAttr: Attribute> = Vec<AnyAttribute>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        use crate::html::attribute::any_attribute::IntoAnyAttribute;\n\n        vec![\n            match self {\n                Either::Left(left) => left.into_any_attr(),\n                Either::Right(right) => right.into_any_attr(),\n            },\n            new_attr.into_any_attr(),\n        ]\n    }\n}\n\nimpl<A, B> Attribute for Either<A, B>\nwhere\n    B: Attribute,\n    A: Attribute,\n{\n    const MIN_LENGTH: usize = max_usize(&[A::MIN_LENGTH, B::MIN_LENGTH]);\n\n    type AsyncOutput = Either<A::AsyncOutput, B::AsyncOutput>;\n    type State = Either<A::State, B::State>;\n    type Cloneable = Either<A::Cloneable, B::Cloneable>;\n    type CloneableOwned = Either<A::CloneableOwned, B::CloneableOwned>;\n\n    fn html_len(&self) -> usize {\n        match self {\n            Either::Left(left) => left.html_len(),\n            Either::Right(right) => right.html_len(),\n        }\n    }\n\n    fn to_html(\n        self,\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n    ) {\n        match self {\n            Either::Left(left) => left.to_html(buf, class, style, inner_html),\n            Either::Right(right) => {\n                right.to_html(buf, class, style, inner_html)\n            }\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State {\n        match self {\n            Either::Left(left) => Either::Left(left.hydrate::<FROM_SERVER>(el)),\n            Either::Right(right) => {\n                Either::Right(right.hydrate::<FROM_SERVER>(el))\n            }\n        }\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        match self {\n            Either::Left(left) => Either::Left(left.build(el)),\n            Either::Right(right) => Either::Right(right.build(el)),\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        match self {\n            Either::Left(left) => {\n                if let Some(state) = state.as_left_mut() {\n                    left.rebuild(state)\n                }\n            }\n            Either::Right(right) => {\n                if let Some(state) = state.as_right_mut() {\n                    right.rebuild(state)\n                }\n            }\n        }\n    }\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        match self {\n            Either::Left(left) => Either::Left(left.into_cloneable()),\n            Either::Right(right) => Either::Right(right.into_cloneable()),\n        }\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        match self {\n            Either::Left(left) => Either::Left(left.into_cloneable_owned()),\n            Either::Right(right) => Either::Right(right.into_cloneable_owned()),\n        }\n    }\n\n    fn dry_resolve(&mut self) {\n        match self {\n            Either::Left(left) => left.dry_resolve(),\n            Either::Right(right) => right.dry_resolve(),\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        match self {\n            Either::Left(left) => Either::Left(left.resolve().await),\n            Either::Right(right) => Either::Right(right.resolve().await),\n        }\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        match self {\n            Either::Left(left) => left.keys(),\n            Either::Right(right) => right.keys(),\n        }\n    }\n}\n\nimpl<A, B> RenderHtml for Either<A, B>\nwhere\n    A: RenderHtml,\n    B: RenderHtml,\n{\n    type AsyncOutput = Either<A::AsyncOutput, B::AsyncOutput>;\n    type Owned = Either<A::Owned, B::Owned>;\n\n    fn dry_resolve(&mut self) {\n        match self {\n            Either::Left(left) => left.dry_resolve(),\n            Either::Right(right) => right.dry_resolve(),\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        match self {\n            Either::Left(left) => Either::Left(left.resolve().await),\n            Either::Right(right) => Either::Right(right.resolve().await),\n        }\n    }\n\n    const MIN_LENGTH: usize = max_usize(&[A::MIN_LENGTH, B::MIN_LENGTH]);\n\n    #[inline(always)]\n    fn html_len(&self) -> usize {\n        match self {\n            Either::Left(i) => i.html_len(),\n            Either::Right(i) => i.html_len(),\n        }\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        match self {\n            Either::Left(left) => {\n                if mark_branches && escape {\n                    buf.open_branch(\"0\");\n                }\n                left.to_html_with_buf(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n                if mark_branches && escape {\n                    buf.close_branch(\"0\");\n                    if *position == Position::NextChildAfterText {\n                        *position = Position::NextChild;\n                    }\n                }\n            }\n            Either::Right(right) => {\n                if mark_branches && escape {\n                    buf.open_branch(\"1\");\n                }\n                right.to_html_with_buf(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n                if mark_branches && escape {\n                    buf.close_branch(\"1\");\n                    if *position == Position::NextChildAfterText {\n                        *position = Position::NextChild;\n                    }\n                }\n            }\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        match self {\n            Either::Left(left) => {\n                if mark_branches && escape {\n                    buf.open_branch(\"0\");\n                }\n                left.to_html_async_with_buf::<OUT_OF_ORDER>(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n                if mark_branches && escape {\n                    buf.close_branch(\"0\");\n                    if *position == Position::NextChildAfterText {\n                        *position = Position::NextChild;\n                    }\n                }\n            }\n            Either::Right(right) => {\n                if mark_branches && escape {\n                    buf.open_branch(\"1\");\n                }\n                right.to_html_async_with_buf::<OUT_OF_ORDER>(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n                if mark_branches && escape {\n                    buf.close_branch(\"1\");\n                    if *position == Position::NextChildAfterText {\n                        *position = Position::NextChild;\n                    }\n                }\n            }\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        match self {\n            Either::Left(left) => {\n                Either::Left(left.hydrate::<FROM_SERVER>(cursor, position))\n            }\n            Either::Right(right) => {\n                Either::Right(right.hydrate::<FROM_SERVER>(cursor, position))\n            }\n        }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        match self {\n            Either::Left(left) => {\n                Either::Left(left.hydrate_async(cursor, position).await)\n            }\n            Either::Right(right) => {\n                Either::Right(right.hydrate_async(cursor, position).await)\n            }\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        match self {\n            Either::Left(left) => Either::Left(left.into_owned()),\n            Either::Right(right) => Either::Right(right.into_owned()),\n        }\n    }\n}\n\n/// Stores each value in the view state, overwriting it only if `Some(_)` is provided.\npub struct EitherKeepAlive<A, B> {\n    /// The first possibility.\n    pub a: Option<A>,\n    /// The second possibility.\n    pub b: Option<B>,\n    /// If `true`, then `b` will be shown.\n    pub show_b: bool,\n}\n\n/// Retained view state for [`EitherKeepAlive`].\npub struct EitherKeepAliveState<A, B> {\n    a: Option<A>,\n    b: Option<B>,\n    showing_b: bool,\n}\n\nimpl<A, B> Render for EitherKeepAlive<A, B>\nwhere\n    A: Render,\n    B: Render,\n{\n    type State = EitherKeepAliveState<A::State, B::State>;\n\n    fn build(self) -> Self::State {\n        let showing_b = self.show_b;\n        let a = self.a.map(Render::build);\n        let b = self.b.map(Render::build);\n        EitherKeepAliveState { a, b, showing_b }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        // set or update A -- `None` just means \"no change\"\n        match (self.a, &mut state.a) {\n            (Some(new), Some(old)) => new.rebuild(old),\n            (Some(new), None) => state.a = Some(new.build()),\n            _ => {}\n        }\n\n        // set or update B\n        match (self.b, &mut state.b) {\n            (Some(new), Some(old)) => new.rebuild(old),\n            (Some(new), None) => state.b = Some(new.build()),\n            _ => {}\n        }\n\n        match (self.show_b, state.showing_b) {\n            // transition from A to B\n            (true, false) => match (&mut state.a, &mut state.b) {\n                (Some(a), Some(b)) => {\n                    a.insert_before_this(b);\n                    a.unmount();\n                }\n                _ => unreachable!(),\n            },\n            // transition from B to A\n            (false, true) => match (&mut state.a, &mut state.b) {\n                (Some(a), Some(b)) => {\n                    b.insert_before_this(a);\n                    b.unmount();\n                }\n                _ => unreachable!(),\n            },\n            _ => {}\n        }\n        state.showing_b = self.show_b;\n    }\n}\n\nimpl<A, B> AddAnyAttr for EitherKeepAlive<A, B>\nwhere\n    A: RenderHtml,\n    B: RenderHtml,\n{\n    type Output<SomeNewAttr: Attribute> = EitherKeepAlive<\n        <A as AddAnyAttr>::Output<SomeNewAttr::Cloneable>,\n        <B as AddAnyAttr>::Output<SomeNewAttr::Cloneable>,\n    >;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let EitherKeepAlive { a, b, show_b } = self;\n        let attr = attr.into_cloneable();\n        EitherKeepAlive {\n            a: a.map(|a| a.add_any_attr(attr.clone())),\n            b: b.map(|b| b.add_any_attr(attr.clone())),\n            show_b,\n        }\n    }\n}\n\nimpl<A, B> RenderHtml for EitherKeepAlive<A, B>\nwhere\n    A: RenderHtml,\n    B: RenderHtml,\n{\n    type AsyncOutput = EitherKeepAlive<A::AsyncOutput, B::AsyncOutput>;\n    type Owned = EitherKeepAlive<A::Owned, B::Owned>;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {\n        if let Some(inner) = &mut self.a {\n            inner.dry_resolve();\n        }\n        if let Some(inner) = &mut self.b {\n            inner.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        let EitherKeepAlive { a, b, show_b } = self;\n        let (a, b) = join(\n            async move {\n                match a {\n                    Some(a) => Some(a.resolve().await),\n                    None => None,\n                }\n            },\n            async move {\n                match b {\n                    Some(b) => Some(b.resolve().await),\n                    None => None,\n                }\n            },\n        )\n        .await;\n        EitherKeepAlive { a, b, show_b }\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        if self.show_b {\n            self.b\n                .expect(\"rendering B to HTML without filling it\")\n                .to_html_with_buf(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n        } else {\n            self.a\n                .expect(\"rendering A to HTML without filling it\")\n                .to_html_with_buf(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        if self.show_b {\n            self.b\n                .expect(\"rendering B to HTML without filling it\")\n                .to_html_async_with_buf::<OUT_OF_ORDER>(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n        } else {\n            self.a\n                .expect(\"rendering A to HTML without filling it\")\n                .to_html_async_with_buf::<OUT_OF_ORDER>(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let showing_b = self.show_b;\n        let a = self.a.map(|a| {\n            if showing_b {\n                a.build()\n            } else {\n                a.hydrate::<FROM_SERVER>(cursor, position)\n            }\n        });\n        let b = self.b.map(|b| {\n            if showing_b {\n                b.hydrate::<FROM_SERVER>(cursor, position)\n            } else {\n                b.build()\n            }\n        });\n\n        EitherKeepAliveState { showing_b, a, b }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let showing_b = self.show_b;\n        let a = if let Some(a) = self.a {\n            Some(if showing_b {\n                a.build()\n            } else {\n                a.hydrate_async(cursor, position).await\n            })\n        } else {\n            None\n        };\n        let b = if let Some(b) = self.b {\n            Some(if showing_b {\n                b.hydrate_async(cursor, position).await\n            } else {\n                b.build()\n            })\n        } else {\n            None\n        };\n\n        EitherKeepAliveState { showing_b, a, b }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        EitherKeepAlive {\n            a: self.a.map(|a| a.into_owned()),\n            b: self.b.map(|b| b.into_owned()),\n            show_b: self.show_b,\n        }\n    }\n}\n\nimpl<A, B> Mountable for EitherKeepAliveState<A, B>\nwhere\n    A: Mountable,\n    B: Mountable,\n{\n    fn unmount(&mut self) {\n        if self.showing_b {\n            self.b.as_mut().expect(\"B was not present\").unmount();\n        } else {\n            self.a.as_mut().expect(\"A was not present\").unmount();\n        }\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        if self.showing_b {\n            self.b\n                .as_mut()\n                .expect(\"B was not present\")\n                .mount(parent, marker);\n        } else {\n            self.a\n                .as_mut()\n                .expect(\"A was not present\")\n                .mount(parent, marker);\n        }\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        if self.showing_b {\n            self.b\n                .as_ref()\n                .expect(\"B was not present\")\n                .insert_before_this(child)\n        } else {\n            self.a\n                .as_ref()\n                .expect(\"A was not present\")\n                .insert_before_this(child)\n        }\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        if self.showing_b {\n            self.b\n                .as_ref()\n                .map(|inner| inner.elements())\n                .unwrap_or_default()\n        } else {\n            self.a\n                .as_ref()\n                .map(|inner| inner.elements())\n                .unwrap_or_default()\n        }\n    }\n}\n\nmacro_rules! tuples {\n    ($num:literal => $($ty:ident),*) => {\n        paste::paste! {\n            #[doc = concat!(\"Retained view state for \", stringify!([<EitherOf $num>]), \".\")]\n            pub struct [<EitherOf $num State>]<$($ty,)*>\n            where\n                $($ty: Render,)*\n\n            {\n                /// Which child view state is being displayed.\n                pub state: [<EitherOf $num>]<$($ty::State,)*>,\n            }\n\n            impl<$($ty,)*> Mountable for [<EitherOf $num State>]<$($ty,)*>\n            where\n                $($ty: Render,)*\n\n            {\n                fn unmount(&mut self) {\n                    match &mut self.state {\n                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.unmount()),)*\n                    };\n                }\n\n                fn mount(\n                    &mut self,\n                    parent: &crate::renderer::types::Element,\n                    marker: Option<&crate::renderer::types::Node>,\n                ) {\n                    match &mut self.state {\n                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.mount(parent, marker)),)*\n                    };\n                }\n\n                fn insert_before_this(&self,\n                    child: &mut dyn Mountable,\n                ) -> bool {\n                    match &self.state {\n                        $([<EitherOf $num>]::$ty(this) =>this.insert_before_this(child),)*\n                    }\n                }\n\n                fn elements(&self) -> Vec<crate::renderer::types::Element> {\n                    match &self.state {\n                        $([<EitherOf $num>]::$ty(this) => this.elements(),)*\n                    }\n                }\n            }\n\n            impl<$($ty,)*> Render for [<EitherOf $num>]<$($ty,)*>\n            where\n                $($ty: Render,)*\n\n            {\n                type State = [<EitherOf $num State>]<$($ty,)*>;\n\n\n                fn build(self) -> Self::State {\n                    let state = match self {\n                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.build()),)*\n                    };\n                    Self::State { state }\n                }\n\n                fn rebuild(self, state: &mut Self::State) {\n                    let new_state = match (self, &mut state.state) {\n                        // rebuild same state and return early\n                        $(([<EitherOf $num>]::$ty(new), [<EitherOf $num>]::$ty(old)) => { return new.rebuild(old); },)*\n                        // or mount new state\n                        $(([<EitherOf $num>]::$ty(new), _) => {\n                            let mut new = new.build();\n                            state.insert_before_this(&mut new);\n                            [<EitherOf $num>]::$ty(new)\n                        },)*\n                    };\n\n                    // and then unmount old state\n                    match &mut state.state {\n                        $([<EitherOf $num>]::$ty(this) => this.unmount(),)*\n                    };\n\n                    // and store the new state\n                    state.state = new_state;\n                }\n            }\n\n            impl<$($ty,)*> AddAnyAttr for [<EitherOf $num>]<$($ty,)*>\n            where\n                $($ty: RenderHtml,)*\n\n            {\n                type Output<SomeNewAttr: Attribute> = [<EitherOf $num>]<\n                    $(<$ty as AddAnyAttr>::Output<SomeNewAttr>,)*\n                >;\n\n                fn add_any_attr<NewAttr: Attribute>(\n                    self,\n                    attr: NewAttr,\n                ) -> Self::Output<NewAttr>\n                where\n                    Self::Output<NewAttr>: RenderHtml,\n                {\n                    match self {\n                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.add_any_attr(attr)),)*\n                    }\n                }\n            }\n\n            impl<$($ty,)*> RenderHtml for [<EitherOf $num>]<$($ty,)*>\n            where\n                $($ty: RenderHtml,)*\n\n            {\n                type AsyncOutput = [<EitherOf $num>]<$($ty::AsyncOutput,)*>;\n                type Owned = [<EitherOf $num>]<$($ty::Owned,)*>;\n\n                const MIN_LENGTH: usize = max_usize(&[$($ty ::MIN_LENGTH,)*]);\n\n\n                fn dry_resolve(&mut self) {\n                    match self {\n                        $([<EitherOf $num>]::$ty(this) => {\n                            this.dry_resolve();\n                        })*\n                    }\n                }\n\n                async fn resolve(self) -> Self::AsyncOutput {\n                    match self {\n                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.resolve().await),)*\n                    }\n                }\n\n                #[inline(always)]\n                fn html_len(&self) -> usize {\n                    match self {\n                        $([<EitherOf $num>]::$ty(i) => i.html_len(),)*\n                    }\n                }\n\n                fn to_html_with_buf(\n                    self,\n                    buf: &mut String,\n                    position: &mut Position,\n                    escape: bool,\n                    mark_branches: bool,\n                    extra_attrs: Vec<AnyAttribute>\n                ) {\n                    match self {\n                        $([<EitherOf $num>]::$ty(this) => {\n                            if mark_branches && escape {\n                                buf.open_branch(stringify!($ty));\n                            }\n                            this.to_html_with_buf(buf, position, escape, mark_branches, extra_attrs);\n                            if mark_branches && escape {\n                                buf.close_branch(stringify!($ty));\n                                if *position == Position::NextChildAfterText {\n                                    *position = Position::NextChild;\n                                }\n                            }\n                        })*\n                    }\n                }\n\n                fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n                    self,\n                    buf: &mut StreamBuilder,\n                    position: &mut Position,\n                    escape: bool,\n                    mark_branches: bool,\n                    extra_attrs: Vec<AnyAttribute>\n                ) where\n                    Self: Sized,\n                {\n                    match self {\n                        $([<EitherOf $num>]::$ty(this) => {\n                            if mark_branches && escape {\n                                buf.open_branch(stringify!($ty));\n                            }\n                            this.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape, mark_branches, extra_attrs);\n                            if mark_branches && escape {\n                                buf.close_branch(stringify!($ty));\n                                if *position == Position::NextChildAfterText {\n                                    *position = Position::NextChild;\n                                }\n                            }\n                        })*\n                    }\n                }\n\n                fn hydrate<const FROM_SERVER: bool>(\n                    self,\n                    cursor: &Cursor,\n                    position: &PositionState,\n                ) -> Self::State {\n                    let state = match self {\n                        $([<EitherOf $num>]::$ty(this) => {\n                            [<EitherOf $num>]::$ty(this.hydrate::<FROM_SERVER>(cursor, position))\n                        })*\n                    };\n\n                    Self::State { state }\n                }\n\n                async fn hydrate_async(\n                    self,\n                    cursor: &Cursor,\n                    position: &PositionState,\n                ) -> Self::State {\n                    let state = match self {\n                        $([<EitherOf $num>]::$ty(this) => {\n                            [<EitherOf $num>]::$ty(this.hydrate_async(cursor, position).await)\n                        })*\n                    };\n\n                    Self::State { state }\n                }\n\n                fn into_owned(self) -> Self::Owned {\n                    match self {\n                        $([<EitherOf $num>]::$ty(this) => {\n                            [<EitherOf $num>]::$ty(this.into_owned())\n                        })*\n                    }\n                }\n            }\n        }\n    }\n}\n\ntuples!(3 => A, B, C);\ntuples!(4 => A, B, C, D);\ntuples!(5 => A, B, C, D, E);\ntuples!(6 => A, B, C, D, E, F);\ntuples!(7 => A, B, C, D, E, F, G);\ntuples!(8 => A, B, C, D, E, F, G, H);\ntuples!(9 => A, B, C, D, E, F, G, H, I);\ntuples!(10 => A, B, C, D, E, F, G, H, I, J);\ntuples!(11 => A, B, C, D, E, F, G, H, I, J, K);\ntuples!(12 => A, B, C, D, E, F, G, H, I, J, K, L);\ntuples!(13 => A, B, C, D, E, F, G, H, I, J, K, L, M);\ntuples!(14 => A, B, C, D, E, F, G, H, I, J, K, L, M, N);\ntuples!(15 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);\ntuples!(16 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);\n"
  },
  {
    "path": "tachys/src/view/error_boundary.rs",
    "content": "use super::{add_attr::AddAnyAttr, Position, PositionState, RenderHtml};\nuse crate::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    ssr::StreamBuilder,\n    view::{iterators::OptionState, Mountable, Render},\n};\nuse either_of::Either;\nuse std::sync::Arc;\nuse throw_error::{Error as AnyError, ErrorHook};\n\nimpl<T, E> Render for Result<T, E>\nwhere\n    T: Render,\n    E: Into<AnyError> + 'static,\n{\n    type State = ResultState<T>;\n\n    fn build(self) -> Self::State {\n        let hook = throw_error::get_error_hook();\n        let (state, error) = match self {\n            Ok(view) => (Either::Left(view.build()), None),\n            Err(e) => (\n                Either::Right(Render::build(())),\n                Some(throw_error::throw(e.into())),\n            ),\n        };\n        ResultState { state, error, hook }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let _guard = state.hook.clone().map(throw_error::set_error_hook);\n        match (&mut state.state, self) {\n            // both errors: throw the new error and replace\n            (Either::Right(_), Err(new)) => {\n                if let Some(old_error) = state.error.take() {\n                    throw_error::clear(&old_error);\n                }\n                state.error = Some(throw_error::throw(new.into()));\n            }\n            // both Ok: need to rebuild child\n            (Either::Left(old), Ok(new)) => {\n                T::rebuild(new, old);\n            }\n            // Ok => Err: unmount, replace with marker, and throw\n            (Either::Left(old), Err(err)) => {\n                let mut new_state = Render::build(());\n                old.insert_before_this(&mut new_state);\n                old.unmount();\n                state.state = Either::Right(new_state);\n                state.error = Some(throw_error::throw(err));\n            }\n            // Err => Ok: clear error and build\n            (Either::Right(old), Ok(new)) => {\n                if let Some(err) = state.error.take() {\n                    throw_error::clear(&err);\n                }\n                let mut new_state = new.build();\n                old.insert_before_this(&mut new_state);\n                old.unmount();\n                state.state = Either::Left(new_state);\n            }\n        }\n    }\n}\n\n/// View state for a `Result<_, _>` view.\npub struct ResultState<T>\nwhere\n    T: Render,\n{\n    /// The view state.\n    state: OptionState<T>,\n    error: Option<throw_error::ErrorId>,\n    hook: Option<Arc<dyn ErrorHook>>,\n}\n\nimpl<T> Drop for ResultState<T>\nwhere\n    T: Render,\n{\n    fn drop(&mut self) {\n        // when the state is cleared, unregister this error; this item is being dropped and its\n        // error should no longer be shown\n        if let Some(e) = self.error.take() {\n            throw_error::clear(&e);\n        }\n    }\n}\n\nimpl<T> Mountable for ResultState<T>\nwhere\n    T: Render,\n{\n    fn unmount(&mut self) {\n        self.state.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        self.state.mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.state.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.state.elements()\n    }\n}\n\nimpl<T, E> AddAnyAttr for Result<T, E>\nwhere\n    T: AddAnyAttr,\n\n    E: Into<AnyError> + Send + 'static,\n{\n    type Output<SomeNewAttr: Attribute> =\n        Result<<T as AddAnyAttr>::Output<SomeNewAttr>, E>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        self.map(|inner| inner.add_any_attr(attr))\n    }\n}\n\nimpl<T, E> RenderHtml for Result<T, E>\nwhere\n    T: RenderHtml,\n    E: Into<AnyError> + Send + 'static,\n{\n    type AsyncOutput = Result<T::AsyncOutput, E>;\n    type Owned = Result<T::Owned, E>;\n\n    const MIN_LENGTH: usize = T::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {\n        if let Ok(inner) = self.as_mut() {\n            inner.dry_resolve()\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        match self {\n            Ok(view) => Ok(view.resolve().await),\n            Err(e) => Err(e),\n        }\n    }\n\n    fn html_len(&self) -> usize {\n        match self {\n            Ok(i) => i.html_len() + 3,\n            Err(_) => 0,\n        }\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut super::Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        match self {\n            Ok(inner) => {\n                inner.to_html_with_buf(\n                    buf,\n                    position,\n                    escape,\n                    mark_branches,\n                    extra_attrs,\n                );\n            }\n            Err(e) => {\n                buf.push_str(\"<!>\");\n                throw_error::throw(e);\n            }\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        match self {\n            Ok(inner) => inner.to_html_async_with_buf::<OUT_OF_ORDER>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            ),\n            Err(e) => {\n                buf.push_sync(\"<!>\");\n                throw_error::throw(e);\n            }\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let hook = throw_error::get_error_hook();\n        let (state, error) = match self {\n            Ok(view) => (\n                Either::Left(view.hydrate::<FROM_SERVER>(cursor, position)),\n                None,\n            ),\n            Err(e) => {\n                let state =\n                    RenderHtml::hydrate::<FROM_SERVER>((), cursor, position);\n                (Either::Right(state), Some(throw_error::throw(e.into())))\n            }\n        };\n        ResultState { state, error, hook }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let hook = throw_error::get_error_hook();\n        let (state, error) = match self {\n            Ok(view) => (\n                Either::Left(view.hydrate_async(cursor, position).await),\n                None,\n            ),\n            Err(e) => {\n                let state =\n                    RenderHtml::hydrate_async((), cursor, position).await;\n                (Either::Right(state), Some(throw_error::throw(e.into())))\n            }\n        };\n        ResultState { state, error, hook }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        match self {\n            Ok(view) => Ok(view.into_owned()),\n            Err(e) => Err(e),\n        }\n    }\n}\n"
  },
  {
    "path": "tachys/src/view/fragment.rs",
    "content": "use super::{\n    any_view::{AnyView, IntoAny},\n    iterators::StaticVec,\n};\nuse crate::html::element::HtmlElement;\n\n/// A typed-erased collection of different views.\npub struct Fragment {\n    /// The nodes contained in the fragment.\n    pub nodes: StaticVec<AnyView>,\n}\n\n/// Converts some view into a type-erased collection of views.\npub trait IntoFragment {\n    /// Converts some view into a type-erased collection of views.\n    fn into_fragment(self) -> Fragment;\n}\n\nimpl FromIterator<AnyView> for Fragment {\n    fn from_iter<T: IntoIterator<Item = AnyView>>(iter: T) -> Self {\n        Fragment::new(iter.into_iter().collect())\n    }\n}\n\nimpl From<AnyView> for Fragment {\n    fn from(view: AnyView) -> Self {\n        Fragment::new(vec![view])\n    }\n}\n\nimpl From<Fragment> for AnyView {\n    fn from(value: Fragment) -> Self {\n        value.nodes.into_any()\n    }\n}\n\nimpl Fragment {\n    /// Creates a new [`Fragment`].\n    #[inline(always)]\n    pub fn new(nodes: Vec<AnyView>) -> Self {\n        Self {\n            nodes: nodes.into(),\n        }\n    }\n}\n\nimpl<E, At, Ch> IntoFragment for HtmlElement<E, At, Ch>\nwhere\n    HtmlElement<E, At, Ch>: IntoAny,\n{\n    fn into_fragment(self) -> Fragment {\n        Fragment::new(vec![self.into_any()])\n    }\n}\n\nimpl IntoFragment for AnyView {\n    fn into_fragment(self) -> Fragment {\n        Fragment::new(vec![self])\n    }\n}\n\nimpl<T> IntoFragment for Vec<T>\nwhere\n    T: IntoAny,\n{\n    fn into_fragment(self) -> Fragment {\n        Fragment::new(self.into_iter().map(IntoAny::into_any).collect())\n    }\n}\n\nimpl<T> IntoFragment for StaticVec<T>\nwhere\n    T: IntoAny,\n{\n    fn into_fragment(self) -> Fragment {\n        Fragment::new(self.into_iter().map(IntoAny::into_any).collect())\n    }\n}\n\nimpl<const N: usize, T> IntoFragment for [T; N]\nwhere\n    T: IntoAny,\n{\n    fn into_fragment(self) -> Fragment {\n        Fragment::new(self.into_iter().map(IntoAny::into_any).collect())\n    }\n}\n\nmacro_rules! tuples {\n\t($($ty:ident),*) => {\n\t\timpl<$($ty),*> IntoFragment for ($($ty,)*)\n\t\twhere\n\t\t\t$($ty: IntoAny),*,\n\n\t\t{\n            fn into_fragment(self) -> Fragment {\n                #[allow(non_snake_case)]\n\t\t\t    let ($($ty,)*) = self;\n                Fragment::new(vec![$($ty.into_any(),)*])\n            }\n        }\n    }\n}\n\ntuples!(A);\ntuples!(A, B);\ntuples!(A, B, C);\ntuples!(A, B, C, D);\ntuples!(A, B, C, D, E);\ntuples!(A, B, C, D, E, F);\ntuples!(A, B, C, D, E, F, G);\ntuples!(A, B, C, D, E, F, G, H);\ntuples!(A, B, C, D, E, F, G, H, I);\ntuples!(A, B, C, D, E, F, G, H, I, J);\ntuples!(A, B, C, D, E, F, G, H, I, J, K);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);\ntuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);\ntuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y\n);\ntuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,\n    Z\n);\n"
  },
  {
    "path": "tachys/src/view/iterators.rs",
    "content": "use super::{\n    add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n    RenderHtml,\n};\nuse crate::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    renderer::Rndr,\n    ssr::StreamBuilder,\n};\nuse either_of::Either;\nuse itertools::Itertools;\n\n/// Retained view state for an `Option`.\npub type OptionState<T> = Either<<T as Render>::State, <() as Render>::State>;\n\nimpl<T> Render for Option<T>\nwhere\n    T: Render,\n{\n    type State = OptionState<T>;\n\n    fn build(self) -> Self::State {\n        match self {\n            Some(value) => Either::Left(value),\n            None => Either::Right(()),\n        }\n        .build()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        match self {\n            Some(value) => Either::Left(value),\n            None => Either::Right(()),\n        }\n        .rebuild(state)\n    }\n}\n\nimpl<T> AddAnyAttr for Option<T>\nwhere\n    T: AddAnyAttr,\n{\n    type Output<SomeNewAttr: Attribute> =\n        Option<<T as AddAnyAttr>::Output<SomeNewAttr>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        self.map(|n| n.add_any_attr(attr))\n    }\n}\n\nimpl<T> RenderHtml for Option<T>\nwhere\n    T: RenderHtml,\n{\n    type AsyncOutput = Option<T::AsyncOutput>;\n    type Owned = Option<T::Owned>;\n\n    const MIN_LENGTH: usize = T::MIN_LENGTH;\n\n    fn dry_resolve(&mut self) {\n        if let Some(inner) = self.as_mut() {\n            inner.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        match self {\n            None => None,\n            Some(value) => Some(value.resolve().await),\n        }\n    }\n\n    fn html_len(&self) -> usize {\n        match self {\n            Some(i) => i.html_len() + 3,\n            None => 3,\n        }\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        match self {\n            Some(value) => Either::Left(value),\n            None => Either::Right(()),\n        }\n        .to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        match self {\n            Some(value) => Either::Left(value),\n            None => Either::Right(()),\n        }\n        .to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    #[track_caller]\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        match self {\n            Some(value) => Either::Left(value),\n            None => Either::Right(()),\n        }\n        .hydrate::<FROM_SERVER>(cursor, position)\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        match self {\n            Some(value) => Either::Left(value),\n            None => Either::Right(()),\n        }\n        .hydrate_async(cursor, position)\n        .await\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self.map(RenderHtml::into_owned)\n    }\n}\n\nimpl<T> Render for Vec<T>\nwhere\n    T: Render,\n{\n    type State = VecState<T::State>;\n\n    fn build(self) -> Self::State {\n        let marker = Rndr::create_placeholder();\n        VecState {\n            states: self.into_iter().map(T::build).collect(),\n            marker,\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let VecState { states, marker } = state;\n        let old = states;\n        // this is an unkeyed diff\n        if old.is_empty() {\n            let mut new = self.build().states;\n            for item in new.iter_mut() {\n                Rndr::try_mount_before(item, marker.as_ref());\n            }\n            *old = new;\n        } else if self.is_empty() {\n            // TODO fast path for clearing\n            for item in old.iter_mut() {\n                item.unmount();\n            }\n            old.clear();\n        } else {\n            let mut adds = vec![];\n            let mut removes_at_end = 0;\n            for item in self.into_iter().zip_longest(old.iter_mut()) {\n                match item {\n                    itertools::EitherOrBoth::Both(new, old) => {\n                        T::rebuild(new, old)\n                    }\n                    itertools::EitherOrBoth::Left(new) => {\n                        let mut new_state = new.build();\n                        Rndr::try_mount_before(&mut new_state, marker.as_ref());\n                        adds.push(new_state);\n                    }\n                    itertools::EitherOrBoth::Right(old) => {\n                        removes_at_end += 1;\n                        old.unmount()\n                    }\n                }\n            }\n            old.truncate(old.len() - removes_at_end);\n            old.append(&mut adds);\n        }\n    }\n}\n\n/// Retained view state for a `Vec<_>`.\npub struct VecState<T>\nwhere\n    T: Mountable,\n{\n    states: Vec<T>,\n    // Vecs keep a placeholder because they have the potential to add additional items,\n    // after their own items but before the next neighbor. It is much easier to add an\n    // item before a known placeholder than to add it after the last known item, so we\n    // just leave a placeholder here unlike zero-or-one iterators (Option, Result, etc.)\n    marker: crate::renderer::types::Placeholder,\n}\n\nimpl<T> Mountable for VecState<T>\nwhere\n    T: Mountable,\n{\n    fn unmount(&mut self) {\n        for state in self.states.iter_mut() {\n            state.unmount();\n        }\n        self.marker.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        for state in self.states.iter_mut() {\n            state.mount(parent, marker);\n        }\n        self.marker.mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        for state in &self.states {\n            if state.insert_before_this(child) {\n                return true;\n            }\n        }\n        self.marker.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.states\n            .iter()\n            .flat_map(|item| item.elements())\n            .collect()\n    }\n}\n\nimpl<T> AddAnyAttr for Vec<T>\nwhere\n    T: AddAnyAttr,\n{\n    type Output<SomeNewAttr: Attribute> =\n        Vec<<T as AddAnyAttr>::Output<SomeNewAttr::Cloneable>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let attr = attr.into_cloneable();\n        self.into_iter()\n            .map(|n| n.add_any_attr(attr.clone()))\n            .collect()\n    }\n}\n\nimpl<T> RenderHtml for Vec<T>\nwhere\n    T: RenderHtml,\n{\n    type AsyncOutput = Vec<T::AsyncOutput>;\n    type Owned = Vec<T::Owned>;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {\n        for inner in self.iter_mut() {\n            inner.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        futures::future::join_all(self.into_iter().map(T::resolve))\n            .await\n            .into_iter()\n            .collect()\n    }\n\n    fn html_len(&self) -> usize {\n        self.iter().map(|n| n.html_len()).sum::<usize>() + 3\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        let mut children = self.into_iter();\n        if let Some(first) = children.next() {\n            first.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n        }\n        for child in children {\n            child.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                // each child will have the extra attributes applied\n                extra_attrs.clone(),\n            );\n        }\n        if escape {\n            buf.push_str(\"<!>\");\n            *position = Position::NextChild;\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        let mut children = self.into_iter();\n        if let Some(first) = children.next() {\n            first.to_html_async_with_buf::<OUT_OF_ORDER>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n        }\n        for child in children {\n            child.to_html_async_with_buf::<OUT_OF_ORDER>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n        }\n        if escape {\n            buf.push_sync(\"<!>\");\n            *position = Position::NextChild;\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let states = self\n            .into_iter()\n            .map(|child| child.hydrate::<FROM_SERVER>(cursor, position))\n            .collect();\n\n        let marker = cursor.next_placeholder(position);\n        position.set(Position::NextChild);\n\n        VecState { states, marker }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let mut states = Vec::with_capacity(self.len());\n        for child in self {\n            states.push(child.hydrate_async(cursor, position).await);\n        }\n\n        let marker = cursor.next_placeholder(position);\n        position.set(Position::NextChild);\n\n        VecState { states, marker }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self.into_iter()\n            .map(RenderHtml::into_owned)\n            .collect::<Vec<_>>()\n    }\n}\n\n/// A container used for ErasedMode. It's slightly better than a raw Vec<> because the rendering traits don't have to worry about the length of the Vec changing, therefore no marker traits etc.\npub struct StaticVec<T>(pub(crate) Vec<T>);\n\nimpl<T: Clone> Clone for StaticVec<T> {\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\nimpl<T> IntoIterator for StaticVec<T> {\n    type Item = T;\n    type IntoIter = std::vec::IntoIter<T>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.into_iter()\n    }\n}\n\nimpl<T> StaticVec<T> {\n    /// Iterates over the items.\n    pub fn iter(&self) -> std::slice::Iter<'_, T> {\n        self.0.iter()\n    }\n}\n\nimpl<T> From<Vec<T>> for StaticVec<T> {\n    fn from(vec: Vec<T>) -> Self {\n        Self(vec)\n    }\n}\n\nimpl<T> From<StaticVec<T>> for Vec<T> {\n    fn from(static_vec: StaticVec<T>) -> Self {\n        static_vec.0\n    }\n}\n\n/// Retained view state for a `StaticVec<Vec<_>>`.\npub struct StaticVecState<T>\nwhere\n    T: Mountable,\n{\n    states: Vec<T>,\n    marker: crate::renderer::types::Placeholder,\n}\n\nimpl<T> Mountable for StaticVecState<T>\nwhere\n    T: Mountable,\n{\n    fn unmount(&mut self) {\n        for state in self.states.iter_mut() {\n            state.unmount();\n        }\n        self.marker.unmount();\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        for state in self.states.iter_mut() {\n            state.mount(parent, marker);\n        }\n        self.marker.mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        for state in &self.states {\n            if state.insert_before_this(child) {\n                return true;\n            }\n        }\n        self.marker.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.states\n            .iter()\n            .flat_map(|item| item.elements())\n            .collect()\n    }\n}\n\nimpl<T> Render for StaticVec<T>\nwhere\n    T: Render,\n{\n    type State = StaticVecState<T::State>;\n\n    fn build(self) -> Self::State {\n        let marker = Rndr::create_placeholder();\n        Self::State {\n            states: self.0.into_iter().map(T::build).collect(),\n            marker,\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let StaticVecState { states, marker } = state;\n        let old = states;\n\n        // reuses the Vec impl\n        if old.is_empty() {\n            let mut new = self.build().states;\n            for item in new.iter_mut() {\n                Rndr::mount_before(item, marker.as_ref());\n            }\n            *old = new;\n        } else if self.0.is_empty() {\n            // TODO fast path for clearing\n            for item in old.iter_mut() {\n                item.unmount();\n            }\n            old.clear();\n        } else {\n            let mut adds = vec![];\n            let mut removes_at_end = 0;\n            for item in self.0.into_iter().zip_longest(old.iter_mut()) {\n                match item {\n                    itertools::EitherOrBoth::Both(new, old) => {\n                        T::rebuild(new, old)\n                    }\n                    itertools::EitherOrBoth::Left(new) => {\n                        let mut new_state = new.build();\n                        Rndr::mount_before(&mut new_state, marker.as_ref());\n                        adds.push(new_state);\n                    }\n                    itertools::EitherOrBoth::Right(old) => {\n                        removes_at_end += 1;\n                        old.unmount()\n                    }\n                }\n            }\n            old.truncate(old.len() - removes_at_end);\n            old.append(&mut adds);\n        }\n    }\n}\n\nimpl<T> AddAnyAttr for StaticVec<T>\nwhere\n    T: AddAnyAttr,\n{\n    type Output<SomeNewAttr: Attribute> =\n        StaticVec<<T as AddAnyAttr>::Output<SomeNewAttr::Cloneable>>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let attr = attr.into_cloneable();\n        self.0\n            .into_iter()\n            .map(|n| n.add_any_attr(attr.clone()))\n            .collect::<Vec<_>>()\n            .into()\n    }\n}\n\nimpl<T> RenderHtml for StaticVec<T>\nwhere\n    T: RenderHtml,\n{\n    type AsyncOutput = StaticVec<T::AsyncOutput>;\n    type Owned = StaticVec<T::Owned>;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {\n        for inner in self.0.iter_mut() {\n            inner.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        futures::future::join_all(self.0.into_iter().map(T::resolve))\n            .await\n            .into_iter()\n            .collect::<Vec<_>>()\n            .into()\n    }\n\n    fn html_len(&self) -> usize {\n        self.0.iter().map(RenderHtml::html_len).sum::<usize>() + 3\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        for child in self.0.into_iter() {\n            child.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n        }\n        if escape {\n            buf.push_str(\"<!>\");\n            *position = Position::NextChild;\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        for child in self.0.into_iter() {\n            child.to_html_async_with_buf::<OUT_OF_ORDER>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n        }\n        if escape {\n            buf.push_sync(\"<!>\");\n            *position = Position::NextChild;\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let states = self\n            .0\n            .into_iter()\n            .map(|child| child.hydrate::<FROM_SERVER>(cursor, position))\n            .collect();\n\n        let marker = cursor.next_placeholder(position);\n        position.set(Position::NextChild);\n\n        Self::State { states, marker }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let mut states = Vec::with_capacity(self.0.len());\n        for child in self.0 {\n            states.push(child.hydrate_async(cursor, position).await);\n        }\n\n        let marker = cursor.next_placeholder(position);\n        position.set(Position::NextChild);\n\n        Self::State { states, marker }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self.0\n            .into_iter()\n            .map(RenderHtml::into_owned)\n            .collect::<Vec<_>>()\n            .into()\n    }\n}\n\nimpl<T, const N: usize> Render for [T; N]\nwhere\n    T: Render,\n{\n    type State = ArrayState<T::State, N>;\n\n    fn build(self) -> Self::State {\n        Self::State {\n            states: self.map(T::build),\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let Self::State { states } = state;\n        let old = states;\n        // this is an unkeyed diff\n        self.into_iter()\n            .zip(old.iter_mut())\n            .for_each(|(new, old)| T::rebuild(new, old));\n    }\n}\n\n/// Retained view state for a `Vec<_>`.\npub struct ArrayState<T, const N: usize>\nwhere\n    T: Mountable,\n{\n    states: [T; N],\n}\n\nimpl<T, const N: usize> Mountable for ArrayState<T, N>\nwhere\n    T: Mountable,\n{\n    fn unmount(&mut self) {\n        self.states.iter_mut().for_each(Mountable::unmount);\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        for state in self.states.iter_mut() {\n            state.mount(parent, marker);\n        }\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        for state in &self.states {\n            if state.insert_before_this(child) {\n                return true;\n            }\n        }\n        false\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.states\n            .iter()\n            .flat_map(|item| item.elements())\n            .collect()\n    }\n}\nimpl<T, const N: usize> AddAnyAttr for [T; N]\nwhere\n    T: AddAnyAttr,\n{\n    type Output<SomeNewAttr: Attribute> =\n        [<T as AddAnyAttr>::Output<SomeNewAttr::Cloneable>; N];\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let attr = attr.into_cloneable();\n        self.map(|n| n.add_any_attr(attr.clone()))\n    }\n}\n\nimpl<T, const N: usize> RenderHtml for [T; N]\nwhere\n    T: RenderHtml,\n{\n    type AsyncOutput = [T::AsyncOutput; N];\n    type Owned = [T::Owned; N];\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {\n        for inner in self.iter_mut() {\n            inner.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        futures::future::join_all(self.into_iter().map(T::resolve))\n            .await\n            .into_iter()\n            .collect::<Vec<_>>()\n            .try_into()\n            .unwrap_or_else(|_| unreachable!())\n    }\n\n    fn html_len(&self) -> usize {\n        self.iter().map(RenderHtml::html_len).sum::<usize>()\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        for child in self.into_iter() {\n            child.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n        }\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        for child in self.into_iter() {\n            child.to_html_async_with_buf::<OUT_OF_ORDER>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let states =\n            self.map(|child| child.hydrate::<FROM_SERVER>(cursor, position));\n        ArrayState { states }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let mut states = Vec::with_capacity(self.len());\n        for child in self {\n            states.push(child.hydrate_async(cursor, position).await);\n        }\n        let Ok(states) = <[<T as Render>::State; N]>::try_from(states) else {\n            unreachable!()\n        };\n        ArrayState { states }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self.into_iter()\n            .map(RenderHtml::into_owned)\n            .collect::<Vec<_>>()\n            .try_into()\n            .unwrap_or_else(|_| unreachable!())\n    }\n}\n"
  },
  {
    "path": "tachys/src/view/keyed.rs",
    "content": "use super::{\n    add_attr::AddAnyAttr, MarkBranch, Mountable, Position, PositionState,\n    Render, RenderHtml,\n};\nuse crate::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    renderer::{CastFrom, Rndr},\n    ssr::StreamBuilder,\n};\nuse drain_filter_polyfill::VecExt as VecDrainFilterExt;\nuse indexmap::IndexSet;\nuse rustc_hash::FxHasher;\nuse std::hash::{BuildHasherDefault, Hash};\n\ntype FxIndexSet<T> = IndexSet<T, BuildHasherDefault<FxHasher>>;\n\n/// Creates a keyed list of views.\npub fn keyed<T, I, K, KF, VF, VFS, V>(\n    items: I,\n    key_fn: KF,\n    view_fn: VF,\n) -> Keyed<T, I, K, KF, VF, VFS, V>\nwhere\n    I: IntoIterator<Item = T>,\n    K: Eq + Hash + SerializableKey + 'static,\n    KF: Fn(&T) -> K,\n    V: Render,\n    VF: Fn(usize, T) -> (VFS, V),\n    VFS: Fn(usize),\n{\n    Keyed {\n        #[cfg(not(feature = \"ssr\"))]\n        items: Some(items),\n        #[cfg(feature = \"ssr\")]\n        items: None,\n        #[cfg(feature = \"ssr\")]\n        ssr_items: items\n            .into_iter()\n            .enumerate()\n            .map(|(i, t)| {\n                let key = if cfg!(feature = \"islands\") {\n                    let key = (key_fn)(&t);\n                    key.ser_key()\n                } else {\n                    String::new()\n                };\n                let (_, view) = (view_fn)(i, t);\n                (key, view)\n            })\n            .collect::<Vec<_>>(),\n        key_fn,\n        view_fn,\n    }\n}\n\n/// A keyed list of views.\npub struct Keyed<T, I, K, KF, VF, VFS, V>\nwhere\n    I: IntoIterator<Item = T>,\n    K: Eq + Hash + 'static,\n    KF: Fn(&T) -> K,\n    VF: Fn(usize, T) -> (VFS, V),\n    VFS: Fn(usize),\n{\n    items: Option<I>,\n    #[cfg(feature = \"ssr\")]\n    ssr_items: Vec<(String, V)>,\n    key_fn: KF,\n    view_fn: VF,\n}\n\n/// By default, keys used in for keyed iteration do not need to be serializable.\n///\n/// However, for some scenarios (like the “islands routing” mode that mixes server-side\n/// rendering with client-side navigation) it is useful to have serializable keys.\n///\n/// When the `islands` feature is not enabled, this trait is implemented by all types.\n///\n/// When the `islands` features is enabled, this is automatically implemented for all types\n/// that implement [`Serialize`](serde::Serialize), and can be manually implemented otherwise.\npub trait SerializableKey {\n    /// Serializes the key to a unique string.\n    ///\n    /// The string can have any value, as long as it is idempotent (i.e., serializing the same key\n    /// multiple times will give the same value).\n    fn ser_key(&self) -> String;\n}\n\n#[cfg(not(feature = \"islands\"))]\nimpl<T> SerializableKey for T {\n    fn ser_key(&self) -> String {\n        panic!(\n            \"SerializableKey called without the `islands` feature enabled. \\\n             Something has gone wrong.\"\n        );\n    }\n}\n#[cfg(feature = \"islands\")]\nimpl<T: serde::Serialize> SerializableKey for T {\n    fn ser_key(&self) -> String {\n        serde_json::to_string(self).expect(\"failed to serialize key\")\n    }\n}\n\n/// Retained view state for a keyed list.\npub struct KeyedState<K, VFS, V>\nwhere\n    K: Eq + Hash + 'static,\n    VFS: Fn(usize),\n    V: Render,\n{\n    parent: Option<crate::renderer::types::Element>,\n    marker: crate::renderer::types::Placeholder,\n    hashed_items: IndexSet<K, BuildHasherDefault<FxHasher>>,\n    rendered_items: Vec<Option<(VFS, V::State)>>,\n}\n\nimpl<T, I, K, KF, VF, VFS, V> Render for Keyed<T, I, K, KF, VF, VFS, V>\nwhere\n    I: IntoIterator<Item = T>,\n    K: Eq + Hash + SerializableKey + 'static,\n    KF: Fn(&T) -> K,\n    V: Render,\n    VF: Fn(usize, T) -> (VFS, V),\n    VFS: Fn(usize),\n{\n    type State = KeyedState<K, VFS, V>;\n\n    fn build(self) -> Self::State {\n        let items = self.items.into_iter().flatten();\n        let (capacity, _) = items.size_hint();\n        let mut hashed_items =\n            FxIndexSet::with_capacity_and_hasher(capacity, Default::default());\n        let mut rendered_items = Vec::with_capacity(capacity);\n        for (index, item) in items.enumerate() {\n            hashed_items.insert((self.key_fn)(&item));\n            let (set_index, view) = (self.view_fn)(index, item);\n            rendered_items.push(Some((set_index, view.build())));\n        }\n        KeyedState {\n            parent: None,\n            marker: Rndr::create_placeholder(),\n            hashed_items,\n            rendered_items,\n        }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let KeyedState {\n            parent,\n            marker,\n            hashed_items,\n            ref mut rendered_items,\n        } = state;\n        let new_items = self.items.into_iter().flatten();\n        let (capacity, _) = new_items.size_hint();\n        let mut new_hashed_items =\n            FxIndexSet::with_capacity_and_hasher(capacity, Default::default());\n\n        let mut items = Vec::new();\n        for item in new_items {\n            new_hashed_items.insert((self.key_fn)(&item));\n            items.push(Some(item));\n        }\n\n        let cmds = diff(hashed_items, &new_hashed_items);\n\n        apply_diff(\n            parent.as_ref(),\n            marker,\n            cmds,\n            rendered_items,\n            &self.view_fn,\n            items,\n        );\n\n        *hashed_items = new_hashed_items;\n    }\n}\n\nimpl<T, I, K, KF, VF, VFS, V> AddAnyAttr for Keyed<T, I, K, KF, VF, VFS, V>\nwhere\n    I: IntoIterator<Item = T> + Send + 'static,\n    K: Eq + Hash + SerializableKey + 'static,\n    KF: Fn(&T) -> K + Send + 'static,\n    V: RenderHtml,\n    V: 'static,\n    VF: Fn(usize, T) -> (VFS, V) + Send + 'static,\n    VFS: Fn(usize) + 'static,\n    T: 'static,\n{\n    type Output<SomeNewAttr: Attribute> = Keyed<\n        T,\n        I,\n        K,\n        KF,\n        Box<\n            dyn Fn(\n                    usize,\n                    T,\n                ) -> (\n                    VFS,\n                    <V as AddAnyAttr>::Output<SomeNewAttr::CloneableOwned>,\n                ) + Send,\n        >,\n        VFS,\n        V::Output<SomeNewAttr::CloneableOwned>,\n    >;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        let Keyed {\n            items,\n            #[cfg(feature = \"ssr\")]\n            ssr_items,\n            key_fn,\n            view_fn,\n        } = self;\n        let attr = attr.into_cloneable_owned();\n        Keyed {\n            items,\n            key_fn,\n            #[cfg(feature = \"ssr\")]\n            ssr_items: ssr_items\n                .into_iter()\n                .map(|(k, v)| (k, v.add_any_attr(attr.clone())))\n                .collect(),\n            view_fn: Box::new(move |index, item| {\n                let (index, view) = view_fn(index, item);\n                (index, view.add_any_attr(attr.clone()))\n            }),\n        }\n    }\n}\n\nimpl<T, I, K, KF, VF, VFS, V> RenderHtml for Keyed<T, I, K, KF, VF, VFS, V>\nwhere\n    I: IntoIterator<Item = T> + Send + 'static,\n    K: Eq + Hash + SerializableKey + 'static,\n    KF: Fn(&T) -> K + Send + 'static,\n    V: RenderHtml + 'static,\n    VF: Fn(usize, T) -> (VFS, V) + Send + 'static,\n    VFS: Fn(usize) + 'static,\n    T: 'static,\n{\n    type AsyncOutput = Vec<V::AsyncOutput>; // TODO\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {\n        #[cfg(feature = \"ssr\")]\n        for view in &mut self.ssr_items {\n            view.dry_resolve();\n        }\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        #[cfg(feature = \"ssr\")]\n        {\n            futures::future::join_all(\n                self.ssr_items.into_iter().map(|(_, view)| view.resolve()),\n            )\n            .await\n            .into_iter()\n            .collect::<Vec<_>>()\n        }\n        #[cfg(not(feature = \"ssr\"))]\n        {\n            futures::future::join_all(\n                self.items.into_iter().flatten().enumerate().map(\n                    |(index, item)| {\n                        let (_, view) = (self.view_fn)(index, item);\n                        view.resolve()\n                    },\n                ),\n            )\n            .await\n            .into_iter()\n            .collect::<Vec<_>>()\n        }\n    }\n\n    #[allow(unused)]\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        if mark_branches && escape {\n            buf.open_branch(\"for\");\n        }\n\n        #[cfg(feature = \"ssr\")]\n        for item in self.ssr_items {\n            if mark_branches && escape {\n                buf.open_branch(\"item\");\n            }\n            item.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n            if mark_branches && escape {\n                buf.close_branch(\"item\");\n            }\n            *position = Position::NextChild;\n        }\n        if mark_branches && escape {\n            buf.close_branch(\"for\");\n        }\n        buf.push_str(\"<!>\");\n    }\n\n    #[allow(unused)]\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        if mark_branches && escape {\n            buf.open_branch(\"for\");\n        }\n\n        #[cfg(feature = \"ssr\")]\n        for (key, item) in self.ssr_items {\n            let branch_name = mark_branches.then(|| format!(\"item-{key}\"));\n            if mark_branches && escape {\n                buf.open_branch(branch_name.as_ref().unwrap());\n            }\n            item.to_html_async_with_buf::<OUT_OF_ORDER>(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs.clone(),\n            );\n            if mark_branches && escape {\n                buf.close_branch(branch_name.as_ref().unwrap());\n            }\n            *position = Position::NextChild;\n        }\n\n        if mark_branches && escape {\n            buf.close_branch(\"for\");\n        }\n        buf.push_sync(\"<!>\");\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        // get parent and position\n        let current = cursor.current();\n        let parent = if position.get() == Position::FirstChild {\n            current\n        } else {\n            Rndr::get_parent(&current)\n                .expect(\"first child of keyed list has no parent\")\n        };\n        let parent = crate::renderer::types::Element::cast_from(parent)\n            .expect(\"parent of keyed list should be an element\");\n\n        // build list\n        let items = self.items.into_iter().flatten();\n        let (capacity, _) = items.size_hint();\n        let mut hashed_items =\n            FxIndexSet::with_capacity_and_hasher(capacity, Default::default());\n        let mut rendered_items = Vec::with_capacity(capacity);\n        for (index, item) in items.enumerate() {\n            hashed_items.insert((self.key_fn)(&item));\n            let (set_index, view) = (self.view_fn)(index, item);\n            let item = view.hydrate::<FROM_SERVER>(cursor, position);\n            rendered_items.push(Some((set_index, item)));\n        }\n        let marker = cursor.next_placeholder(position);\n        position.set(Position::NextChild);\n\n        KeyedState {\n            parent: Some(parent),\n            marker,\n            hashed_items,\n            rendered_items,\n        }\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        // get parent and position\n        let current = cursor.current();\n        let parent = if position.get() == Position::FirstChild {\n            current\n        } else {\n            Rndr::get_parent(&current)\n                .expect(\"first child of keyed list has no parent\")\n        };\n        let parent = crate::renderer::types::Element::cast_from(parent)\n            .expect(\"parent of keyed list should be an element\");\n\n        // build list\n        let items = self.items.into_iter().flatten();\n        let (capacity, _) = items.size_hint();\n        let mut hashed_items =\n            FxIndexSet::with_capacity_and_hasher(capacity, Default::default());\n        let mut rendered_items = Vec::with_capacity(capacity);\n        for (index, item) in items.enumerate() {\n            hashed_items.insert((self.key_fn)(&item));\n            let (set_index, view) = (self.view_fn)(index, item);\n            let item = view.hydrate_async(cursor, position).await;\n            rendered_items.push(Some((set_index, item)));\n        }\n        let marker = cursor.next_placeholder(position);\n        position.set(Position::NextChild);\n\n        KeyedState {\n            parent: Some(parent),\n            marker,\n            hashed_items,\n            rendered_items,\n        }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl<K, VFS, V> Mountable for KeyedState<K, VFS, V>\nwhere\n    K: Eq + Hash + 'static,\n    VFS: Fn(usize),\n    V: Render,\n{\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        self.parent = Some(parent.clone());\n        for (_, item) in self.rendered_items.iter_mut().flatten() {\n            item.mount(parent, marker);\n        }\n        self.marker.mount(parent, marker);\n    }\n\n    fn unmount(&mut self) {\n        for (_, item) in self.rendered_items.iter_mut().flatten() {\n            item.unmount();\n        }\n        self.marker.unmount();\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.rendered_items\n            .first()\n            .map(|item| {\n                if let Some((_, item)) = item {\n                    item.insert_before_this(child)\n                } else {\n                    false\n                }\n            })\n            .unwrap_or_else(|| self.marker.insert_before_this(child))\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.rendered_items\n            .iter()\n            .flatten()\n            .flat_map(|item| item.1.elements())\n            .collect()\n    }\n}\n\ntrait VecExt<T> {\n    fn get_next_closest_mounted_sibling(\n        &self,\n        start_at: usize,\n    ) -> Option<&Option<T>>;\n}\n\nimpl<T> VecExt<T> for Vec<Option<T>> {\n    fn get_next_closest_mounted_sibling(\n        &self,\n        start_at: usize,\n    ) -> Option<&Option<T>> {\n        self[start_at..].iter().find(|s| s.is_some())\n    }\n}\n\n/// Calculates the operations needed to get from `from` to `to`.\nfn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {\n    if from.is_empty() && to.is_empty() {\n        return Diff::default();\n    } else if to.is_empty() {\n        return Diff {\n            clear: true,\n            ..Default::default()\n        };\n    } else if from.is_empty() {\n        return Diff {\n            added: to\n                .iter()\n                .enumerate()\n                .map(|(at, _)| DiffOpAdd {\n                    at,\n                    mode: DiffOpAddMode::Append,\n                })\n                .collect(),\n            ..Default::default()\n        };\n    }\n\n    let mut removed = vec![];\n    let mut moved = vec![];\n    let mut added = vec![];\n    let max_len = std::cmp::max(from.len(), to.len());\n\n    for index in 0..max_len {\n        let from_item = from.get_index(index);\n        let to_item = to.get_index(index);\n\n        // if they're the same, do nothing\n        if from_item != to_item {\n            // if it's only in old, not new, remove it\n            if from_item.is_some() && !to.contains(from_item.unwrap()) {\n                let op = DiffOpRemove { at: index };\n                removed.push(op);\n            }\n            // if it's only in new, not old, add it\n            if to_item.is_some() && !from.contains(to_item.unwrap()) {\n                let op = DiffOpAdd {\n                    at: index,\n                    mode: DiffOpAddMode::Normal,\n                };\n                added.push(op);\n            }\n            // if it's in both old and new, it can either\n            // 1) be moved (and need to move in the DOM)\n            // 2) be moved (but not need to move in the DOM)\n            //    * this would happen if, for example, 2 items\n            //      have been added before it, and it has moved by 2\n            if let Some(from_item) = from_item {\n                if let Some(to_item) = to.get_full(from_item) {\n                    let moves_forward_by = (to_item.0 as i32) - (index as i32);\n                    let move_in_dom = moves_forward_by\n                        != (added.len() as i32) - (removed.len() as i32);\n\n                    let op = DiffOpMove {\n                        from: index,\n                        len: 1,\n                        to: to_item.0,\n                        move_in_dom,\n                    };\n                    moved.push(op);\n                }\n            }\n        }\n    }\n\n    moved = group_adjacent_moves(moved);\n\n    Diff {\n        removed,\n        items_to_move: moved.iter().map(|m| m.len).sum(),\n        moved,\n        added,\n        clear: false,\n    }\n}\n\n/// Group adjacent items that are being moved as a group.\n/// For example from `[2, 3, 5, 6]` to `[1, 2, 3, 4, 5, 6]` should result\n/// in a move for `2,3` and `5,6` rather than 4 individual moves.\nfn group_adjacent_moves(moved: Vec<DiffOpMove>) -> Vec<DiffOpMove> {\n    let mut prev: Option<DiffOpMove> = None;\n    let mut new_moved = Vec::with_capacity(moved.len());\n    for m in moved {\n        match prev {\n            Some(mut p) => {\n                if (m.from == p.from + p.len) && (m.to == p.to + p.len) {\n                    p.len += 1;\n                    prev = Some(p);\n                } else {\n                    new_moved.push(prev.take().unwrap());\n                    prev = Some(m);\n                }\n            }\n            None => prev = Some(m),\n        }\n    }\n    if let Some(prev) = prev {\n        new_moved.push(prev)\n    }\n    new_moved\n}\n\n#[derive(Debug, Default, PartialEq, Eq)]\nstruct Diff {\n    removed: Vec<DiffOpRemove>,\n    moved: Vec<DiffOpMove>,\n    items_to_move: usize,\n    added: Vec<DiffOpAdd>,\n    clear: bool,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\nstruct DiffOpMove {\n    /// The index this range is starting relative to `from`.\n    from: usize,\n    /// The number of elements included in this range.\n    len: usize,\n    /// The starting index this range will be moved to relative to `to`.\n    to: usize,\n    /// Marks this move to be applied to the DOM, or just to the underlying\n    /// storage\n    move_in_dom: bool,\n}\n\nimpl Default for DiffOpMove {\n    fn default() -> Self {\n        Self {\n            from: 0,\n            to: 0,\n            len: 1,\n            move_in_dom: true,\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\nstruct DiffOpAdd {\n    at: usize,\n    mode: DiffOpAddMode,\n}\n\n#[derive(Debug, PartialEq, Eq)]\nstruct DiffOpRemove {\n    at: usize,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]\nenum DiffOpAddMode {\n    #[default]\n    Normal,\n    Append,\n}\n\nfn apply_diff<T, VFS, V>(\n    parent: Option<&crate::renderer::types::Element>,\n    marker: &crate::renderer::types::Placeholder,\n    diff: Diff,\n    children: &mut Vec<Option<(VFS, V::State)>>,\n    view_fn: &dyn Fn(usize, T) -> (VFS, V),\n    mut items: Vec<Option<T>>,\n) where\n    VFS: Fn(usize),\n    V: Render,\n{\n    // The order of cmds needs to be:\n    // 1. Clear\n    // 2. Removals\n    // 3. Move out\n    // 4. Resize\n    // 5. Move in\n    // 6. Additions\n    // 7. Removes holes\n    if diff.clear {\n        for (_, mut child) in children.drain(0..).flatten() {\n            child.unmount();\n        }\n\n        if diff.added.is_empty() {\n            return;\n        }\n    }\n\n    for DiffOpRemove { at } in &diff.removed {\n        let (_, mut item_to_remove) = children[*at].take().unwrap();\n\n        item_to_remove.unmount();\n    }\n\n    let (move_cmds, add_cmds) = unpack_moves(&diff);\n\n    let mut moved_children = move_cmds\n        .iter()\n        .map(|move_| children[move_.from].take())\n        .collect::<Vec<_>>();\n\n    children.resize_with(children.len() + diff.added.len(), || None);\n\n    for (i, DiffOpMove { to, .. }) in move_cmds\n        .iter()\n        .enumerate()\n        .filter(|(_, move_)| !move_.move_in_dom)\n    {\n        children[*to] = moved_children[i]\n            .take()\n            .inspect(|(set_index, _)| set_index(*to));\n    }\n\n    for (i, DiffOpMove { to, .. }) in move_cmds\n        .into_iter()\n        .enumerate()\n        .filter(|(_, move_)| move_.move_in_dom)\n    {\n        let (set_index, mut each_item) = moved_children[i].take().unwrap();\n\n        if let Some(parent) = parent {\n            if let Some(Some((_, state))) =\n                children.get_next_closest_mounted_sibling(to)\n            {\n                state.insert_before_this_or_marker(\n                    parent,\n                    &mut each_item,\n                    Some(marker.as_ref()),\n                )\n            } else {\n                each_item.try_mount(parent, Some(marker.as_ref()));\n            }\n        }\n\n        set_index(to);\n        children[to] = Some((set_index, each_item));\n    }\n\n    for DiffOpAdd { at, mode } in add_cmds {\n        let item = items[at].take().unwrap();\n        let (set_index, item) = view_fn(at, item);\n        let mut item = item.build();\n\n        if let Some(parent) = parent {\n            match mode {\n                DiffOpAddMode::Normal => {\n                    if let Some(Some((_, state))) =\n                        children.get_next_closest_mounted_sibling(at)\n                    {\n                        state.insert_before_this_or_marker(\n                            parent,\n                            &mut item,\n                            Some(marker.as_ref()),\n                        )\n                    } else {\n                        item.try_mount(parent, Some(marker.as_ref()));\n                    }\n                }\n                DiffOpAddMode::Append => {\n                    item.try_mount(parent, Some(marker.as_ref()));\n                }\n            }\n        }\n\n        children[at] = Some((set_index, item));\n    }\n\n    #[allow(unstable_name_collisions)]\n    children.drain_filter(|c| c.is_none());\n}\n\nfn unpack_moves(diff: &Diff) -> (Vec<DiffOpMove>, Vec<DiffOpAdd>) {\n    let mut moves = Vec::with_capacity(diff.items_to_move);\n    let mut adds = Vec::with_capacity(diff.added.len());\n\n    let mut removes_iter = diff.removed.iter();\n    let mut adds_iter = diff.added.iter();\n    let mut moves_iter = diff.moved.iter();\n\n    let mut removes_next = removes_iter.next();\n    let mut adds_next = adds_iter.next();\n    let mut moves_next = moves_iter.next().copied();\n\n    for i in 0..diff.items_to_move + diff.added.len() + diff.removed.len() {\n        if let Some(DiffOpRemove { at, .. }) = removes_next {\n            if i == *at {\n                removes_next = removes_iter.next();\n\n                continue;\n            }\n        }\n\n        match (adds_next, &mut moves_next) {\n            (Some(add), Some(move_)) => {\n                if add.at == i {\n                    adds.push(*add);\n\n                    adds_next = adds_iter.next();\n                } else {\n                    let mut single_move = *move_;\n                    single_move.len = 1;\n\n                    moves.push(single_move);\n\n                    move_.len -= 1;\n                    move_.from += 1;\n                    move_.to += 1;\n\n                    if move_.len == 0 {\n                        moves_next = moves_iter.next().copied();\n                    }\n                }\n            }\n            (Some(add), None) => {\n                adds.push(*add);\n\n                adds_next = adds_iter.next();\n            }\n            (None, Some(move_)) => {\n                let mut single_move = *move_;\n                single_move.len = 1;\n\n                moves.push(single_move);\n\n                move_.len -= 1;\n                move_.from += 1;\n                move_.to += 1;\n\n                if move_.len == 0 {\n                    moves_next = moves_iter.next().copied();\n                }\n            }\n            (None, None) => break,\n        }\n    }\n\n    (moves, adds)\n}\n/*\n#[cfg(test)]\nmod tests {\n    use crate::{\n        html::element::{li, ul, HtmlElement, Li},\n        renderer::mock_dom::MockDom,\n        view::{keyed::keyed, Render},\n    };\n\n    fn item(key: usize) -> HtmlElement<Li, (), String, MockDom> {\n        li((), key.to_string())\n    }\n\n    #[test]\n    fn keyed_creates_list() {\n        let el = ul((), keyed(1..=3, |k| *k, item));\n        let el_state = el.build();\n        assert_eq!(\n            el_state.el.to_debug_html(),\n            \"<ul><li>1</li><li>2</li><li>3</li></ul>\"\n        );\n    }\n\n    #[test]\n    fn adding_items_updates_list() {\n        let el = ul((), keyed(1..=3, |k| *k, item));\n        let mut el_state = el.build();\n        let el = ul((), keyed(1..=5, |k| *k, item));\n        el.rebuild(&mut el_state);\n        assert_eq!(\n            el_state.el.to_debug_html(),\n            \"<ul><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>\"\n        );\n    }\n\n    #[test]\n    fn removing_items_updates_list() {\n        let el = ul((), keyed(1..=3, |k| *k, item));\n        let mut el_state = el.build();\n        let el = ul((), keyed(1..=2, |k| *k, item));\n        el.rebuild(&mut el_state);\n        assert_eq!(\n            el_state.el.to_debug_html(),\n            \"<ul><li>1</li><li>2</li></ul>\"\n        );\n    }\n\n    #[test]\n    fn swapping_items_updates_list() {\n        let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));\n        let mut el_state = el.build();\n        let el = ul((), keyed([1, 4, 3, 2, 5], |k| *k, item));\n        el.rebuild(&mut el_state);\n        assert_eq!(\n            el_state.el.to_debug_html(),\n            \"<ul><li>1</li><li>4</li><li>3</li><li>2</li><li>5</li></ul>\"\n        );\n    }\n\n    #[test]\n    fn swapping_and_removing_orders_correctly() {\n        let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));\n        let mut el_state = el.build();\n        let el = ul((), keyed([1, 4, 3, 5], |k| *k, item));\n        el.rebuild(&mut el_state);\n        assert_eq!(\n            el_state.el.to_debug_html(),\n            \"<ul><li>1</li><li>4</li><li>3</li><li>5</li></ul>\"\n        );\n    }\n\n    #[test]\n    fn arbitrarily_hard_adjustment() {\n        let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));\n        let mut el_state = el.build();\n        let el = ul((), keyed([2, 4, 3], |k| *k, item));\n        el.rebuild(&mut el_state);\n        assert_eq!(\n            el_state.el.to_debug_html(),\n            \"<ul><li>2</li><li>4</li><li>3</li></ul>\"\n        );\n    }\n\n    #[test]\n    fn a_series_of_moves() {\n        let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));\n        let mut el_state = el.build();\n        let el = ul((), keyed([2, 4, 3], |k| *k, item));\n        el.rebuild(&mut el_state);\n        let el = ul((), keyed([1, 7, 5, 11, 13, 17], |k| *k, item));\n        el.rebuild(&mut el_state);\n        let el = ul((), keyed([2, 6, 8, 7, 13], |k| *k, item));\n        el.rebuild(&mut el_state);\n        let el = ul((), keyed([13, 4, 5, 3], |k| *k, item));\n        el.rebuild(&mut el_state);\n        let el = ul((), keyed([1, 2, 3, 4], |k| *k, item));\n        el.rebuild(&mut el_state);\n        assert_eq!(\n            el_state.el.to_debug_html(),\n            \"<ul><li>1</li><li>2</li><li>3</li><li>4</li></ul>\"\n        );\n    }\n\n    #[test]\n    fn clearing_works() {\n        let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));\n        let mut el_state = el.build();\n        let el = ul((), keyed([], |k| *k, item));\n        el.rebuild(&mut el_state);\n        assert_eq!(el_state.el.to_debug_html(), \"<ul></ul>\");\n    }\n}\n*/\n"
  },
  {
    "path": "tachys/src/view/mod.rs",
    "content": "use self::add_attr::AddAnyAttr;\nuse crate::{\n    html::attribute::any_attribute::AnyAttribute, hydration::Cursor,\n    ssr::StreamBuilder,\n};\nuse or_poisoned::OrPoisoned;\nuse std::{\n    cell::RefCell,\n    future::Future,\n    rc::Rc,\n    sync::{Arc, RwLock},\n};\n\n/// Add attributes to typed views.\npub mod add_attr;\n/// A typed-erased view type.\npub mod any_view;\n/// Allows choosing between one of several views.\npub mod either;\n/// View rendering for `Result<_, _>` types.\npub mod error_boundary;\n/// A type-erased view collection.\npub mod fragment;\n/// View implementations for several iterable types.\npub mod iterators;\n/// Keyed list iteration.\npub mod keyed;\nmod primitives;\n/// Optimized types for static strings known at compile time.\n#[cfg(all(feature = \"nightly\", rustc_nightly))]\npub mod static_types;\n/// View implementation for string types.\npub mod strings;\n/// Optimizations for creating views via HTML `<template>` nodes.\npub mod template;\n/// View implementations for tuples.\npub mod tuples;\n\n/// The `Render` trait allows rendering something as part of the user interface.\npub trait Render: Sized {\n    /// The “view state” for this type, which can be retained between updates.\n    ///\n    /// For example, for a text node, `State` might be the actual DOM text node\n    /// and the previous string, to allow for diffing between updates.\n    type State: Mountable;\n\n    /// Creates the view for the first time, without hydrating from existing HTML.\n    fn build(self) -> Self::State;\n\n    /// Updates the view with new data.\n    fn rebuild(self, state: &mut Self::State);\n}\n\n#[doc(hidden)]\npub trait MarkBranch {\n    fn open_branch(&mut self, branch_id: &str);\n\n    fn close_branch(&mut self, branch_id: &str);\n}\n\nimpl MarkBranch for String {\n    fn open_branch(&mut self, branch_id: &str) {\n        self.push_str(\"<!--bo-\");\n        self.push_str(branch_id);\n        self.push_str(\"-->\");\n    }\n\n    fn close_branch(&mut self, branch_id: &str) {\n        self.push_str(\"<!--bc-\");\n        self.push_str(branch_id);\n        self.push_str(\"-->\");\n    }\n}\n\nimpl MarkBranch for StreamBuilder {\n    fn open_branch(&mut self, branch_id: &str) {\n        self.sync_buf.push_str(\"<!--bo-\");\n        self.sync_buf.push_str(branch_id);\n        self.sync_buf.push_str(\"-->\");\n    }\n\n    fn close_branch(&mut self, branch_id: &str) {\n        self.sync_buf.push_str(\"<!--bc-\");\n        self.sync_buf.push_str(branch_id);\n        self.sync_buf.push_str(\"-->\");\n    }\n}\n\n/// The `RenderHtml` trait allows rendering something to HTML, and transforming\n/// that HTML into an interactive interface.\n///\n/// This process is traditionally called “server rendering” and “hydration.” As a\n/// metaphor, this means that the structure of the view is created on the server, then\n/// “dehydrated” to HTML, sent across the network, and “rehydrated” with interactivity\n/// in the browser.\n///\n/// However, the same process can be done entirely in the browser: for example, a view\n/// can be transformed into some HTML that is used to create a `<template>` node, which\n/// can be cloned many times and “hydrated,” which is more efficient than creating the\n/// whole view piece by piece.\npub trait RenderHtml\nwhere\n    Self: Render + AddAnyAttr + Send,\n{\n    /// The type of the view after waiting for all asynchronous data to load.\n    type AsyncOutput: RenderHtml;\n\n    /// An equivalent value that is `'static`.\n    type Owned: RenderHtml + 'static;\n\n    /// The minimum length of HTML created when this view is rendered.\n    const MIN_LENGTH: usize;\n\n    /// Whether this should actually exist in the DOM, if it is the child of an element.\n    const EXISTS: bool = true;\n\n    /// “Runs” the view without other side effects. For primitive types, this is a no-op. For\n    /// reactive types, this can be used to gather data about reactivity or about asynchronous data\n    /// that needs to be loaded.\n    fn dry_resolve(&mut self);\n\n    /// Waits for any asynchronous sections of the view to load and returns the output.\n    fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;\n\n    /// An estimated length for this view, when rendered to HTML.\n    ///\n    /// This is used for calculating the string buffer size when rendering HTML. It does not need\n    /// to be precise, but should be an appropriate estimate. The more accurate, the fewer\n    /// reallocations will be required and the faster server-side rendering will be.\n    fn html_len(&self) -> usize {\n        Self::MIN_LENGTH\n    }\n\n    /// Renders a view to an HTML string.\n    fn to_html(self) -> String\n    where\n        Self: Sized,\n    {\n        let mut buf = String::with_capacity(self.html_len());\n        self.to_html_with_buf(\n            &mut buf,\n            &mut Position::FirstChild,\n            true,\n            false,\n            vec![],\n        );\n        buf\n    }\n\n    /// Renders a view to HTML with branch markers. This can be used to support libraries that diff\n    /// HTML pages against one another, by marking sections of the view that branch to different\n    /// types with marker comments.\n    fn to_html_branching(self) -> String\n    where\n        Self: Sized,\n    {\n        let mut buf = String::with_capacity(self.html_len());\n        self.to_html_with_buf(\n            &mut buf,\n            &mut Position::FirstChild,\n            true,\n            true,\n            vec![],\n        );\n        buf\n    }\n\n    /// Renders a view to an in-order stream of HTML.\n    fn to_html_stream_in_order(self) -> StreamBuilder\n    where\n        Self: Sized,\n    {\n        let mut builder = StreamBuilder::with_capacity(self.html_len(), None);\n        self.to_html_async_with_buf::<false>(\n            &mut builder,\n            &mut Position::FirstChild,\n            true,\n            false,\n            vec![],\n        );\n        builder.finish()\n    }\n\n    /// Renders a view to an in-order stream of HTML with branch markers. This can be used to support libraries that diff\n    /// HTML pages against one another, by marking sections of the view that branch to different\n    /// types with marker comments.\n    fn to_html_stream_in_order_branching(self) -> StreamBuilder\n    where\n        Self: Sized,\n    {\n        let mut builder = StreamBuilder::with_capacity(self.html_len(), None);\n        self.to_html_async_with_buf::<false>(\n            &mut builder,\n            &mut Position::FirstChild,\n            true,\n            true,\n            vec![],\n        );\n        builder.finish()\n    }\n\n    /// Renders a view to an out-of-order stream of HTML.\n    fn to_html_stream_out_of_order(self) -> StreamBuilder\n    where\n        Self: Sized,\n    {\n        //let capacity = self.html_len();\n        let mut builder =\n            StreamBuilder::with_capacity(self.html_len(), Some(vec![0]));\n\n        self.to_html_async_with_buf::<true>(\n            &mut builder,\n            &mut Position::FirstChild,\n            true,\n            false,\n            vec![],\n        );\n        builder.finish()\n    }\n\n    /// Renders a view to an out-of-order stream of HTML with branch markers. This can be used to support libraries that diff\n    /// HTML pages against one another, by marking sections of the view that branch to different\n    /// types with marker comments.\n    fn to_html_stream_out_of_order_branching(self) -> StreamBuilder\n    where\n        Self: Sized,\n    {\n        let mut builder =\n            StreamBuilder::with_capacity(self.html_len(), Some(vec![0]));\n\n        self.to_html_async_with_buf::<true>(\n            &mut builder,\n            &mut Position::FirstChild,\n            true,\n            true,\n            vec![],\n        );\n        builder.finish()\n    }\n\n    /// Renders a view to HTML, writing it into the given buffer.\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    );\n\n    /// Renders a view into a buffer of (synchronous or asynchronous) HTML chunks.\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        buf.with_buf(|buf| {\n            self.to_html_with_buf(\n                buf,\n                position,\n                escape,\n                mark_branches,\n                extra_attrs,\n            )\n        });\n    }\n\n    /// Makes a set of DOM nodes rendered from HTML interactive.\n    ///\n    /// If `FROM_SERVER` is `true`, this HTML was rendered using [`RenderHtml::to_html`]\n    /// (e.g., during server-side rendering ).\n    ///\n    /// If `FROM_SERVER` is `false`, the HTML was rendered using [`ToTemplate::to_template`]\n    /// (e.g., into a `<template>` element).\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State;\n\n    /// Asynchronously makes a set of DOM nodes rendered from HTML interactive.\n    ///\n    /// Async hydration is useful for types that may need to wait before being hydrated:\n    /// for example, lazily-loaded routes need async hydration, because the client code\n    /// may be loading asynchronously, while the server HTML was already rendered.\n    fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> impl Future<Output = Self::State> {\n        async { self.hydrate::<true>(cursor, position) }\n    }\n\n    /// Hydrates using [`RenderHtml::hydrate`], beginning at the given element.\n    fn hydrate_from<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n    ) -> Self::State\n    where\n        Self: Sized,\n    {\n        self.hydrate_from_position::<FROM_SERVER>(el, Position::default())\n    }\n\n    /// Hydrates using [`RenderHtml::hydrate`], beginning at the given element and position.\n    fn hydrate_from_position<const FROM_SERVER: bool>(\n        self,\n        el: &crate::renderer::types::Element,\n        position: Position,\n    ) -> Self::State\n    where\n        Self: Sized,\n    {\n        let cursor = Cursor::new(el.clone());\n        let position = PositionState::new(position);\n        self.hydrate::<FROM_SERVER>(&cursor, &position)\n    }\n\n    /// Convert into the equivalent value that is `'static`.\n    fn into_owned(self) -> Self::Owned;\n}\n\n/// Allows a type to be mounted to the DOM.\npub trait Mountable {\n    /// Detaches the view from the DOM.\n    fn unmount(&mut self);\n\n    /// Mounts a node to the interface.\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    );\n\n    /// Mounts a node to the interface. Returns `false` if it could not be mounted.\n    fn try_mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) -> bool {\n        self.mount(parent, marker);\n        true\n    }\n\n    /// Inserts another `Mountable` type before this one. Returns `false` if\n    /// this does not actually exist in the UI (for example, `()`).\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool;\n\n    /// Inserts another `Mountable` type before this one, or before the marker\n    /// if this one doesn't exist in the UI (for example, `()`).\n    fn insert_before_this_or_marker(\n        &self,\n        parent: &crate::renderer::types::Element,\n        child: &mut dyn Mountable,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        if !self.insert_before_this(child) {\n            child.mount(parent, marker);\n        }\n    }\n\n    /// wip\n    fn elements(&self) -> Vec<crate::renderer::types::Element>;\n}\n\n/// Indicates where a node should be mounted to its parent.\npub enum MountKind {\n    /// Node should be mounted before this marker node.\n    Before(crate::renderer::types::Node),\n    /// Node should be appended to the parent’s children.\n    Append,\n}\n\nimpl<T> Mountable for Option<T>\nwhere\n    T: Mountable,\n{\n    fn unmount(&mut self) {\n        if let Some(ref mut mounted) = self {\n            mounted.unmount()\n        }\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        if let Some(ref mut inner) = self {\n            inner.mount(parent, marker);\n        }\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.as_ref()\n            .map(|inner| inner.insert_before_this(child))\n            .unwrap_or(false)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.as_ref()\n            .map(|inner| inner.elements())\n            .unwrap_or_default()\n    }\n}\n\nimpl<T> Mountable for Rc<RefCell<T>>\nwhere\n    T: Mountable,\n{\n    fn unmount(&mut self) {\n        self.borrow_mut().unmount()\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        self.borrow_mut().mount(parent, marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.borrow().insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        self.borrow().elements()\n    }\n}\n\n/// Allows data to be added to a static template.\npub trait ToTemplate {\n    /// The HTML content of the static template.\n    const TEMPLATE: &'static str = \"\";\n    /// The `class` attribute content known at compile time.\n    const CLASS: &'static str = \"\";\n    /// The `style` attribute content known at compile time.\n    const STYLE: &'static str = \"\";\n    /// The length of the template.\n    const LEN: usize = Self::TEMPLATE.len();\n\n    /// Renders a view type to a template. This does not take actual view data,\n    /// but can be used for constructing part of an HTML `<template>` that corresponds\n    /// to a view of a particular type.\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    );\n\n    /// Renders a view type to a template in attribute position.\n    fn to_template_attribute(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        Self::to_template(buf, class, style, inner_html, position);\n    }\n}\n\n/// Keeps track of what position the item currently being hydrated is in, relative to its siblings\n/// and parents.\n#[derive(Debug, Default, Clone)]\npub struct PositionState(Arc<RwLock<Position>>);\n\nimpl PositionState {\n    /// Creates a new position tracker.\n    pub fn new(position: Position) -> Self {\n        Self(Arc::new(RwLock::new(position)))\n    }\n\n    /// Sets the current position.\n    pub fn set(&self, position: Position) {\n        *self.0.write().or_poisoned() = position;\n    }\n\n    /// Gets the current position.\n    pub fn get(&self) -> Position {\n        *self.0.read().or_poisoned()\n    }\n\n    /// Creates a new [`PositionState`], which starts with the same [`Position`], but no longer\n    /// shares data with this `PositionState`.\n    pub fn deep_clone(&self) -> Self {\n        let current = self.get();\n        Self(Arc::new(RwLock::new(current)))\n    }\n}\n\n/// The position of this element, relative to others.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]\npub enum Position {\n    /// This is the current node.\n    Current,\n    /// This is the first child of its parent.\n    #[default]\n    FirstChild,\n    /// This is the next child after another child.\n    NextChild,\n    /// This is the next child after a text node.\n    NextChildAfterText,\n    /// This is the only child of its parent.\n    OnlyChild,\n    /// This is the last child of its parent.\n    LastChild,\n}\n\n/// Declares that this type can be converted into some other type, which can be rendered.\npub trait IntoRender {\n    /// The renderable type into which this type can be converted.\n    type Output;\n\n    /// Consumes this value, transforming it into the renderable type.\n    fn into_render(self) -> Self::Output;\n}\n\nimpl<T> IntoRender for T\nwhere\n    T: Render,\n{\n    type Output = Self;\n\n    fn into_render(self) -> Self::Output {\n        self\n    }\n}\n"
  },
  {
    "path": "tachys/src/view/primitives.rs",
    "content": "use super::{Mountable, Position, PositionState, Render, RenderHtml};\nuse crate::{\n    html::attribute::any_attribute::AnyAttribute,\n    hydration::Cursor,\n    no_attrs,\n    renderer::{CastFrom, Rndr},\n    view::ToTemplate,\n};\nuse std::{\n    fmt::Write,\n    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},\n    num::{\n        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8,\n        NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64,\n        NonZeroU8, NonZeroUsize,\n    },\n};\n\n// any changes here should also be made in src/reactive_graph/guards.rs\nmacro_rules! render_primitive {\n  ($($child_type:ty),* $(,)?) => {\n    $(\n\t\tpaste::paste! {\n\t\t\tpub struct [<$child_type:camel State>](crate::renderer::types::Text, $child_type);\n\n\t\t\timpl Mountable for [<$child_type:camel State>] {\n\t\t\t\t\tfn unmount(&mut self) {\n\t\t\t\t\t\tself.0.unmount()\n\t\t\t\t\t}\n\n\t\t\t\t\tfn mount(\n\t\t\t\t\t\t&mut self,\n\t\t\t\t\t\tparent: &crate::renderer::types::Element,\n\t\t\t\t\t\tmarker: Option<&crate::renderer::types::Node>,\n\t\t\t\t\t) {\n\t\t\t\t\t\tRndr::insert_node(parent, self.0.as_ref(), marker);\n\t\t\t\t\t}\n\n\t\t\t\t\tfn insert_before_this(&self,\n\t\t\t\t\t\tchild: &mut dyn Mountable,\n\t\t\t\t\t) -> bool {\n                        self.0.insert_before_this(child)\n\t\t\t\t\t}\n\n\t\t\t\t\tfn elements(&self) -> Vec<crate::renderer::types::Element> {\n\t\t\t\t\t\tvec![]\n\t\t\t\t\t}\n\t\t\t}\n\n\t\t\timpl Render for $child_type {\n\t\t\t\ttype State = [<$child_type:camel State>];\n\n\n\t\t\t\tfn build(self) -> Self::State {\n\t\t\t\t\tlet node = Rndr::create_text_node(&self.to_string());\n\t\t\t\t\t[<$child_type:camel State>](node, self)\n\t\t\t\t}\n\n\t\t\t\tfn rebuild(self, state: &mut Self::State) {\n\t\t\t\t\tlet [<$child_type:camel State>](node, this) = state;\n\t\t\t\t\tif &self != this {\n\t\t\t\t\t\tRndr::set_text(node, &self.to_string());\n\t\t\t\t\t\t*this = self;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n            no_attrs!($child_type);\n\n\t\t\timpl RenderHtml for $child_type\n\t\t\t{\n\t\t\t\ttype AsyncOutput = Self;\n\t\t\t\ttype Owned = Self;\n\n\t\t\t\tconst MIN_LENGTH: usize = 0;\n\n                fn dry_resolve(&mut self) {}\n\n                async fn resolve(self) -> Self::AsyncOutput {\n                    self\n                }\n\n\t\t\t\tfn to_html_with_buf(self, buf: &mut String, position: &mut Position, _escape: bool, _mark_branches: bool, _extra_attrs: Vec<AnyAttribute>) {\n\t\t\t\t\t// add a comment node to separate from previous sibling, if any\n\t\t\t\t\tif matches!(position, Position::NextChildAfterText) {\n\t\t\t\t\t\tbuf.push_str(\"<!>\")\n\t\t\t\t\t}\n\t\t\t\t\t_ = write!(buf, \"{}\", self);\n\t\t\t\t\t*position = Position::NextChildAfterText;\n\t\t\t\t}\n\n\t\t\t\tfn hydrate<const FROM_SERVER: bool>(\n\t\t\t\t\tself,\n\t\t\t\t\tcursor: &Cursor,\n\t\t\t\t\tposition: &PositionState,\n\t\t\t\t) -> Self::State {\n\t\t\t\t\tif position.get() == Position::FirstChild {\n\t\t\t\t\t\tcursor.child();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcursor.sibling();\n\t\t\t\t\t}\n\n\t\t\t\t\t// separating placeholder marker comes before text node\n\t\t\t\t\tif matches!(position.get(), Position::NextChildAfterText) {\n\t\t\t\t\t\tcursor.sibling();\n\t\t\t\t\t}\n\n\t\t\t\t\tlet node = cursor.current();\n\t\t\t\t\tlet node = crate::renderer::types::Text::cast_from(node.clone())\n\t\t\t\t\t\t.unwrap_or_else(|| crate::hydration::failed_to_cast_text_node(node));\n\n\t\t\t\t\tif !FROM_SERVER {\n\t\t\t\t\t\tRndr::set_text(&node, &self.to_string());\n\t\t\t\t\t}\n\t\t\t\t\tposition.set(Position::NextChildAfterText);\n\n\t\t\t\t\t[<$child_type:camel State>](node, self)\n\t\t\t\t}\n\n\t\t\t\tfn into_owned(self) -> Self::Owned {\n\t\t\t\t\tself\n\t\t\t\t}\n\t\t\t}\n\n\t\t\timpl<'a> ToTemplate for $child_type {\n\t\t\t\tconst TEMPLATE: &'static str = \" <!>\";\n\n\t\t\t\tfn to_template(\n\t\t\t\t\tbuf: &mut String,\n\t\t\t\t\t_class: &mut String,\n\t\t\t\t\t_style: &mut String,\n\t\t\t\t\t_inner_html: &mut String,\n\t\t\t\t\tposition: &mut Position,\n\t\t\t\t) {\n\t\t\t\t\tif matches!(*position, Position::NextChildAfterText) {\n\t\t\t\t\t\tbuf.push_str(\"<!>\")\n\t\t\t\t\t}\n\t\t\t\t\tbuf.push(' ');\n\t\t\t\t\t*position = Position::NextChildAfterText;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    )*\n  };\n}\n\nrender_primitive![\n    usize,\n    u8,\n    u16,\n    u32,\n    u64,\n    u128,\n    isize,\n    i8,\n    i16,\n    i32,\n    i64,\n    i128,\n    f32,\n    f64,\n    char,\n    bool,\n    IpAddr,\n    SocketAddr,\n    SocketAddrV4,\n    SocketAddrV6,\n    Ipv4Addr,\n    Ipv6Addr,\n    NonZeroI8,\n    NonZeroU8,\n    NonZeroI16,\n    NonZeroU16,\n    NonZeroI32,\n    NonZeroU32,\n    NonZeroI64,\n    NonZeroU64,\n    NonZeroI128,\n    NonZeroU128,\n    NonZeroIsize,\n    NonZeroUsize,\n];\n"
  },
  {
    "path": "tachys/src/view/static_types.rs",
    "content": "use super::{\n    add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n    RenderHtml, ToTemplate,\n};\nuse crate::{\n    html::attribute::{\n        any_attribute::AnyAttribute,\n        maybe_next_attr_erasure_macros::{\n            next_attr_combine, next_attr_output_type,\n        },\n        Attribute, AttributeKey, AttributeValue, NamedAttributeKey,\n        NextAttribute,\n    },\n    hydration::Cursor,\n    renderer::{CastFrom, Rndr},\n};\nuse std::marker::PhantomData;\n\n/// An attribute for which both the key and the value are known at compile time,\n/// i.e., as `&'static str`s.\n#[derive(Debug)]\npub struct StaticAttr<K: AttributeKey, const V: &'static str> {\n    ty: PhantomData<K>,\n}\n\nimpl<K: AttributeKey, const V: &'static str> Clone for StaticAttr<K, V> {\n    fn clone(&self) -> Self {\n        Self { ty: PhantomData }\n    }\n}\n\nimpl<K: AttributeKey, const V: &'static str> PartialEq for StaticAttr<K, V> {\n    fn eq(&self, _other: &Self) -> bool {\n        // by definition, two static attrs with same key and same const V are same\n        true\n    }\n}\n\n/// Creates an [`Attribute`] whose key and value are both known at compile time.\npub fn static_attr<K: AttributeKey, const V: &'static str>() -> StaticAttr<K, V>\n{\n    StaticAttr { ty: PhantomData }\n}\n\nimpl<K, const V: &'static str> ToTemplate for StaticAttr<K, V>\nwhere\n    K: AttributeKey,\n{\n    fn to_template(\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n        buf.push(' ');\n        buf.push_str(K::KEY);\n        buf.push_str(\"=\\\"\");\n        buf.push_str(V);\n        buf.push('\"');\n    }\n}\n\nimpl<K, const V: &'static str> Attribute for StaticAttr<K, V>\nwhere\n    K: AttributeKey,\n{\n    const MIN_LENGTH: usize = K::KEY.len() + 3 + V.len(); // K::KEY + =\"...\" + V\n\n    type AsyncOutput = Self;\n    type State = ();\n    type Cloneable = Self;\n    type CloneableOwned = Self;\n\n    #[inline(always)]\n    fn html_len(&self) -> usize {\n        K::KEY.len() + 3 + V.len()\n    }\n\n    fn to_html(\n        self,\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n    ) {\n        AttributeValue::to_html(V, K::KEY, buf)\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        _el: &crate::renderer::types::Element,\n    ) -> Self::State {\n    }\n\n    fn build(self, el: &crate::renderer::types::Element) -> Self::State {\n        Rndr::set_attribute(el, K::KEY, V);\n    }\n\n    fn rebuild(self, _state: &mut Self::State) {}\n\n    fn into_cloneable(self) -> Self::Cloneable {\n        self\n    }\n\n    fn into_cloneable_owned(self) -> Self::CloneableOwned {\n        self\n    }\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn keys(&self) -> Vec<NamedAttributeKey> {\n        vec![NamedAttributeKey::Attribute(K::KEY.into())]\n    }\n}\n\nimpl<K, const V: &'static str> NextAttribute for StaticAttr<K, V>\nwhere\n    K: AttributeKey,\n{\n    next_attr_output_type!(Self, NewAttr);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        new_attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        next_attr_combine!(StaticAttr::<K, V> { ty: PhantomData }, new_attr)\n    }\n}\n\n/// A static string that is known at compile time and can be optimized by including its type in the\n/// view tree.\n#[derive(Debug, Clone, Copy)]\npub struct Static<const V: &'static str>;\n\nimpl<const V: &'static str> PartialEq for Static<V> {\n    fn eq(&self, _other: &Self) -> bool {\n        // by definition, two static values of same const V are same\n        true\n    }\n}\n\nimpl<const V: &'static str> AsRef<str> for Static<V> {\n    fn as_ref(&self) -> &str {\n        V\n    }\n}\n\nimpl<const V: &'static str> Render for Static<V>\nwhere\n    crate::renderer::types::Text: Mountable,\n{\n    type State = Option<crate::renderer::types::Text>;\n\n    fn build(self) -> Self::State {\n        // a view state has to be returned so it can be mounted\n        Some(Rndr::create_text_node(V))\n    }\n\n    // This type is specified as static, so no rebuilding is done.\n    fn rebuild(self, _state: &mut Self::State) {}\n}\n\nimpl<const V: &'static str> RenderHtml for Static<V> {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = V.len();\n\n    fn dry_resolve(&mut self) {}\n\n    // this won't actually compile because if a weird interaction because the const &'static str and\n    // the RPITIT, so we just refine it to a concrete future type; this will never change in any\n    // case\n    #[allow(refining_impl_trait)]\n    fn resolve(self) -> std::future::Ready<Self> {\n        std::future::ready(self)\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // add a comment node to separate from previous sibling, if any\n        if matches!(position, Position::NextChildAfterText) {\n            buf.push_str(\"<!>\")\n        }\n        if V.is_empty() && escape {\n            buf.push(' ');\n        } else if escape {\n            let escaped = html_escape::encode_text(V);\n            buf.push_str(&escaped);\n        } else {\n            buf.push_str(V);\n        }\n        *position = Position::NextChildAfterText;\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        if position.get() == Position::FirstChild {\n            cursor.child();\n        } else {\n            cursor.sibling();\n        }\n\n        // separating placeholder marker comes before text node\n        if matches!(position.get(), Position::NextChildAfterText) {\n            cursor.sibling();\n        }\n\n        let node = cursor.current();\n        let node = crate::renderer::types::Text::cast_from(node.clone())\n            .unwrap_or_else(|| {\n                crate::hydration::failed_to_cast_text_node(node)\n            });\n\n        position.set(Position::NextChildAfterText);\n\n        Some(node)\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl<const V: &'static str> AddAnyAttr for Static<V> {\n    type Output<NewAttr: Attribute> = Static<V>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        // inline helper function to assist the compiler with type inference\n        #[inline(always)]\n        const fn create_static<const S: &'static str, A: Attribute>(\n        ) -> <Static<S> as AddAnyAttr>::Output<A> {\n            Static\n        }\n\n        // call the helper function with the current const value and new attribute type\n        create_static::<V, NewAttr>()\n    }\n}\n\nimpl<const V: &'static str> ToTemplate for Static<V> {\n    const TEMPLATE: &'static str = V;\n\n    fn to_template(\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        if matches!(*position, Position::NextChildAfterText) {\n            buf.push_str(\"<!>\")\n        }\n        buf.push_str(V);\n        *position = Position::NextChildAfterText;\n    }\n}\n"
  },
  {
    "path": "tachys/src/view/strings.rs",
    "content": "use super::{\n    Mountable, Position, PositionState, Render, RenderHtml, ToTemplate,\n};\nuse crate::{\n    html::attribute::any_attribute::AnyAttribute,\n    hydration::Cursor,\n    no_attrs,\n    renderer::{CastFrom, Rndr},\n};\nuse std::{borrow::Cow, rc::Rc, sync::Arc};\n\nno_attrs!(&'a str);\nno_attrs!(String);\nno_attrs!(Arc<str>);\nno_attrs!(Cow<'a, str>);\n\n/// Retained view state for `&str`.\npub struct StrState<'a> {\n    pub(crate) node: crate::renderer::types::Text,\n    str: &'a str,\n}\n\nimpl<'a> Render for &'a str {\n    type State = StrState<'a>;\n\n    fn build(self) -> Self::State {\n        let node = Rndr::create_text_node(self);\n        StrState { node, str: self }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let StrState { node, str } = state;\n        if &self != str {\n            Rndr::set_text(node, self);\n            *str = self;\n        }\n    }\n}\n\nimpl RenderHtml for &str {\n    type AsyncOutput = Self;\n    type Owned = String;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        // add a comment node to separate from previous sibling, if any\n        if matches!(position, Position::NextChildAfterText) {\n            buf.push_str(\"<!>\")\n        }\n        if self.is_empty() && escape {\n            buf.push(' ');\n        } else if escape {\n            let escaped = html_escape::encode_text(self);\n            buf.push_str(&escaped);\n        } else {\n            buf.push_str(self);\n        }\n        *position = Position::NextChildAfterText;\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        if position.get() == Position::FirstChild {\n            cursor.child();\n        } else {\n            cursor.sibling();\n        }\n\n        // separating placeholder marker comes before text node\n        if matches!(position.get(), Position::NextChildAfterText) {\n            cursor.sibling();\n        }\n\n        let node = cursor.current();\n        let node = crate::renderer::types::Text::cast_from(node.clone())\n            .unwrap_or_else(|| {\n                crate::hydration::failed_to_cast_text_node(node)\n            });\n\n        if !FROM_SERVER {\n            Rndr::set_text(&node, self);\n        }\n        position.set(Position::NextChildAfterText);\n\n        StrState { node, str: self }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self.to_string()\n    }\n}\n\nimpl ToTemplate for &str {\n    const TEMPLATE: &'static str = \" <!>\";\n\n    fn to_template(\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        if matches!(*position, Position::NextChildAfterText) {\n            buf.push_str(\"<!>\")\n        }\n        buf.push(' ');\n        *position = Position::NextChildAfterText;\n    }\n}\n\nimpl Mountable for StrState<'_> {\n    fn unmount(&mut self) {\n        self.node.unmount()\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        Rndr::insert_node(parent, self.node.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.node.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\n/// Retained view state for `String`.\npub struct StringState {\n    node: crate::renderer::types::Text,\n    str: String,\n}\n\nimpl Render for String {\n    type State = StringState;\n\n    fn build(self) -> Self::State {\n        let node = Rndr::create_text_node(&self);\n        StringState { node, str: self }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let StringState { node, str } = state;\n        if &self != str {\n            Rndr::set_text(node, &self);\n            *str = self;\n        }\n    }\n}\n\nimpl RenderHtml for String {\n    const MIN_LENGTH: usize = 0;\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        <&str as RenderHtml>::to_html_with_buf(\n            self.as_str(),\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let StrState { node, .. } =\n            self.as_str().hydrate::<FROM_SERVER>(cursor, position);\n        StringState { node, str: self }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl ToTemplate for String {\n    const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;\n\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        <&str as ToTemplate>::to_template(\n            buf, class, style, inner_html, position,\n        )\n    }\n}\n\nimpl Mountable for StringState {\n    fn unmount(&mut self) {\n        self.node.unmount()\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        Rndr::insert_node(parent, self.node.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.node.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\n/// Retained view state for `Rc<str>`.\npub struct RcStrState {\n    node: crate::renderer::types::Text,\n    str: Rc<str>,\n}\n\nimpl Render for Rc<str> {\n    type State = RcStrState;\n\n    fn build(self) -> Self::State {\n        let node = Rndr::create_text_node(&self);\n        RcStrState { node, str: self }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let RcStrState { node, str } = state;\n        if !Rc::ptr_eq(&self, str) {\n            Rndr::set_text(node, &self);\n            *str = self;\n        }\n    }\n}\n\n// can't Send an Rc<str> between threads, so can't implement async HTML rendering that might need\n// to send it\n/*\nimpl RenderHtml for Rc<str>\nwhere\n\n{\n    type AsyncOutput = Self;\n\n    const MIN_LENGTH: usize = 0;\n\n    async fn resolve(self) -> Self::AsyncOutput {\n    self\n    }\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html_with_buf(self, buf: &mut String, position: &mut Position, escape: bool, mark_branches: bool) {\n        <&str as RenderHtml>::to_html_with_buf(&self, buf, position)\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let this: &str = self.as_ref();\n        let StrState { node, .. } =\n            this.hydrate::<FROM_SERVER>(cursor, position);\n        RcStrState { node, str: self }\n    }\n}*/\n\nimpl ToTemplate for Rc<str> {\n    const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;\n\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        <&str as ToTemplate>::to_template(\n            buf, class, style, inner_html, position,\n        )\n    }\n}\n\nimpl Mountable for RcStrState {\n    fn unmount(&mut self) {\n        self.node.unmount()\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        Rndr::insert_node(parent, self.node.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.node.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\n/// Retained view state for `Arc<str>`.\npub struct ArcStrState {\n    node: crate::renderer::types::Text,\n    str: Arc<str>,\n}\n\nimpl Render for Arc<str> {\n    type State = ArcStrState;\n\n    fn build(self) -> Self::State {\n        let node = Rndr::create_text_node(&self);\n        ArcStrState { node, str: self }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let ArcStrState { node, str } = state;\n        if self != *str {\n            Rndr::set_text(node, &self);\n            *str = self;\n        }\n    }\n}\n\nimpl RenderHtml for Arc<str> {\n    type AsyncOutput = Self;\n    type Owned = Self;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        <&str as RenderHtml>::to_html_with_buf(\n            &self,\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let this: &str = self.as_ref();\n        let StrState { node, .. } =\n            this.hydrate::<FROM_SERVER>(cursor, position);\n        ArcStrState { node, str: self }\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self\n    }\n}\n\nimpl ToTemplate for Arc<str> {\n    const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;\n\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        <&str as ToTemplate>::to_template(\n            buf, class, style, inner_html, position,\n        )\n    }\n}\n\nimpl Mountable for ArcStrState {\n    fn unmount(&mut self) {\n        self.node.unmount()\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        Rndr::insert_node(parent, self.node.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.node.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\n/// Retained view state for `Cow<'_, str>`.\npub struct CowStrState<'a> {\n    node: crate::renderer::types::Text,\n    str: Cow<'a, str>,\n}\n\nimpl<'a> Render for Cow<'a, str> {\n    type State = CowStrState<'a>;\n\n    fn build(self) -> Self::State {\n        let node = Rndr::create_text_node(&self);\n        CowStrState { node, str: self }\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        let CowStrState { node, str } = state;\n        if self != *str {\n            Rndr::set_text(node, &self);\n            *str = self;\n        }\n    }\n}\n\nimpl RenderHtml for Cow<'_, str> {\n    type AsyncOutput = Self;\n    type Owned = String;\n\n    const MIN_LENGTH: usize = 0;\n\n    fn dry_resolve(&mut self) {}\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self\n    }\n\n    fn html_len(&self) -> usize {\n        self.len()\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        <&str as RenderHtml>::to_html_with_buf(\n            &self,\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let this: &str = self.as_ref();\n        let StrState { node, .. } =\n            this.hydrate::<FROM_SERVER>(cursor, position);\n        CowStrState { node, str: self }\n    }\n\n    fn into_owned(self) -> <Self as RenderHtml>::Owned {\n        self.into_owned()\n    }\n}\n\nimpl ToTemplate for Cow<'_, str> {\n    const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;\n\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        <&str as ToTemplate>::to_template(\n            buf, class, style, inner_html, position,\n        )\n    }\n}\n\nimpl Mountable for CowStrState<'_> {\n    fn unmount(&mut self) {\n        self.node.unmount()\n    }\n\n    fn mount(\n        &mut self,\n        parent: &crate::renderer::types::Element,\n        marker: Option<&crate::renderer::types::Node>,\n    ) {\n        Rndr::insert_node(parent, self.node.as_ref(), marker);\n    }\n\n    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {\n        self.node.insert_before_this(child)\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n"
  },
  {
    "path": "tachys/src/view/template.rs",
    "content": "use super::{\n    add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,\n    RenderHtml, ToTemplate,\n};\nuse crate::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    renderer::Rndr,\n};\n\n/// A view wrapper that uses a `<template>` node to optimize DOM node creation.\n///\n/// Rather than creating all of the DOM nodes each time it is built, this template will create a\n/// single `<template>` node once, then use `.cloneNode(true)` to clone that entire tree, and\n/// hydrate it to add event listeners and interactivity for this instance.\npub struct ViewTemplate<V> {\n    view: V,\n}\n\nimpl<V> ViewTemplate<V>\nwhere\n    V: Render + ToTemplate + 'static,\n{\n    /// Creates a new view template.\n    pub fn new(view: V) -> Self {\n        Self { view }\n    }\n\n    fn to_template() -> crate::renderer::types::TemplateElement {\n        Rndr::get_template::<V>()\n    }\n}\n\nimpl<V> Render for ViewTemplate<V>\nwhere\n    V: Render + RenderHtml + ToTemplate + 'static,\n    V::State: Mountable,\n{\n    type State = V::State;\n\n    // TODO try_build/try_rebuild()\n\n    fn build(self) -> Self::State {\n        let tpl = Self::to_template();\n        let contents = Rndr::clone_template(&tpl);\n        self.view\n            .hydrate::<false>(&Cursor::new(contents), &Default::default())\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.view.rebuild(state)\n    }\n}\n\nimpl<V> AddAnyAttr for ViewTemplate<V>\nwhere\n    V: RenderHtml + ToTemplate + 'static,\n    V::State: Mountable,\n{\n    type Output<SomeNewAttr: Attribute> = ViewTemplate<V>;\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr> {\n        panic!(\"AddAnyAttr not supported on ViewTemplate\");\n    }\n}\n\nimpl<V> RenderHtml for ViewTemplate<V>\nwhere\n    V: RenderHtml + ToTemplate + 'static,\n    V::State: Mountable,\n{\n    type AsyncOutput = V::AsyncOutput;\n    type Owned = V::Owned;\n\n    const MIN_LENGTH: usize = V::MIN_LENGTH;\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        self.view.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        )\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        self.view.hydrate::<FROM_SERVER>(cursor, position)\n    }\n\n    fn dry_resolve(&mut self) {\n        self.view.dry_resolve();\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        self.view.resolve().await\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        self.view.into_owned()\n    }\n}\n\nimpl<V> ToTemplate for ViewTemplate<V>\nwhere\n    V: RenderHtml + ToTemplate + 'static,\n    V::State: Mountable,\n{\n    const TEMPLATE: &'static str = V::TEMPLATE;\n\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        V::to_template(buf, class, style, inner_html, position);\n    }\n}\n"
  },
  {
    "path": "tachys/src/view/tuples.rs",
    "content": "use super::{\n    Mountable, Position, PositionState, Render, RenderHtml, ToTemplate,\n};\nuse crate::{\n    html::attribute::{any_attribute::AnyAttribute, Attribute},\n    hydration::Cursor,\n    renderer::Rndr,\n    view::{add_attr::AddAnyAttr, StreamBuilder},\n};\nuse const_str_slice_concat::{\n    const_concat, const_concat_with_separator, str_from_buffer,\n};\n\nimpl Render for () {\n    type State = crate::renderer::types::Placeholder;\n\n    fn build(self) -> Self::State {\n        Rndr::create_placeholder()\n    }\n\n    fn rebuild(self, _state: &mut Self::State) {}\n}\n\nimpl RenderHtml for () {\n    type AsyncOutput = ();\n    type Owned = ();\n\n    const MIN_LENGTH: usize = 3;\n    const EXISTS: bool = false;\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        _mark_branches: bool,\n        _extra_attrs: Vec<AnyAttribute>,\n    ) {\n        if escape {\n            buf.push_str(\"<!>\");\n            *position = Position::NextChild;\n        }\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        let marker = cursor.next_placeholder(position);\n        position.set(Position::NextChild);\n        marker\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {}\n\n    fn dry_resolve(&mut self) {}\n\n    fn into_owned(self) -> Self::Owned {}\n}\n\nimpl AddAnyAttr for () {\n    type Output<SomeNewAttr: Attribute> = ();\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        _attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n    }\n}\n\nimpl Mountable for () {\n    fn unmount(&mut self) {}\n\n    fn mount(\n        &mut self,\n        _parent: &crate::renderer::types::Element,\n        _marker: Option<&crate::renderer::types::Node>,\n    ) {\n    }\n\n    fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {\n        false\n    }\n\n    fn elements(&self) -> Vec<crate::renderer::types::Element> {\n        vec![]\n    }\n}\n\nimpl ToTemplate for () {\n    const TEMPLATE: &'static str = \"<!>\";\n\n    fn to_template(\n        buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n        buf.push_str(\"<!>\");\n    }\n\n    fn to_template_attribute(\n        _buf: &mut String,\n        _class: &mut String,\n        _style: &mut String,\n        _inner_html: &mut String,\n        _position: &mut Position,\n    ) {\n    }\n}\n\nimpl<A: Render> Render for (A,) {\n    type State = A::State;\n\n    fn build(self) -> Self::State {\n        self.0.build()\n    }\n\n    fn rebuild(self, state: &mut Self::State) {\n        self.0.rebuild(state)\n    }\n}\n\nimpl<A> RenderHtml for (A,)\nwhere\n    A: RenderHtml,\n{\n    type AsyncOutput = (A::AsyncOutput,);\n    type Owned = (A::Owned,);\n\n    const MIN_LENGTH: usize = A::MIN_LENGTH;\n    const EXISTS: bool = A::EXISTS;\n\n    fn html_len(&self) -> usize {\n        self.0.html_len()\n    }\n\n    fn to_html_with_buf(\n        self,\n        buf: &mut String,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) {\n        self.0.to_html_with_buf(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n        self,\n        buf: &mut StreamBuilder,\n        position: &mut Position,\n        escape: bool,\n        mark_branches: bool,\n        extra_attrs: Vec<AnyAttribute>,\n    ) where\n        Self: Sized,\n    {\n        self.0.to_html_async_with_buf::<OUT_OF_ORDER>(\n            buf,\n            position,\n            escape,\n            mark_branches,\n            extra_attrs,\n        );\n    }\n\n    fn hydrate<const FROM_SERVER: bool>(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        self.0.hydrate::<FROM_SERVER>(cursor, position)\n    }\n\n    async fn hydrate_async(\n        self,\n        cursor: &Cursor,\n        position: &PositionState,\n    ) -> Self::State {\n        self.0.hydrate_async(cursor, position).await\n    }\n\n    async fn resolve(self) -> Self::AsyncOutput {\n        (self.0.resolve().await,)\n    }\n\n    fn dry_resolve(&mut self) {\n        self.0.dry_resolve();\n    }\n\n    fn into_owned(self) -> Self::Owned {\n        (self.0.into_owned(),)\n    }\n}\n\nimpl<A: ToTemplate> ToTemplate for (A,) {\n    const TEMPLATE: &'static str = A::TEMPLATE;\n    const CLASS: &'static str = A::CLASS;\n    const STYLE: &'static str = A::STYLE;\n\n    fn to_template(\n        buf: &mut String,\n        class: &mut String,\n        style: &mut String,\n        inner_html: &mut String,\n        position: &mut Position,\n    ) {\n        A::to_template(buf, class, style, inner_html, position)\n    }\n}\n\nimpl<A> AddAnyAttr for (A,)\nwhere\n    A: AddAnyAttr,\n{\n    type Output<SomeNewAttr: Attribute> = (A::Output<SomeNewAttr>,);\n\n    fn add_any_attr<NewAttr: Attribute>(\n        self,\n        attr: NewAttr,\n    ) -> Self::Output<NewAttr>\n    where\n        Self::Output<NewAttr>: RenderHtml,\n    {\n        (self.0.add_any_attr(attr),)\n    }\n}\n\nmacro_rules! impl_view_for_tuples {\n\t($first:ident, $($ty:ident),* $(,)?) => {\n\t\timpl<$first, $($ty),*> Render for ($first, $($ty,)*)\n\t\twhere\n\t\t\t$first: Render,\n\t\t\t$($ty: Render),*,\n\n\t\t{\n\t\t\ttype State = ($first::State, $($ty::State,)*);\n\n\t\t\tfn build(self) -> Self::State {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                (\n                    $first.build(),\n                    $($ty.build()),*\n                )\n\t\t\t}\n\n\t\t\tfn rebuild(self, state: &mut Self::State) {\n\t\t\t\tpaste::paste! {\n\t\t\t\t\tlet ([<$first:lower>], $([<$ty:lower>],)*) = self;\n\t\t\t\t\tlet ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;\n\t\t\t\t\t[<$first:lower>].rebuild([<view_ $first:lower>]);\n\t\t\t\t\t$([<$ty:lower>].rebuild([<view_ $ty:lower>]));*\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\timpl<$first, $($ty),*> RenderHtml for ($first, $($ty,)*)\n\t\twhere\n\t\t\t$first: RenderHtml,\n\t\t\t$($ty: RenderHtml),*,\n\n\t\t{\n            type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);\n            type Owned = ($first::Owned, $($ty::Owned,)*);\n            const EXISTS: bool = $first::EXISTS || $($ty::EXISTS || )* false;\n            const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;\n\n            #[inline(always)]\n            fn html_len(&self) -> usize {\n                #[allow(non_snake_case)]\n\t\t\t    let ($first, $($ty,)* ) = self;\n                $($ty.html_len() +)* $first.html_len()\n            }\n\n\t\t\tfn to_html_with_buf(\n                self,\n                buf: &mut String,\n                position: &mut Position,\n                escape: bool,\n                mark_branches: bool,\n                extra_attrs: Vec<AnyAttribute>\n            ) {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)* ) = self;\n                $first.to_html_with_buf(buf, position, escape, mark_branches, extra_attrs.clone());\n                $($ty.to_html_with_buf(buf, position, escape, mark_branches, extra_attrs.clone()));*\n\t\t\t}\n\n\t\t\tfn to_html_async_with_buf<const OUT_OF_ORDER: bool>(\n\t\t\t\tself,\n\t\t\t\tbuf: &mut StreamBuilder,\n                position: &mut Position,\n                escape: bool,\n                mark_branches: bool,\n                extra_attrs: Vec<AnyAttribute>\n            ) where\n\t\t\t\tSelf: Sized,\n\t\t\t{\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)* ) = self;\n                $first.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape, mark_branches, extra_attrs.clone());\n                $($ty.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape, mark_branches, extra_attrs.clone()));*\n\t\t\t}\n\n\t\t\tfn hydrate<const FROM_SERVER: bool>(self, cursor: &Cursor, position: &PositionState) -> Self::State {\n                #[allow(non_snake_case)]\n\t\t\t\t\tlet ($first, $($ty,)* ) = self;\n\t\t\t\t\t(\n\t\t\t\t\t\t$first.hydrate::<FROM_SERVER>(cursor, position),\n\t\t\t\t\t\t$($ty.hydrate::<FROM_SERVER>(cursor, position)),*\n\t\t\t\t\t)\n\t\t\t}\n\n            async fn hydrate_async(self, cursor: &Cursor, position: &PositionState) -> Self::State {\n                #[allow(non_snake_case)]\n\t\t\t\t\tlet ($first, $($ty,)* ) = self;\n\t\t\t\t\t(\n\t\t\t\t\t\t$first.hydrate_async(cursor, position).await,\n\t\t\t\t\t\t$($ty.hydrate_async(cursor, position).await),*\n\t\t\t\t\t)\n\t\t\t}\n\n            async fn resolve(self) -> Self::AsyncOutput {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                futures::join!(\n                    $first.resolve(),\n                    $($ty.resolve()),*\n                )\n            }\n\n            fn dry_resolve(&mut self) {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                $first.dry_resolve();\n                $($ty.dry_resolve());*\n            }\n\n            fn into_owned(self) -> Self::Owned {\n                #[allow(non_snake_case)]\n                let ($first, $($ty,)*) = self;\n                (\n                    $first.into_owned(),\n                    $($ty.into_owned()),*\n                )\n            }\n\t\t}\n\n\t\timpl<$first, $($ty),*> ToTemplate for ($first, $($ty,)*)\n\t\twhere\n\t\t\t$first: ToTemplate,\n\t\t\t$($ty: ToTemplate),*\n\t\t{\n\t\t\tconst TEMPLATE: &'static str = str_from_buffer(&const_concat(&[\n\t\t\t\t$first::TEMPLATE, $($ty::TEMPLATE),*\n\t\t\t]));\n\t\t\tconst CLASS: &'static str = str_from_buffer(&const_concat_with_separator(&[\n\t\t\t\t$first::CLASS, $($ty::CLASS),*\n\t\t\t], \" \"));\n\t\t\tconst STYLE: &'static str = str_from_buffer(&const_concat_with_separator(&[\n\t\t\t\t$first::STYLE, $($ty::STYLE),*\n\t\t\t], \";\"));\n\n\t\t\tfn to_template(buf: &mut String, class: &mut String, style: &mut String, inner_html: &mut String, position: &mut Position)  {\n                $first ::to_template(buf, class, style, inner_html, position);\n                $($ty::to_template(buf, class, style, inner_html, position));*;\n\t\t\t}\n\t\t}\n\n\t\timpl<$first, $($ty),*> Mountable for ($first, $($ty,)*) where\n\t\t\t$first: Mountable,\n\t\t\t$($ty: Mountable),*,\n\n\t\t{\n\t\t\tfn unmount(&mut self) {\n                #[allow(non_snake_case)] // better macro performance\n                let ($first, $($ty,)*) = self;\n                $first.unmount();\n                $($ty.unmount());*\n\t\t\t}\n\n\t\t\tfn mount(\n\t\t\t\t&mut self,\n\t\t\t\tparent: &crate::renderer::types::Element,\n\t\t\t\tmarker: Option<&crate::renderer::types::Node>,\n\t\t\t) {\n                #[allow(non_snake_case)] // better macro performance\n                let ($first, $($ty,)*) = self;\n                $first.mount(parent, marker);\n                $($ty.mount(parent, marker));*\n\t\t\t}\n\n\t\t\tfn insert_before_this(&self,\n\t\t\t\tchild: &mut dyn Mountable,\n\t\t\t) -> bool {\n                #[allow(non_snake_case)] // better macro performance\n                let ($first, $($ty,)*) = self;\n                $first.insert_before_this(child)\n                $(|| $ty.insert_before_this(child))*\n\t\t\t}\n\n            fn elements(&self) -> Vec<crate::renderer::types::Element> {\n                #[allow(non_snake_case)] // better macro performance\n                let ($first, $($ty,)*) = self;\n                $first.elements().into_iter()\n                $(.chain($ty.elements()))*\n                    .collect()\n            }\n\t\t}\n\n        impl<$first, $($ty,)*> AddAnyAttr for ($first, $($ty,)*)\n        where\n\t\t\t$first: AddAnyAttr,\n\t\t\t$($ty: AddAnyAttr),*,\n\n        {\n            type Output<SomeNewAttr: Attribute> = ($first::Output<SomeNewAttr::Cloneable>, $($ty::Output<SomeNewAttr::Cloneable>,)*);\n\n            fn add_any_attr<NewAttr: Attribute>(\n                self,\n                attr: NewAttr,\n            ) -> Self::Output<NewAttr>\n            where\n                Self::Output<NewAttr>: RenderHtml,\n            {\n                let shared = attr.into_cloneable();\n                #[allow(non_snake_case)] // better macro performance\n                let ($first, $($ty,)*) = self;\n                ($first.add_any_attr(shared.clone()), $($ty.add_any_attr(shared.clone()),)*)\n            }\n        }\n    };\n}\n\nimpl_view_for_tuples!(A, B);\nimpl_view_for_tuples!(A, B, C);\nimpl_view_for_tuples!(A, B, C, D);\nimpl_view_for_tuples!(A, B, C, D, E);\nimpl_view_for_tuples!(A, B, C, D, E, F);\nimpl_view_for_tuples!(A, B, C, D, E, F, G);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);\nimpl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);\nimpl_view_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T\n);\nimpl_view_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U\n);\nimpl_view_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V\n);\nimpl_view_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W\n);\nimpl_view_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X\n);\nimpl_view_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y\n);\nimpl_view_for_tuples!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,\n    Z\n);\n"
  }
]