[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [upsuper]\n"
  },
  {
    "path": ".github/workflows/check.yml",
    "content": "on: [push, pull_request]\nname: Check\n\njobs:\n  format:\n    name: Format\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v2\n\n      - name: Install stable toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: stable\n          override: true\n          components: rustfmt\n\n      - name: Run cargo fmt\n        uses: actions-rs/cargo@v1\n        with:\n          command: fmt\n          args: --all -- --check\n\n  clippy:\n    name: Clippy\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v2\n\n      - name: Install stable toolchain\n        id: toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: stable\n          override: true\n          components: clippy\n\n      - name: Cache Cargo\n        uses: actions/cache@v2\n        with:\n          path: |\n            ~/.cargo/registry\n            ~/.cargo/git\n          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n          restore-keys: |\n            ${{ runner.os }}-cargo-\n\n      - name: Cache target\n        uses: actions/cache@v2\n        with:\n          path: target\n          key: ${{ runner.os }}-clippy-${{ steps.toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('**/*.rs') }}\n          restore-keys: |\n            ${{ runner.os }}-clippy-${{ steps.toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.lock') }}-\n            ${{ runner.os }}-clippy-${{ steps.toolchain.outputs.rustc_hash }}-\n\n      - name: Run cargo clippy\n        uses: actions-rs/cargo@v1\n        with:\n          command: clippy\n          args: -- -D warnings\n"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "content": "on:\n  push:\n    branches:\n      - master\n\nname: Deploy\n\njobs:\n  deploy:\n    name: Deploy\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v2\n\n      - name: Install stable toolchain\n        id: toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: stable\n          override: true\n\n      - name: Cache Cargo\n        uses: actions/cache@v2\n        with:\n          path: |\n            ~/.cargo/registry\n            ~/.cargo/git\n          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n          restore-keys: |\n            ${{ runner.os }}-cargo-\n\n      - name: Cache target\n        uses: actions/cache@v2\n        with:\n          path: target\n          key: ${{ runner.os }}-build-${{ steps.toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('**/*.rs') }}\n          restore-keys: |\n            ${{ runner.os }}-build-${{ steps.toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.lock') }}-\n            ${{ runner.os }}-build-${{ steps.toolchain.outputs.rustc_hash }}-\n\n      - name: Build pages\n        shell: bash\n        run: ./build_pages.sh\n\n      - name: Deploy\n        uses: peaceiris/actions-gh-pages@v3\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_dir: ./out\n"
  },
  {
    "path": ".gitignore",
    "content": "/.idea\n/out\n/target\n**/*.rs.bk\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"cheatsheet-gen\"\nversion = \"0.1.0\"\nauthors = [\"Xidorn Quan <me@upsuper.org>\"]\nedition = \"2018\"\npublish = false\n\n[dependencies]\nbitflags = \"1.0.4\"\ncombine = \"4.0.1\"\neither_n = \"0.2.0\"\nlazy_static = \"1.3.0\"\nserde_yaml = \"0.8.9\"\nv_htmlescape = \"0.13.1\"\n\n[dependencies.serde]\nversion = \"1.0.90\"\nfeatures = [\"derive\"]\n\n[dev-dependencies]\npretty_assertions = \"1\"\n"
  },
  {
    "path": "LICENSE",
    "content": "Permission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "build_pages.sh",
    "content": "#!/usr/bin/env bash\n\nset -xe\n\nmkdir out\ncp static/* out\nfor f in data/*.yml; do\n  cargo run -- \"${f}\" \"out/$(basename \"${f}\" .yml).html\"\ndone\n"
  },
  {
    "path": "data/chrono.yml",
    "content": "title: Chrono\n\nbase:\n  chrono: \"https://docs.rs/chrono/0.4.6/chrono/\"\n  time: \"https://docs.rs/time/0.1.42/time/\"\n\nmain:\n  # DateTime & NaiveDateTime\n  - - type: \"DateTime<Tz>\"\n      constraints: \"where Tz: TimeZone\"\n      impls:\n        - \"Datelike\"\n        - \"Timelike\"\n        - \"Sub<Self> => Output = Duration\"\n        - \"Add<Duration> => Output = Self\"\n        - \"Sub<Duration> => Output = Self\"\n        - \"Add<FixedOffset> => Output = Self\"\n        - \"Sub<FixedOffset> => Output = Self\"\n      groups:\n        - items:\n            - \"::from_utc (NaiveDateTime, Tz::Offset) -> Self\"\n        - name: \"Checked calculation\"\n          items:\n            - \"checked_add_signed (Duration) -> Option<Self>\"\n            - \"checked_sub_signed (Duration) -> Option<Self>\"\n            - \"signed_duration_since (DateTime<Tz2>) -> Duration\"\n    - type: \"DateTime<FixedOffset>\"\n      groups:\n        - name: \"Parse\"\n          items:\n            - \"::parse_from_rfc2822 (&str) -> ParseResult<Self>\"\n            - \"::parse_from_rfc3339 (&str) -> ParseResult<Self>\"\n            - \"::parse_from_str (&str, &str) -> ParseResult<Self>\"\n    - type: \"&DateTime<Tz>\"\n      constraints: \"where Tz: TimeZone\"\n      groups:\n        - name: \"Data & time\"\n          items:\n            - \"date () -> Date<Tz>\"\n            - \"time () -> NaiveTime\"\n        - name: \"Timestamp\"\n          items:\n            - \"timestamp        () -> i64\"\n            - \"timestamp_millis () -> i64\"\n            - \"timestamp_nanos  () -> i64\"\n        - name: \"Timestamp sub-second part\"\n          items:\n            - \"timestamp_subsec_millis () -> u32\"\n            - \"timestamp_subsec_micros () -> u32\"\n            - \"timestamp_subsec_nanos  () -> u32\"\n        - name: \"Timezone\"\n          items:\n            - \"offset () -> &Tz::Offset\"\n            - \"timezone () -> Tz\"\n            - \"with_timezone (&Tz2) -> DateTime<Tz2>\"\n        - name: \"To NaiveDateTime\"\n          items:\n            - \"naive_utc () -> NaiveDateTime\"\n            - \"naive_local () -> NaiveDateTime\"\n    - type: \"&DateTime<Tz>\"\n      constraints: |-\n        where Tz: TimeZone,\n              Tz::Offset: Display\n      groups:\n        - items:\n            - \"format (&str) -> Display\"\n            - \"to_rfc2822 () -> String\"\n            - \"to_rfc3339 () -> String\"\n            - \"to_rfc3339_opts (SecondsFormat, bool) -> String\"\n    - type: \"NaiveDateTime\"\n      impls:\n        - \"Datelike\"\n        - \"Timelike\"\n        - \"AddAssign<Duration>\"\n        - \"SubAssign<Duration>\"\n        - \"Add<Duration> => Output = Self\"\n        - \"Sub<Duration> => Output = Self\"\n        - \"Add<FixedOffset> => Output = Self\"\n        - \"Sub<FixedOffset> => Output = Self\"\n      groups:\n        - name: \"Construct\"\n          items:\n            - \"::new    (NaiveDate, NaiveTime) -> Self\"\n            - \"::from_timestamp     (i64, u32) -> Self\"\n            - \"::from_timestamp_opt (i64, u32) -> Self\"\n            - \"::parse_from_str (&str, &str) -> ParseResult<Self>\"\n        - name: \"Checked calculation\"\n          items:\n            - \"checked_add_signed (Duration) -> Option<Self>\"\n            - \"checked_sub_signed (Duration) -> Option<Self>\"\n            - \"signed_duration_since (NaiveDateTime) -> Duration\"\n    - type: \"&NaiveDateTime\"\n      groups:\n        - name: \"Date & time\"\n          items:\n            - \"date () -> NaiveDate\"\n            - \"time () -> NaiveTime\"\n        - name: \"Timestamp\"\n          items:\n            - \"timestamp        () -> i64\"\n            - \"timestamp_millis () -> i64\"\n            - \"timestamp_nanos  () -> i64\"\n        - name: \"Timestamp sub-second part\"\n          items:\n            - \"timestamp_subsec_millis () -> u32\"\n            - \"timestamp_subsec_micros () -> u32\"\n            - \"timestamp_subsec_nanos  () -> u32\"\n        - name: \"Format\"\n          items:\n            - \"format (&str) -> Display\"\n  # Date & NaiveDate\n  - - type: \"Date<Tz>\"\n      constraints: \"where Tz: TimeZone\"\n      impls:\n        - \"Datelike\"\n        - \"Sub<Self> => Output = Duration\"\n        - \"Add<Duration> => Output = Self\"\n        - \"Sub<Duration> => Output = Self\"\n      groups:\n        - items:\n            - \"::from_utc (NaiveDate, Tz::Offset) -> Self\"\n        - name: \"Checked calculation\"\n          items:\n            - \"checked_add_signed (Duration) -> Option<Self>\"\n            - \"checked_sub_signed (Duration) -> Option<Self>\"\n            - \"signed_duration_since (Date<Tz2>) -> Duration\"\n    - type: \"&Date<Tz>\"\n      constraints: \"where Tz: TimeZone\"\n      groups:\n        - name: \"To DateTime (panic when invalid)\"\n          items:\n            - \"and_hms       (u32, u32, u32)      -> DateTime<Tz>\"\n            - \"and_hms_milli (u32, u32, u32, u32) -> DateTime<Tz>\"\n            - \"and_hms_micro (u32, u32, u32, u32) -> DateTime<Tz>\"\n            - \"and_hms_nano  (u32, u32, u32, u32) -> DateTime<Tz>\"\n        - name: \"To DateTime (None when invalid)\"\n          items:\n            - \"and_time          (NaiveTime)          -> Option<DateTime<Tz>>\"\n            - \"and_hms_opt       (u32, u32, u32)      -> Option<DateTime<Tz>>\"\n            - \"and_hms_milli_opt (u32, u32, u32, u32) -> Option<DateTime<Tz>>\"\n            - \"and_hms_micro_opt (u32, u32, u32, u32) -> Option<DateTime<Tz>>\"\n            - \"and_hms_nano_opt  (u32, u32, u32, u32) -> Option<DateTime<Tz>>\"\n        - name: \"Next / previous date\"\n          items:\n            - \"succ () -> Self\"\n            - \"pred () -> Self\"\n            - \"succ_opt () -> Option<Self>\"\n            - \"pred_opt () -> Option<Self>\"\n        - name: \"Timezone\"\n          items:\n            - \"offset () -> &Tz::Offset\"\n            - \"timezone () -> Tz\"\n            - \"with_timezone (&Tz2) -> Date<Tz2>\"\n        - name: \"To NaiveDate\"\n          items:\n            - \"naive_utc   () -> NaiveDate\"\n            - \"naive_local () -> NaiveDate\"\n        - name: \"Format\"\n          items:\n            - \"format (&str) -> Display where Tz::Offset: Display\"\n    - type: \"NaiveDate\"\n      impls:\n        - \"Datelike\"\n        - \"AddAssign<Duration>\"\n        - \"SubAssign<Duration>\"\n        - \"Sub<Self> => Output = Duration\"\n        - \"Add<Duration> => Output = Self\"\n        - \"Sub<Duration> => Output = Self\"\n      groups:\n        - name: \"Construct (panic when invalid)\"\n          items:\n            - \"::from_ymd    (i32, u32, u32)     -> Self\"\n            - \"::from_yo     (i32, u32)          -> Self\"\n            - \"::from_isoywd (i32, u32, Weekday) -> Self\"\n            - \"::from_num_days_from_ce (i32)     -> Self\"\n        - name: \"Construct (None when invalid)\"\n          items:\n            - \"::from_ymd_opt    (i32, u32, u32)     -> Option<Self>\"\n            - \"::from_yo_opt     (i32, u32)          -> Option<Self>\"\n            - \"::from_isoywd_opt (i32, u32, Weekday) -> Option<Self>\"\n            - \"::from_num_days_from_ce_opt (i32)     -> Option<Self>\"\n        - name: \"Parse\"\n          items:\n            - \"::parse_from_str (&str, &str) -> ParseResult<Self>\"\n        - name: \"Checked calculation\"\n          items:\n            - \"checked_add_signed (Duration) -> Option<Self>\"\n            - \"checked_sub_signed (Duration) -> Option<Self>\"\n            - \"signed_duration_since (Self) -> Duration\"\n    - type: \"&NaiveDate\"\n      groups:\n        - name: \"To NaiveDateTime\"\n          items:\n            - \"and_time (NaiveTime) -> NaiveDateTime\"\n        - name: \"To NaiveDateTime (panic when invalid)\"\n          items:\n            - \"and_hms       (u32, u32, u32)      -> NaiveDateTime\"\n            - \"and_hms_milli (u32, u32, u32, u32) -> NaiveDateTime\"\n            - \"and_hms_micro (u32, u32, u32, u32) -> NaiveDateTime\"\n            - \"and_hms_nano  (u32, u32, u32, u32) -> NaiveDateTime\"\n        - name: \"To NaiveDateTime (Option when invalid)\"\n          items:\n            - \"and_hms_opt       (u32, u32, u32)      -> Option<NaiveDateTime>\"\n            - \"and_hms_milli_opt (u32, u32, u32, u32) -> Option<NaiveDateTime>\"\n            - \"and_hms_micro_opt (u32, u32, u32, u32) -> Option<NaiveDateTime>\"\n            - \"and_hms_nano_opt  (u32, u32, u32, u32) -> Option<NaiveDateTime>\"\n        - name: \"Next / previous date\"\n          items:\n            - \"succ () -> Self\"\n            - \"pred () -> Self\"\n            - \"succ_opt () -> Option<Self>\"\n            - \"pred_opt () -> Option<Self>\"\n        - name: \"Format\"\n          items:\n            - \"format (&str) -> Display\"\n  # NaiveTime & Duration\n  - - type: \"NaiveTime\"\n      impls:\n        - \"Timelike\"\n        - \"AddAssign<Duration>\"\n        - \"SubAssign<Duration>\"\n        - \"Sub<Self> => Output = Duration\"\n        - \"Add<Duration> => Output = Self\"\n        - \"Sub<Duration> => Output = Self\"\n        - \"Add<FixedOffset> => Output = Self\"\n        - \"Sub<FixedOffset> => Output = Self\"\n      groups:\n        - name: \"Construct (panic when invalid)\"\n          items:\n            - \"::from_hms       (u32, u32, u32)      -> Self\"\n            - \"::from_hms_milli (u32, u32, u32, u32) -> Self\"\n            - \"::from_hms_micro (u32, u32, u32, u32) -> Self\"\n            - \"::from_hms_nano  (u32, u32, u32, u32) -> Self\"\n            - \"::from_num_seconds_from_midnight (u32, u32) -> Self\"\n        - name: \"Construct (None when invalid)\"\n          items:\n            - \"::from_hms_opt       (u32, u32, u32)      -> Option<Self>\"\n            - \"::from_hms_milli_opt (u32, u32, u32, u32) -> Option<Self>\"\n            - \"::from_hms_micro_opt (u32, u32, u32, u32) -> Option<Self>\"\n            - \"::from_hms_nano_opt  (u32, u32, u32, u32) -> Option<Self>\"\n            - \"::from_num_seconds_from_midnight_opt (u32, u32) -> Option<Self>\"\n        - name: \"Parse\"\n          items:\n            - \"::parse_from_str (&str, &str) -> ParseResult<Self>\"\n        - name: \"Calculation\"\n          items:\n            - \"signed_duration_since (Self) -> Duration\"\n    - type: \"&NaiveTime\"\n      groups:\n        - name: \"Overflowing calculation\"\n          items:\n            - \"overflowing_add_signed (Duration) -> (Self, i64)\"\n            - \"overflowing_sub_signed (Duration) -> (Self, i64)\"\n        - name: \"Format\"\n          items:\n            - \"format (&str) -> Display\"\n    - type: \"Duration\"\n      impls:\n        - \"Neg       => Output = Self\"\n        - \"Mul<i32>  => Output = Self\"\n        - \"Div<i32>  => Output = Self\"\n        - \"Add<Self> => Output = Self\"\n        - \"Sub<Self> => Output = Self\"\n      groups:\n        - name: \"Construct from numbers\"\n          items:\n            - \"::weeks   (i64) -> Self\"\n            - \"::days    (i64) -> Self\"\n            - \"::hours   (i64) -> Self\"\n            - \"::minutes (i64) -> Self\"\n            - \"::seconds (i64) -> Self\"\n            - \"::milliseconds (i64) -> Self\"\n            - \"::microseconds (i64) -> Self\"\n            - \"::nanoseconds  (i64) -> Self\"\n        - name: \"Measure duration\"\n          items:\n            - \"::span (() -> ()) -> Self\"\n        - name: \"Special values\"\n          items:\n            - \"::zero () -> Self\"\n            - \"::min_value () -> Self\"\n            - \"::max_value () -> Self\"\n        - name: \"From Duration in std\"\n          items:\n            - \"::from_std (StdDuration) -> Result<Self, OutOfRangeError>\"\n    - type: \"&Duration\"\n      groups:\n        - name: \"Numbers\"\n          items:\n            - \"num_weeks   () -> i64\"\n            - \"num_days    () -> i64\"\n            - \"num_hours   () -> i64\"\n            - \"num_minutes () -> i64\"\n            - \"num_seconds () -> i64\"\n            - \"num_milliseconds () -> i64\"\n            - \"num_microseconds () -> Option<i64>\"\n            - \"num_nanoseconds  () -> Option<i64>\"\n        - name: \"Checked calculation\"\n          items:\n            - \"checked_add (&Self) -> Option<Self>\"\n            - \"checked_sub (&Self) -> Option<Self>\"\n        - items:\n            - \"is_zero () -> bool\"\n            - \"to_std () -> Result<StdDuration, OutOfRangeError>\"\n  # Datelike & Timelike\n  - - type: \"&Datelike\"\n      groups:\n        - name: \"Date numbers\"\n          items:\n            - \"year     () -> i32\"\n            - \"month    () -> u32\"\n            - \"day      () -> u32\"\n            - \"ordinal  () -> u32\"\n        - name: \"Date numbers (zero-based)\"\n          items:\n            - \"month0   () -> u32\"\n            - \"day0     () -> u32\"\n            - \"ordinal0 () -> u32\"\n        - name: \"Week\"\n          items:\n            - \"weekday  () -> Weekday\"\n            - \"iso_week () -> IsoWeek\"\n        - name: \"Construct with different date number\"\n          items:\n            - \"with_year    (i32) -> Option<Self>\"\n            - \"with_month   (u32) -> Option<Self>\"\n            - \"with_day     (u32) -> Option<Self>\"\n            - \"with_ordinal (u32) -> Option<Self>\"\n        - name: \"Construct with different date number (zero-based)\"\n          items:\n            - \"with_month0   (u32) -> Option<Self>\"\n            - \"with_day0     (u32) -> Option<Self>\"\n            - \"with_ordinal0 (u32) -> Option<Self>\"\n        - name: \"Common era\"\n          items:\n            - \"year_ce () -> (bool, u32)\"\n            - \"num_days_from_ce () -> i32\"\n    - type: \"&Timelike\"\n      groups:\n        - name: \"Time numbers\"\n          items:\n            - \"hour       () -> u32\"\n            - \"minute     () -> u32\"\n            - \"second     () -> u32\"\n            - \"nanosecond () -> u32\"\n        - name: \"Construct with different numbers\"\n          items:\n            - \"with_hour       (u32) -> Option<Self>\"\n            - \"with_minute     (u32) -> Option<Self>\"\n            - \"with_second     (u32) -> Option<Self>\"\n            - \"with_nanosecond (u32) -> Option<Self>\"\n        - items:\n            - \"hour12 () -> (bool, u32)\"\n            - \"num_seconds_from_midnight () -> u32\"\n\ntrait_impls:\n  - pat: \"TimeZone\"\n    impls:\n      - \"Utc\"\n      - \"Local\"\n      - \"FixedOffset\"\n\nreferences:\n  - kind: enum\n    names:\n      - \"chrono::Weekday\"\n      - \"chrono::SecondsFormat\"\n      - \"chrono::format::Item\"\n      - \"std::option::Option\"\n      - \"std::result::Result\"\n  - kind: struct\n    names:\n      - \"chrono::Date\"\n      - \"chrono::DateTime\"\n      - \"chrono::Duration\"\n      - \"chrono::naive::IsoWeek\"\n      - \"chrono::naive::NaiveDate\"\n      - \"chrono::naive::NaiveDateTime\"\n      - \"chrono::naive::NaiveTime\"\n      - \"chrono::offset::FixedOffset\"\n      - \"chrono::offset::Local\"\n      - \"chrono::offset::Utc\"\n      - \"std::string::String\"\n      - \"time::OutOfRangeError\"\n    aliases:\n      StdDuration: \"std::time::Duration\"\n  - kind: trait\n    names:\n      - \"chrono::Datelike\"\n      - \"chrono::Timelike\"\n      - \"chrono::offset::TimeZone\"\n      - \"std::clone::Clone\"\n      - \"std::fmt::Display\"\n      - \"std::iter::Iterator\"\n      - \"std::ops::Add\"\n      - \"std::ops::AddAssign\"\n      - \"std::ops::Div\"\n      - \"std::ops::Mul\"\n      - \"std::ops::Neg\"\n      - \"std::ops::Sub\"\n      - \"std::ops::SubAssign\"\n  - kind: type\n    names:\n      - \"chrono::format::ParseResult\"\n"
  },
  {
    "path": "data/fs.yml",
    "content": "title: Filesystem\n\nbase:\n  fs2: \"https://docs.rs/fs2/0.4.3/fs2/\"\n\nmain:\n  # fs & fs2\n  - - mod: \"fs\"\n      path: \"std::fs\"\n      groups:\n        - name: \"Read & write\"\n          items:\n            - \"read (AsRef<Path>) -> Result<Vec<u8>>\"\n            - \"read_to_string (AsRef<Path>) -> Result<String>\"\n            - \"write (AsRef<Path>, &str) -> Result<()>\"\n        - name: \"Directory\"\n          items:\n            - \"read_dir (AsRef<Path>) -> Result<Iterator<Item = Result<DirEntry>>>\"\n            - \"create_dir     (AsRef<Path>) -> Result<()>\"\n            - \"create_dir_all (AsRef<Path>) -> Result<()>\"\n            - \"remove_dir     (AsRef<Path>) -> Result<()>\"\n            - \"remove_dir_all (AsRef<Path>) -> Result<()>\"\n        - name: \"File operation\"\n          items:\n            - \"copy   (AsRef<Path>, AsRef<Path>) -> Result<u64>\"\n            - \"rename (AsRef<Path>, AsRef<Path>) -> Result<()>\"\n            - \"remove_file (AsRef<Path>) -> Result<()>\"\n        - name: \"Metadata\"\n          items:\n            - \"metadata         (AsRef<Path>) -> Result<Metadata>\"\n            - \"symlink_metadata (AsRef<Path>) -> Result<Metadata>\"\n            - \"set_permissions  (AsRef<Path>, Permissions) -> Result<()>\"\n        - name: \"Link\"\n          items:\n            - \"canonicalize (AsRef<Path>) -> Result<PathBuf>\"\n            - \"hard_link (AsRef<Path>, AsRef<Path>) -> Result<()>\"\n            - \"read_link (AsRef<Path>) -> Result<PathBuf>\"\n    - mod: \"fs2\"\n      path: \"fs2\"\n      groups:\n        - name: \"Filesystem info\"\n          items:\n            - \"available_space (AsRef<Path>) -> Result<u64>\"\n            - \"free_space      (AsRef<Path>) -> Result<u64>\"\n            - \"total_space     (AsRef<Path>) -> Result<u64>\"\n            - \"allocation_granularity (AsRef<Path>) -> Result<u64>\"\n            - \"statvfs (AsRef<Path>) -> Result<FsStats>\"\n  # Path\n  - - type: \"&Path\"\n      groups:\n        - name: \"Type conversion and display\"\n          items:\n            - \"as_os_str   () -> &OsStr\"\n            - \"to_path_buf () -> PathBuf\"\n            - \"to_str      () -> Option<&str>\"\n            - \"to_string_lossy () -> Cow<str>\"\n            - \"display () -> Display\"\n        - name: \"Path type\"\n          items:\n            - \"has_root    () -> bool\"\n            - \"is_absolute () -> bool\"\n            - \"is_relative () -> bool\"\n        - name: \"Filename in path\"\n          items:\n            - \"file_name () -> Option<&OsStr>\"\n            - \"file_stem () -> Option<&OsStr>\"\n            - \"extension () -> Option<&OsStr>\"\n        - name: \"Components of path\"\n          items:\n            - \"iter       () -> Iterator<Item = &OsStr>\"\n            - \"components () -> Iterator<Item = Component>\"\n        - name: \"Ancestors\"\n          items:\n            - \"parent    () -> Option<&Path>\"\n            - \"ancestors () -> Iterator<Item = &Path>\"\n        - name: \"Prefix / suffix\"\n          items:\n            # TODO correctly use std::result::Result instead\n            - \"strip_prefix () -> Result<&Path, StripPrefixError>\"\n            - \"starts_with (AsRef<Path>) -> bool\"\n            - \"ends_with   (AsRef<Path>) -> bool\"\n        - name: \"Construct new path\"\n          items:\n            - \"join (AsRef<Path>) -> PathBuf\"\n            - \"with_file_name (AsRef<OsStr>) -> PathBuf\"\n            - \"with_extension (AsRef<OsStr>) -> PathBuf\"\n        - name: \"Property of path target\"\n          items:\n            - \"exists  () -> bool\"\n            - \"is_file () -> bool\"\n            - \"is_dir  () -> bool\"\n        - name: \"Metadata\"\n          items:\n            - \"metadata () -> Result<Metadata>\"\n            - \"symlink_metadata () -> Result<Metadata>\"\n        - name: \"Misc\"\n          items:\n            - \"read_dir     () -> Result<Iterator<Item = Result<DirEntry>>>\"\n            - \"read_link    () -> Result<PathBuf>\"\n            - \"canonicalize () -> Result<PathBuf>\"\n    - type: \"PathBuf\"\n      groups:\n        - items:\n            - \"::new () -> PathBuf\"\n            - \"into_os_string () -> OsString\"\n            - \"into_boxed_path () -> Box<Path>\"\n    - type: \"&mut PathBuf\"\n      groups:\n        - items:\n            - \"push (AsRef<Path>)\"\n            - \"pop () -> bool\"\n            - \"set_file_name (AsRef<OsStr>)\"\n            - \"set_extension (AsRef<OsStr>) -> bool\"\n  # File & fs2::FileExt\n  - - type: \"File\"\n      impls:\n        - \"Read\"\n        - \"Write\"\n        - \"Seek\"\n        - \"FileExt\"\n      groups:\n        - name: \"Open file\"\n          items:\n            - \"::open   (AsRef<Path>) -> Result<File>\"\n            - \"::create (AsRef<Path>) -> Result<File>\"\n    - type: \"&File\"\n      impls:\n        - \"Read\"\n        - \"Write\"\n        - \"Seek\"\n      groups:\n        - name: \"Syncing\"\n          items:\n            - \"sync_all  () -> Result<()>\"\n            - \"sync_data () -> Result<()>\"\n        - name: \"Metadata\"\n          items:\n            - \"metadata () -> Result<Metadata>\"\n            - \"set_permissions (Permissions) -> Result<()>\"\n        - name: \"Other\"\n          items:\n            - \"set_len (u64) -> Result<()>\"\n            - \"try_clone () -> Result<File>\"\n    - type: \"&FileExt\"\n      groups:\n        - name: \"Allocation\"\n          items:\n            - \"allocate (u64) -> Result<()>\"\n            - \"allocated_size () -> Result<u64>\"\n        - name: \"Lock\"\n          items:\n            - \"unlock () -> Result<()>\"\n            - \"lock_shared    () -> Result<()>\"\n            - \"lock_exclusive () -> Result<()>\"\n            - \"try_lock_shared    () -> Result<()>\"\n            - \"try_lock_exclusive () -> Result<()>\"\n  # Metadata\n  - - type: \"&Metadata\"\n      groups:\n        - name: \"File type\"\n          items:\n            - \"is_dir  () -> bool\"\n            - \"is_file () -> bool\"\n            - \"file_type () -> FileType\"\n        - name: \"Time\"\n          items:\n            - \"created  () -> Result<SystemTime>\"\n            - \"modified () -> Result<SystemTime>\"\n            - \"accessed () -> Result<SystemTime>\"\n        - name: \"Misc\"\n          items:\n            - \"len () -> u64\"\n            - \"permissions () -> Permissions\"\n\ntrait_impls:\n  - pat: \"AsRef<Path>\"\n    impls:\n      - \"&str\"\n      - \"&Path\"\n      - \"&OsStr\"\n  - pat: \"AsRef<OsStr>\"\n    impls:\n      - \"&str\"\n      - \"&OsStr\"\n      - \"&Path\"\n\nreferences:\n  - kind: trait\n    names:\n      - \"fs2::FileExt\"\n      - \"std::convert::AsRef\"\n      - \"std::fmt::Display\"\n      - \"std::io::Read\"\n      - \"std::io::Seek\"\n      - \"std::io::Write\"\n      - \"std::iter::Iterator\"\n  - kind: enum\n    names:\n      - \"std::borrow::Cow\"\n      - \"std::io::SeekFrom\"\n      - \"std::option::Option\"\n      - \"std::path::Component\"\n  - kind: struct\n    names:\n      - \"fs2::FsStats\"\n      - \"std::boxed::Box\"\n      - \"std::ffi::OsStr\"\n      - \"std::ffi::OsString\"\n      - \"std::fs::DirEntry\"\n      - \"std::fs::File\"\n      - \"std::fs::FileType\"\n      - \"std::fs::Metadata\"\n      - \"std::fs::Permissions\"\n      - \"std::path::Path\"\n      - \"std::path::PathBuf\"\n      - \"std::path::StripPrefixError\"\n      - \"std::string::String\"\n      - \"std::time::SystemTime\"\n      - \"std::vec::Vec\"\n  - kind: type\n    names:\n      - \"std::io::Result\"\n"
  },
  {
    "path": "data/index.yml",
    "content": "title: Basics\n\nmain:\n  # Option & Result\n  - - type: \"Option<T>\"\n      groups:\n        - name: \"To inner type\"\n          items:\n            - \"unwrap () -> T\"\n            - \"unwrap_or (T) -> T\"\n            - \"unwrap_or_else (() -> T) -> T\"\n            - \"unwrap_or_default () -> T where T: Default\"\n            - \"expect (&str) -> T\"\n        - name: \"Converting to another type\"\n          items:\n            - \"map ((T) -> U) -> Option<U>\"\n            - \"map_or (U, (T) -> U) -> U\"\n            - \"map_or_else (() -> U, (T) -> U) -> U\"\n        - name: \"To Result\"\n          items:\n            - \"ok_or (E) -> Result<T, E>\"\n            - \"ok_or_else (() -> E) -> Result<T, E>\"\n        - name: \"Conditioning\"\n          items:\n            - \"filter ((&T) -> bool) -> Option<T>\"\n            - \"and (Option<U>) -> Option<U>\"\n            - \"and_then ((T) -> Option<U>) -> Option<U>\"\n            - \"or (Option<T>) -> Option<T>\"\n            - \"or_else (() -> Option<T>) -> Option<T>\"\n            - \"xor (Option<T>) -> Option<T>\"\n    - type: \"Option<&T>\"\n      groups:\n        - name: \"Cloning inner\"\n          items:\n            - \"cloned () -> Option<T> where T: Clone\"\n            - \"copied () -> Option<T> where T: Copy\"\n    - type: \"Option<Option<T>>\"\n      groups:\n        - items:\n            - \"flatten () -> Option<T>\"\n    - type: \"Option<Result<T, E>>\"\n      groups:\n        - items:\n            - \"transpose () -> Result<Option<T>, E>\"\n    - type: \"&Option<T>\"\n      groups:\n        - name: \"Checking inner\"\n          items:\n            - \"is_some () -> bool\"\n            - \"is_none () -> bool\"\n        - name: \"To inner reference\"\n          items:\n            - \"as_ref () -> Option<&T>\"\n            - \"iter () -> Iterator<&T>\"\n            - |-\n              as_deref () -> Option<&U>\n              where T: Deref<Target = U>\n    - type: \"&mut Option<T>\"\n      groups:\n        - name: \"To inner mutable reference\"\n          items:\n            - \"as_mut () -> Option<&mut T>\"\n            - \"iter_mut () -> Iterator<&mut T>\"\n            - |-\n              as_deref_mut () -> Option<&mut U>\n              where T: DerefMut + Deref<Target = U>\n        - name: \"Mutation\"\n          items:\n            - \"take () -> Option<T>\"\n            - \"replace (T) -> Option<T>\"\n            - \"insert (T) -> &mut T\"\n            - \"get_or_insert (T) -> &mut T\"\n            - \"get_or_insert_with (() -> T) -> &mut T\"\n    - type: \"Result<T, E>\"\n      groups:\n        - name: \"To inner type\"\n          items:\n            - \"unwrap () -> T where E: Debug\"\n            - \"unwrap_err () -> E where T: Debug\"\n            - \"unwrap_or (T) -> T\"\n            - \"unwrap_or_else ((E) -> T) -> T\"\n            - \"unwrap_or_default () -> T where T: Default\"\n            - \"expect (&str) -> T\"\n            - \"expect_err (&str) -> E\"\n            - \"ok () -> Option<T>\"\n            - \"err () -> Option<E>\"\n        - name: \"Mapping\"\n          items:\n            - \"map ((T) -> U) -> Result<U, E>\"\n            - \"map_err ((E) -> F) -> Result<T, F>\"\n            - \"map_or (U, (T) -> U) -> U\"\n            - \"map_or_else ((E) -> U, (T) -> U) -> U\"\n        - name: \"Conditioning\"\n          items:\n            - \"and (Result<U, E>) -> Result<U, E>\"\n            - \"and_then ((T) -> Result<U, E>) -> Result<U, E>\"\n            - \"or (Result<T, F>) -> Result<T, F>\"\n            - \"or_else ((E) -> Result<T, F>) -> Result<T, F>\"\n    - type: \"Result<Option<T>, E>\"\n      groups:\n        - name: \"Transposing\"\n          items:\n            - \"transpose () -> Option<Result<T, E>>\"\n    - type: \"&Result<T, E>\"\n      groups:\n        - name: \"Checking inner\"\n          items:\n            - \"is_ok () -> bool\"\n            - \"is_err () -> bool\"\n        - name: \"To inner reference\"\n          items:\n            - \"as_ref () -> Result<&T, &E>\"\n            - \"iter () -> Iterator<Item = &T>\"\n    - type: \"&mut Result<T, E>\"\n      groups:\n        - name: \"To inner mutable reference\"\n          items:\n            - \"as_mut () -> Result<&mut T, &mut E>\"\n            - \"iter_mut () -> Iterator<Item = &mut T>\"\n  # Iterator\n  - - type: \"Iterator<Item = T>\"\n      groups:\n        - name: \"Mapping and filtering\"\n          items:\n            - \"map        (( T) -> U)         -> Iterator<Item = U>\"\n            - \"filter     ((&T) -> bool)      -> Iterator<Item = T>\"\n            - \"filter_map (( T) -> Option<U>) -> Iterator<Item = U>\"\n        - name: \"Collecting and folding\"\n          items:\n            - \"fold (S, (S, T) -> S) -> S\"\n            - \"collect () -> B where B: FromIterator<T>\"\n            - \"partition ((&T) -> bool) -> (B, B) where B: Default + Extend<T>\"\n        - name: \"Counting and enumerating\"\n          items:\n            - \"count () -> usize\"\n            - \"last () -> Option<T>\"\n            - \"enumerate () -> Iterator<Item = (usize, T)>\"\n        - name: \"Combining with other iterators\"\n          items:\n            - \"zip   (IntoIterator<Item = U>) -> Iterator<Item = (T, U)>\"\n            - \"chain (IntoIterator<Item = T>) -> Iterator<Item = T>\"\n        - name: \"Flattening\"\n          items:\n            - \"flatten () -> Iterator<U> where T: IntoIterator<U>\"\n            - \"flat_map ((T) -> IntoIterator<Item = U>) -> Iterator<Item = U>\"\n        - name: \"Taking and skipping\"\n          items:\n            - \"skip (usize) -> Iterator<Item = T>\"\n            - \"take (usize) -> Iterator<Item = T>\"\n            - \"skip_while ((&T) -> bool) -> Iterator<Item = T>\"\n            - \"take_while ((&T) -> bool) -> Iterator<Item = T>\"\n            - \"step_by (usize) -> Iterator<Item = T>\"\n        - name: \"Misc. iterating\"\n          items:\n            - \"for_each ((T) -> ()) -> ()\"\n            - \"inspect ((&T) -> ()) -> Iterator<Item = T>\"\n            - \"scan (S, (&mut S, T) -> Option<U>) -> Iterator<Item = U>\"\n        - name: \"Calculations\"\n          items:\n            - \"sum     () -> S where S: Sum<T>\"\n            - \"product () -> P where P: Product<T>\"\n        - name: \"Maximum and minimum\"\n          items:\n            - \"max () -> Option<T> where T: Ord\"\n            - \"min () -> Option<T> where T: Ord\"\n            - \"max_by ((&T, &T) -> Ordering) -> Option<T>\"\n            - \"min_by ((&T, &T) -> Ordering) -> Option<T>\"\n            - \"max_by_key ((&T) -> U) -> Option<T> where U: Ord\"\n            - \"min_by_key ((&T) -> U) -> Option<T> where U: Ord\"\n        - name: \"Comparing with another iterator\"\n          items:\n            - \"eq (IntoIterator<Item = T>) -> bool where T: PartialEq\"\n            - \"ne (IntoIterator<Item = T>) -> bool where T: PartialEq\"\n            - \"lt (IntoIterator<Item = T>) -> bool where T: PartialOrd\"\n            - \"le (IntoIterator<Item = T>) -> bool where T: PartialOrd\"\n            - \"gt (IntoIterator<Item = T>) -> bool where T: PartialOrd\"\n            - \"ge (IntoIterator<Item = T>) -> bool where T: PartialOrd\"\n            - \"cmp (IntoIterator<Item = T>) -> Ordering where T: Ord\"\n            - \"partial_cmp (IntoIterator<Item = T>)\\n-> Option<Ordering> where T: PartialOrd\"\n        - name: \"Reversing and cycling\"\n          items:\n            - \"rev   () -> Iterator<Item = T> where Self: DoubleEndedIterator\"\n            - \"cycle () -> Iterator<Item = T> where Self: Clone\"\n    - type: \"Iterator<Item = &T>\"\n      groups:\n        - name: \"Cloning inner\"\n          items:\n            - \"cloned () -> Iterator<T> where T: Clone\"\n            - \"copied () -> Iterator<T> where T: Copy\"\n    - type: \"&mut Iterator<Item = T>\"\n      groups:\n        - name: \"Finding and positioning\"\n          items:\n            - \"find      ((&T) -> bool)      -> Option<T>\"\n            - \"find_map  (( T) -> Option<U>) -> Option<U>\"\n            - \"position  (( T) -> bool)      -> Option<usize>\"\n            - |-\n              rposition (( T) -> bool)      -> Option<usize>\n              where Self: ExactSizeIterator + DoubleEndedIterator\n        - name: \"Boolean operations\"\n          items:\n            - \"all ((T) -> bool) -> bool\"\n            - \"any ((T) -> bool) -> bool\"\n        - name: \"Try iterating\"\n          items:\n            - \"try_for_each   ((T) -> R) -> R where R: Try<Ok = ()>\"\n            - \"try_fold (S, (S, T) -> R) -> R where R: Try<Ok = S>\"\n    - mod: \"iter\"\n      path: \"std::iter\"\n      groups:\n        - name: \"Creating simple iterators\"\n          items:\n            - \"empty () -> Iterator<Item = T>\"\n            - \"once (T) -> Iterator<Item = T>\"\n            - \"once_with (() -> T) -> Iterator<Item = T>\"\n            - \"repeat (T) -> Iterator<Item = T> where T: Clone\"\n            - \"repeat_with (() -> T) -> Iterator<Item = T>\"\n            - \"from_fn (() -> Option<T>) -> Iterator<Item = T>\"\n            - \"successors (Option<T>, (&T) -> Option<T>) -> Iterator<Item = T>\"\n  # Slice & Vec\n  - - type: \"&[T]\"\n      groups:\n        - name: \"Splitting to iterator\"\n          items:\n            - \"split  ((&T) -> bool) -> Iterator<Item = &[T]>\"\n            - \"rsplit ((&T) -> bool) -> Iterator<Item = &[T]>\"\n            - \"splitn  (usize, (&T) -> bool) -> Iterator<Item = &[T]>\"\n            - \"rsplitn (usize, (&T) -> bool) -> Iterator<Item = &[T]>\"\n        - name: \"Splitting at position\"\n          items:\n            - \"split_at (usize) -> (&[T], &[T])\"\n            - \"split_first () -> Option<(&T, &[T])>\"\n            - \"split_last  () -> Option<(&T, &[T])>\"\n        - name: \"Chunks and windows\"\n          items:\n            - \"chunks        (usize) -> Iterator<Item = &[T]>\"\n            - \"chunks_exact  (usize) -> Iterator<Item = &[T]>\"\n            - \"rchunks       (usize) -> Iterator<Item = &[T]>\"\n            - \"rchunks_exact (usize) -> Iterator<Item = &[T]>\"\n            - \"windows       (usize) -> Iterator<Item = &[T]>\"\n        - name: \"Matching\"\n          items:\n            - \"contains    (&T)   -> bool where T: PartialEq\"\n            - \"starts_with (&[T]) -> bool where T: PartialEq\"\n            - \"ends_with   (&[T]) -> bool where T: PartialEq\"\n        - name: \"Binary searching\"\n          items:\n            - \"binary_search (&T)                   -> Result<usize, usize> where T: Ord\"\n            - \"binary_search_by ((&T) -> Ordering)  -> Result<usize, usize>\"\n            - \"binary_search_by_key (&B, (&T) -> B) -> Result<usize, usize> where B: Ord\"\n        - name: \"Getting and iterating\"\n          items:\n            - \"first () -> Option<&T>\"\n            - \"last  () -> Option<&T>\"\n            - \"get (SliceIndex<[T]>) -> Option<&T>\"\n            - \"iter () -> Iterator<Item = &T>\"\n        - name: \"Length\"\n          items:\n            - \"len () -> usize\"\n            - \"is_empty () -> bool\"\n    - type: \"&mut [T]\"\n      groups:\n        - name: \"Splitting to iterator\"\n          items:\n            - \"split_mut  ((&T) -> bool) -> Iterator<Item = &mut [T]>\"\n            - \"rsplit_mut ((&T) -> bool) -> Iterator<Item = &mut [T]>\"\n            - \"splitn_mut  (usize, (&T) -> bool) -> Iterator<Item = &mut [T]>\"\n            - \"rsplitn_mut (usize, (&T) -> bool) -> Iterator<Item = &mut [T]>\"\n        - name: \"Splitting at position\"\n          items:\n            - \"split_at_mut (usize) -> (&mut [T], &mut [T])\"\n            - \"split_first_mut () -> Option<(&mut T, &mut [T])>\"\n            - \"split_last_mut  () -> Option<(&mut T, &mut [T])>\"\n        - name: \"Chunks\"\n          items:\n            - \"chunks_mut        (usize) -> Iterator<Item = &mut [T]>\"\n            - \"chunks_exact_mut  (usize) -> Iterator<Item = &mut [T]>\"\n            - \"rchunks_mut       (usize) -> Iterator<Item = &mut [T]>\"\n            - \"rchunks_exact_mut (usize) -> Iterator<Item = &mut [T]>\"\n        - name: \"Sorting\"\n          items:\n            - \"sort () where T: Ord\"\n            - \"sort_by ((&T, &T) -> Ordering)\"\n            - \"sort_by_key ((&T) -> K) where K: Ord\"\n            - \"sort_by_cached_key ((&T) -> K) where K: Ord\"\n            - \"sort_unstable () where T: Ord\"\n            - \"sort_unstable_by ((&T, &T) -> Ordering)\"\n            - \"sort_unstable_by_key ((&T) -> K) where K: Ord\"\n        - name: \"Rearranging\"\n          items:\n            - \"swap (usize, usize)\"\n            - \"reverse ()\"\n            - \"rotate_left (usize)\"\n            - \"rotate_right (usize)\"\n        - name: \"Overriding\"\n          items:\n            - \"swap_with_slice  (&mut [T])\"\n            - \"copy_from_slice  (&[T]) where T: Copy\"\n            - \"clone_from_slice (&[T]) where T: Clone\"\n        - name: \"Getting and iterating\"\n          items:\n            - \"first_mut () -> Option<&mut T>\"\n            - \"last_mut  () -> Option<&mut T>\"\n            - \"get_mut (SliceIndex<[T]>) -> Option<&mut T>\"\n            - \"iter_mut () -> Iterator<Item = &mut T>\"\n    - type: \"&mut Vec<T>\"\n      groups:\n        - name: \"Adding and removing single item\"\n          items:\n            - \"push (T)\"\n            - \"pop () -> Option<T>\"\n            - \"insert (usize, T)\"\n            - \"remove (usize) -> T\"\n            - \"swap_remove (usize) -> T\"\n        - name: \"Extending\"\n          items:\n            - \"append (&mut Vec<T>)\"\n            - trait_impl: \"Extend<T>\"\n              content: \"extend (IntoIterator<Item = T>)\"\n            - trait_impl: \"Extend<&'a T>\"\n              content: \"extend (IntoIterator<Item = &T>) where T: Copy\"\n            - \"extend_from_slice (&[T]) where T: Clone\"\n        - name: \"Resizing\"\n          items:\n            - \"truncate (usize)\"\n            - \"resize (usize, T) where T: Clone\"\n            - \"resize_with (usize, () -> T)\"\n        - name: \"Clearing\"\n          items:\n            - \"clear ()\"\n            - \"retain ((&T) -> bool)\"\n        - name: \"Removing or replacing range into iterator\"\n          items:\n            - \"drain  (RangeBounds<usize>) -> Iterator<T>\"\n            - \"splice (RangeBounds<usize>, IntoIterator<Item = T>) -> Iterator<T>\"\n        - name: \"Deduplicating\"\n          items:\n            - \"dedup () where T: PartialEq\"\n            - \"dedup_by ((&mut T, &mut T) -> bool)\"\n            - \"dedup_by_key ((&mut T) -> K) where K: PartialEq\"\n        - name: \"Splitting off\"\n          items:\n            - \"split_off (usize) -> Vec<T>\"\n        - name: \"Capacity manipulation\"\n          items:\n            - \"reserve (usize)\"\n            - \"reserve_exact (usize)\"\n            - \"shrink_to_fit ()\"\n    - mod: \"slice\"\n      path: \"std::slice\"\n      groups:\n        - name: \"Creating slice from reference\"\n          items:\n            - \"from_ref (&T) -> &[T]\"\n            - \"from_mut (&mut T) -> &mut [T]\"\n  # String-related\n  - - type: \"&[u8]\"\n      groups:\n        - name: \"ASCII\"\n          items:\n            - \"is_ascii () -> bool\"\n            - \"eq_ignore_ascii_case (&[u8]) -> bool\"\n            - \"to_ascii_uppercase () -> Vec<u8>\"\n            - \"to_ascii_lowercase () -> Vec<u8>\"\n    - type: \"&mut [u8]\"\n      groups:\n        - name: \"ASCII\"\n          items:\n            - \"make_ascii_uppercase ()\"\n            - \"make_ascii_lowercase ()\"\n    - mod: \"str\"\n      path: \"std::str\"\n      groups:\n        - name: \"Bytes\"\n          items:\n            - \"from_utf8     (&[u8])     -> Result<&str, Utf8Error>\"\n            - \"from_utf8_mut (&mut [u8]) -> Result<&mut str, Utf8Error>\"\n    - type: \"&str\"\n      groups:\n        - name: \"Chars\"\n          items:\n            - \"chars () -> Iterator<Item = char>\"\n            - \"char_indices () -> Iterator<Item = (usize, char)>\"\n            - \"is_char_boundary (usize) -> bool\"\n        - name: \"Bytes\"\n          items:\n            - \"bytes () -> Iterator<Item = u8>\"\n            - \"as_bytes () -> &[u8]\"\n        - name: \"Splitting to two parts\"\n          items:\n            - \"split_at (usize) -> (&str, &str)\"\n        - name: \"Splitting to iterator\"\n          items:\n            - \"lines () -> Iterator<Item = &str>\"\n            - \"split_whitespace () -> Iterator<Item = &str>\"\n            - \"split_ascii_whitespace () -> Iterator<Item = &str>\"\n            - \"split  (Pattern) -> Iterator<Item = &str>\"\n            - \"rsplit (Pattern) -> Iterator<Item = &str>\"\n            - \"splitn  (usize, Pattern) -> Iterator<Item = &str>\"\n            - \"rsplitn (usize, Pattern) -> Iterator<Item = &str>\"\n            - \"split_terminator  (Pattern) -> Iterator<Item = &str>\"\n            - \"rsplit_terminator (Pattern) -> Iterator<Item = &str>\"\n        - name: \"Trimming\"\n          items:\n            - \"trim       () -> &str\"\n            - \"trim_start () -> &str\"\n            - \"trim_end   () -> &str\"\n            - \"trim_matches       (Pattern) -> &str\"\n            - \"trim_start_matches (Pattern) -> &str\"\n            - \"trim_end_matches   (Pattern) -> &str\"\n        - name: \"Matching and finding\"\n          items:\n            - \"contains    (Pattern) -> bool\"\n            - \"starts_with (Pattern) -> bool\"\n            - \"ends_with   (Pattern) -> bool\"\n            - \"find  (Pattern) -> Option<usize>\"\n            - \"rfind (Pattern) -> Option<usize>\"\n            - \"matches  (Pattern) -> Iterator<Item = &str>\"\n            - \"rmatches (Pattern) -> Iterator<Item = &str>\"\n            - \"match_indices  (Pattern) -> Iterator<Item = (usize, &str)>\"\n            - \"rmatch_indices (Pattern) -> Iterator<Item = (usize, &str)>\"\n        - name: \"Case\"\n          items:\n            - \"to_uppercase () -> String\"\n            - \"to_lowercase () -> String\"\n            - \"to_ascii_uppercase () -> String\"\n            - \"to_ascii_lowercase () -> String\"\n            - \"eq_ignore_ascii_case (&str) -> bool\"\n        - name: \"Replacing\"\n          items:\n            - \"replace  (Pattern, &str) -> String\"\n            - \"replacen (Pattern, &str, usize) -> String\"\n        - name: \"Length\"\n          items:\n            - \"len () -> usize\"\n            - \"is_empty () -> bool\"\n        - name: \"Misc.\"\n          items:\n            - \"is_ascii () -> bool\"\n            - \"repeat (usize) -> String\"\n            - \"encode_utf16 () -> Iterator<Item = u16>\"\n            - \"parse () -> Result<F, F::Err> where F: FromStr\"\n    - type: \"&mut str\"\n      groups:\n        - name: \"Splitting to two parts\"\n          items:\n            - \"split_at_mut (usize) -> (&mut str, &mut str)\"\n        - name: \"Case conversion\"\n          items:\n            - \"make_ascii_uppercase ()\"\n            - \"make_ascii_lowercase ()\"\n    - type: \"&mut String\"\n      groups:\n        - name: \"Inserting and appending string\"\n          items:\n            - \"push_str (&str)\"\n            - \"insert_str (usize, &str)\"\n        - name: \"Adding and removing char\"\n          items:\n            - \"push (char)\"\n            - \"pop () -> Option<char>\"\n            - \"insert (usize, char)\"\n            - \"remove (usize) -> char\"\n        - name: \"Clearing\"\n          items:\n            - \"clear ()\"\n            - \"truncate (usize)\"\n            - \"retain ((char) -> bool)\"\n        - name: \"Capacity manipulation\"\n          items:\n            - \"reserve (usize)\"\n            - \"reserve_exact (usize)\"\n            - \"shrink_to_fit ()\"\n        - name: \"Misc.\"\n          items:\n            - \"split_off (usize) -> String\"\n            - \"replace_range (RangeBounds<usize>, &str)\"\n            - \"drain (RangeBounds<usize>) -> Iterator<Item = char>\"\n\ntrait_impls:\n  - pat: \"Try<Ok = T>\"\n    generic: \"T\"\n    impls:\n      - \"Option<T>\"\n      - \"Result<T, E>\"\n  - pat: \"Pattern\"\n    impls:\n      - \"char\"\n      - \"&str\"\n      - \"&[char]\"\n      - \"(char) -> bool\"\n  - pat: \"SliceIndex<[T]>\"\n    generic: \"T\"\n    impls:\n      - \"usize\"\n      - \"usize..usize\"\n      - \"usize..\"\n      - \"     ..usize\"\n      - \"usize..=usize\"\n      - \"     ..=usize\"\n      - \"     ..\"\n  - pat: \"RangeBounds<usize>\"\n    impls:\n      - \"usize..usize\"\n      - \"usize..\"\n      - \"     ..usize\"\n      - \"usize..=usize\"\n      - \"     ..=usize\"\n      - \"     ..\"\n\nreferences:\n  - kind: trait\n    names:\n      - \"std::clone::Clone\"\n      - \"std::cmp::Ord\"\n      - \"std::cmp::PartialEq\"\n      - \"std::cmp::PartialOrd\"\n      - \"std::default::Default\"\n      - \"std::fmt::Debug\"\n      - \"std::iter::DoubleEndedIterator\"\n      - \"std::iter::ExactSizeIterator\"\n      - \"std::iter::Extend\"\n      - \"std::iter::FromIterator\"\n      - \"std::iter::IntoIterator\"\n      - \"std::iter::Iterator\"\n      - \"std::iter::Product\"\n      - \"std::iter::Sum\"\n      - \"std::marker::Copy\"\n      - \"std::ops::Deref\"\n      - \"std::ops::DerefMut\"\n      - \"std::ops::RangeBounds\"\n      - \"std::ops::Try\"\n      - \"std::slice::SliceIndex\"\n      - \"std::str::FromStr\"\n      - \"std::str::pattern::Pattern\"\n  - kind: enum\n    names:\n      - \"std::option::Option\"\n      - \"std::cmp::Ordering\"\n      - \"std::result::Result\"\n  - kind: struct\n    names:\n      - \"std::ops::Range\"\n      - \"std::str::Utf8Error\"\n      - \"std::string::String\"\n      - \"std::vec::Vec\"\n"
  },
  {
    "path": "src/input.rs",
    "content": "use serde::Deserialize;\nuse std::collections::HashMap;\nuse std::error::Error;\nuse std::fs::File;\nuse std::path::Path;\n\nconst DEFAULT_STD_URL: &str = \"https://doc.rust-lang.org/std/\";\n\n#[derive(Debug, Deserialize)]\npub struct InputData {\n    pub title: String,\n    #[serde(default)]\n    pub base: BaseUrlMap,\n    pub main: Vec<Vec<Part>>,\n    #[serde(default)]\n    pub trait_impls: Vec<TraitImplPattern>,\n    pub references: Vec<References>,\n}\n\nimpl InputData {\n    pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Box<dyn Error>> {\n        let file = File::open(path)?;\n        Ok(serde_yaml::from_reader(file)?)\n    }\n}\n\n#[derive(Debug, Default, Deserialize)]\npub struct BaseUrlMap(HashMap<String, String>);\n\nimpl BaseUrlMap {\n    pub fn get_url_for(&self, name: &str) -> Option<&str> {\n        self.0.get(name).map(String::as_str).or(match name {\n            \"std\" => Some(DEFAULT_STD_URL),\n            _ => None,\n        })\n    }\n}\n\n// TODO: try to avoid using untagged here\n// because untagged makes it hard to debug data file when parsing fails.\n// Also see https://github.com/serde-rs/serde/issues/1520\n#[derive(Debug, Deserialize)]\n#[serde(untagged)]\npub enum Part {\n    Mod(Mod),\n    Type(Type),\n}\n\n#[derive(Debug, Deserialize)]\npub struct Mod {\n    #[serde(rename = \"mod\")]\n    pub name: String,\n    pub path: String,\n    pub groups: Vec<Group>,\n}\n\n#[derive(Debug, Deserialize)]\npub struct Type {\n    #[serde(rename = \"type\")]\n    pub ty: String,\n    pub constraints: Option<String>,\n    pub impls: Option<Vec<String>>,\n    pub groups: Vec<Group>,\n}\n\n#[derive(Clone, Copy, Debug, Deserialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum Kind {\n    Enum,\n    Primitive,\n    Struct,\n    Trait,\n    Type,\n    Union,\n}\n\nimpl Kind {\n    pub fn to_str(self) -> &'static str {\n        match self {\n            Kind::Enum => \"enum\",\n            Kind::Primitive => \"primitive\",\n            Kind::Struct => \"struct\",\n            Kind::Trait => \"trait\",\n            Kind::Type => \"type\",\n            Kind::Union => \"union\",\n        }\n    }\n}\n\n#[derive(Debug, Deserialize)]\npub struct Group {\n    pub name: Option<String>,\n    pub items: Vec<InputItem>,\n}\n\n#[derive(Debug, Deserialize)]\n#[serde(untagged)]\npub enum InputItem {\n    Plain(String),\n    Detailed {\n        trait_impl: Option<String>,\n        content: String,\n    },\n}\n\nimpl InputItem {\n    pub fn content(&self) -> &str {\n        match self {\n            InputItem::Plain(content) => content.as_str(),\n            InputItem::Detailed { content, .. } => content.as_str(),\n        }\n    }\n\n    pub fn trait_impl(&self) -> Option<&str> {\n        match self {\n            InputItem::Plain(_) => None,\n            InputItem::Detailed { trait_impl, .. } => trait_impl.as_ref().map(String::as_str),\n        }\n    }\n}\n\n#[derive(Debug, Deserialize)]\npub struct TraitImplPattern {\n    pub pat: String,\n    pub generic: Option<String>,\n    pub impls: Vec<String>,\n}\n\n#[derive(Debug, Deserialize)]\npub struct References {\n    pub kind: Kind,\n    pub names: Vec<String>,\n    #[serde(default)]\n    pub aliases: HashMap<String, String>,\n}\n"
  },
  {
    "path": "src/macros.rs",
    "content": "#[cfg(test)]\n#[macro_export]\nmacro_rules! tokens {\n    ($($t:tt)*) => {{\n        #[allow(unused_imports)]\n        use crate::token::{Primitive, Range, Token, TokenStream};\n        let mut result = vec![];\n        tokens_impl!(result $($t)*);\n        result\n    }};\n}\n\n#[cfg(test)]\n#[macro_export]\nmacro_rules! tokens_impl {\n    ($result:ident) => {};\n    ($result:ident where $($t:tt)*) => {\n        $result.push(Token::Where);\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident +$ident:ident $($t:tt)*) => {\n        $result.push(Token::AssocType(stringify!($ident)));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident $ident:ident $($t:tt)*) => {\n        $result.push(Token::Identifier(stringify!($ident)));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident $str:literal $($t:tt)*) => {\n        $result.push(Token::Text($str));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident &$r:literal $($t:tt)*) => {\n        $result.push(Token::Primitive(Primitive::Ref(concat!(\"&\", $r))));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident *$r:literal $($t:tt)*) => {\n        $result.push(Token::Primitive(Primitive::Ptr(concat!(\"*\", $r))));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident @() $($t:tt)*) => {\n        $result.push(Token::Type(TokenStream(vec![\n            Token::Primitive(Primitive::Unit),\n        ])));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident @( $($inner:tt)* ) $($t:tt)*) => {\n        $result.push(Token::Type(TokenStream(vec![\n            Token::Primitive(Primitive::TupleStart),\n            Token::Nested(TokenStream(tokens!($($inner)*))),\n            Token::Primitive(Primitive::TupleEnd),\n        ])));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident @[ $($inner:tt)* ] $($t:tt)*) => {\n        let mut inner = vec![];\n        inner.push(Token::Primitive(Primitive::SliceStart));\n        tokens_impl!(inner $($inner)*);\n        inner.push(Token::Primitive(Primitive::SliceEnd));\n        $result.push(Token::Type(TokenStream(inner)));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident ~$range:ident $($t:tt)*) => {\n        $result.push(Token::Range(Range::$range));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident @$ident:ident $($t:tt)*) => {\n        $result.push(Token::Type(TokenStream(vec![\n            Token::Primitive(Primitive::Named(stringify!($ident))),\n        ])));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident ^$ident:ident $($t:tt)*) => {\n        $result.push(Token::Type(TokenStream(vec![\n            Token::Identifier(stringify!($ident)),\n        ])));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident ^[ $($inner:tt)* ] $($t:tt)*) => {\n        $result.push(Token::Type(TokenStream(tokens!($($inner)*))));\n        tokens_impl!($result $($t)*);\n    };\n    ($result:ident { $($inner:tt)* } $($t:tt)*) => {\n        $result.push(Token::Nested(TokenStream(tokens!($($inner)*))));\n        tokens_impl!($result $($t)*);\n    };\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "#[macro_use]\nextern crate combine;\n\nuse crate::input::InputData;\nuse crate::page_gen::generate_to;\nuse std::env;\nuse std::error::Error;\n\n#[macro_use]\nmod macros;\n\nmod input;\nmod page_gen;\nmod parser;\nmod token;\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let mut args = env::args();\n    let _ = args.next(); // executable path\n                         // Get path of input data file and output file\n    let input_file = args.next().expect(\"must specify input file\");\n    let output_file = args.next().expect(\"must specify output file\");\n    // Generate the page\n    let input = InputData::from_file(&input_file)?;\n    generate_to(&output_file, &input)?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/page_gen.rs",
    "content": "use crate::input::{\n    BaseUrlMap, Group, InputData, InputItem, Kind, Mod, Part, References, TraitImplPattern, Type,\n};\nuse crate::parser::{self, ParsedItem};\nuse crate::token::{Primitive, RangeToken, Token, TokenStream};\nuse bitflags::bitflags;\nuse std::collections::HashMap;\nuse std::fmt::{Display, Formatter, Result, Write as _};\nuse std::fs::File;\nuse std::io::{self, Write as _};\nuse std::iter;\nuse std::path::Path;\nuse v_htmlescape::escape;\n\npub fn generate_to(path: impl AsRef<Path>, input: &InputData) -> io::Result<()> {\n    let mut file = File::create(path)?;\n    let content_writer = PageContentWriter { input };\n    write!(\n        file,\n        include_str!(\"template.html\"),\n        title = escape(&input.title),\n        content = content_writer\n    )?;\n    Ok(())\n}\n\nstruct PageContentWriter<'a> {\n    input: &'a InputData,\n}\n\nimpl Display for PageContentWriter<'_> {\n    fn fmt(&self, f: &mut Formatter) -> Result {\n        let InputData {\n            base,\n            trait_impls,\n            references,\n            main,\n            ..\n        } = self.input;\n        Generator::new(base, trait_impls, references).generate(f, main)\n    }\n}\n\nstruct Generator<'a> {\n    base: &'a BaseUrlMap,\n    trait_impls: Vec<TraitImpl<'a>>,\n    references: HashMap<&'a str, Reference>,\n}\n\nstruct TraitImpl<'a> {\n    pat: TokenStream<'a>,\n    generic: Option<&'a str>,\n    impls: Vec<TokenStream<'a>>,\n}\n\nimpl<'a> Generator<'a> {\n    fn new(\n        base: &'a BaseUrlMap,\n        trait_impls: &'a [TraitImplPattern],\n        ref_data: &'a [References],\n    ) -> Self {\n        let trait_impls = trait_impls\n            .iter()\n            .map(|trait_impl| {\n                let pat = parse_type(&trait_impl.pat);\n                let generic = trait_impl.generic.as_deref();\n                let impls = trait_impl\n                    .impls\n                    .iter()\n                    .map(|impl_| {\n                        parser::parse_trait_impl(impl_)\n                            .map_err(|_| format!(\"failed to parse trait impl: {}\", impl_))\n                            .unwrap()\n                    })\n                    .collect();\n                TraitImpl {\n                    pat,\n                    generic,\n                    impls,\n                }\n            })\n            .collect();\n        let references = ref_data\n            .iter()\n            .flat_map(|reference| {\n                let kind = reference.kind;\n                iter::empty()\n                    .chain(reference.names.iter().map(move |item| {\n                        let (path, name) = parse_path(item);\n                        let url = build_type_url(base, &path, kind, name);\n                        (name, Reference { kind, url })\n                    }))\n                    .chain(reference.aliases.iter().map(move |(alias, path)| {\n                        let (path, name) = parse_path(path);\n                        let url = build_type_url(base, &path, kind, name);\n                        (alias.as_str(), Reference { kind, url })\n                    }))\n            })\n            .collect();\n        Generator {\n            base,\n            trait_impls,\n            references,\n        }\n    }\n\n    fn generate(&self, f: &mut Formatter, data: &[Vec<Part>]) -> Result {\n        write!(f, \"<main>\")?;\n        data.iter()\n            .try_for_each(|section| self.generate_section(f, section))?;\n        write!(f, \"</main>\")?;\n        Ok(())\n    }\n\n    fn generate_section(&self, f: &mut Formatter, section: &[Part]) -> Result {\n        write!(f, r#\"<section class=\"section\">\"#)?;\n        section\n            .iter()\n            .try_for_each(|part| self.generate_part(f, part))?;\n        write!(f, \"</section>\")?;\n        Ok(())\n    }\n\n    fn generate_part(&self, f: &mut Formatter, part: &Part) -> Result {\n        let info = self.build_part_info(part);\n        write!(f, r#\"<hgroup class=\"part-title-group\">\"#)?;\n        write!(\n            f,\n            r#\"<h2 class=\"part-title\"><a href=\"{}\">{}</a></h2>\"#,\n            info.base_url,\n            escape(info.title)\n        )?;\n        if let Some(constraints) = &info.constraints {\n            write!(f, r#\"<h3 class=\"part-subtitle\">\"#)?;\n            self.generate_tokens(f, constraints, Flags::LINKIFY | Flags::EXPAND_TRAIT)?;\n            write!(f, \"</h3>\")?;\n        }\n        write!(f, \"</hgroup>\")?;\n        if let Part::Type(ty) = part {\n            if let Some(impls) = &ty.impls {\n                self.generate_impls(f, impls)?;\n            }\n        }\n        info.groups\n            .iter()\n            .try_for_each(|group| self.generate_group(f, group, &info))\n    }\n\n    fn build_part_info(&self, part: &'a Part) -> PartInfo<'a> {\n        match part {\n            Part::Mod(m) => self.build_part_info_for_mod(m),\n            Part::Type(t) => self.build_part_info_for_type(t),\n        }\n    }\n\n    fn build_part_info_for_mod(&self, m: &'a Mod) -> PartInfo<'a> {\n        let path: Vec<_> = m.path.split(\"::\").collect();\n        let url = build_path_url(self.base, &path);\n        PartInfo {\n            title: &m.name,\n            base_url: url,\n            constraints: None,\n            groups: &m.groups,\n            fn_type: FunctionType::Function,\n        }\n    }\n\n    fn build_part_info_for_type(&self, t: &'a Type) -> PartInfo<'a> {\n        let ty = parse_type(&t.ty);\n        // Unwrap references\n        let mut inner = &ty;\n        loop {\n            let mut iter = inner.0.iter().filter(|token| !token.is_whitespace_only());\n            let next_token = match iter.next() {\n                Some(Token::Primitive(Primitive::Ref(_))) => iter.next(),\n                _ => break,\n            };\n            inner = match next_token {\n                Some(Token::Type(inner)) => inner,\n                _ => unreachable!(\"unexpected token after ref: {:?}\", next_token),\n            };\n        }\n        // Use the first token as the source of base url for this part\n        let first_token = inner.0.first().expect(\"empty inner\");\n        let url = match first_token {\n            Token::Identifier(ident) => match self.references.get(ident) {\n                Some(r) => r.url.clone(),\n                None => unreachable!(\"unknown name: {}\", ident),\n            },\n            Token::Primitive(primitive) => self.get_primitive_url(primitive),\n            _ => unreachable!(\"unexpected token inside type: {}\", first_token),\n        };\n        let constraints =\n            t.constraints.as_ref().map(|constraints| {\n                match parser::parse_constraints(constraints.as_str()) {\n                    Ok(tokens) => tokens,\n                    Err(_) => unreachable!(\"failed to parse: {}\", constraints),\n                }\n            });\n        PartInfo {\n            title: &t.ty,\n            base_url: url,\n            constraints,\n            groups: &t.groups,\n            fn_type: FunctionType::Method,\n        }\n    }\n\n    fn generate_impls(&self, f: &mut Formatter, impls: &[String]) -> Result {\n        write!(f, r#\"<ul class=\"type-impls\">\"#)?;\n        for impl_item in impls.iter() {\n            let parsed = match parser::parse_impl(impl_item) {\n                Ok(tokens) => tokens,\n                Err(_) => unreachable!(\"failed to parse impl: {}\", impl_item),\n            };\n            write!(f, \"<li>impl \")?;\n            self.generate_tokens(f, &parsed, Flags::LINKIFY)?;\n            write!(f, \"</li>\")?;\n        }\n        write!(f, \"</ul>\")?;\n        Ok(())\n    }\n\n    fn generate_group(&self, f: &mut Formatter, group: &Group, part_info: &PartInfo) -> Result {\n        if let Some(name) = &group.name {\n            write!(f, r#\"<h3 class=\"group-title\">{}</h3>\"#, escape(name))?;\n        }\n        write!(f, r#\"<ul class=\"group-list\">\"#)?;\n        group\n            .items\n            .iter()\n            .try_for_each(|item| self.generate_item(f, item, part_info))?;\n        write!(f, \"</ul>\")?;\n        Ok(())\n    }\n\n    fn generate_item(&self, f: &mut Formatter, item: &InputItem, part_info: &PartInfo) -> Result {\n        let parsed = ParsedItem::parse(item.content())\n            .map_err(|_| format!(\"failed to parse `{}`\", item.content()))\n            .unwrap();\n        let kind = match part_info.fn_type {\n            FunctionType::Function => \"fn\",\n            FunctionType::Method => {\n                if parsed.takes_self {\n                    \"method\"\n                } else {\n                    \"fn\"\n                }\n            }\n        };\n        write!(f, r#\"<li class=\"item item-{}\">\"#, kind)?;\n        write!(f, r#\"<span class=\"prefix-fn\">fn </span>\"#)?;\n        let url = match part_info.fn_type {\n            FunctionType::Function => format!(\"fn.{}.html\", parsed.name),\n            FunctionType::Method => match item.trait_impl() {\n                Some(trait_impl) => format!(\"#impl-{}\", escape(trait_impl)),\n                None => format!(\"#method.{}\", parsed.name),\n            },\n        };\n        write!(\n            f,\n            r#\"<a href=\"{}{}\" class=\"{}\">{}</a>\"#,\n            part_info.base_url, url, kind, parsed.name\n        )?;\n        self.generate_tokens(f, &parsed.tokens, Flags::LINKIFY | Flags::EXPAND_TRAIT)?;\n        write!(f, \"</li>\")?;\n        Ok(())\n    }\n\n    fn generate_tokens(&self, f: &mut Formatter, tokens: &TokenStream<'_>, flags: Flags) -> Result {\n        tokens.0.iter().try_for_each(|token| match token {\n            Token::Text(text) => write!(f, \"{}\", escape(text)),\n            Token::Where => write!(f, r#\"<span class=\"where\">where</span>\"#),\n            Token::Identifier(ident) => self.generate_identifier(f, ident, flags),\n            Token::AssocType(ty) => write!(f, r#\"<span class=\"assoc-type\">{}</span>\"#, ty),\n            Token::Primitive(primitive) => self.generate_primitive(f, primitive, flags),\n            Token::Range(range) => self.generate_range(f, *range, flags),\n            Token::Type(ty) => self.generate_type(f, ty, flags),\n            Token::Nested(nested) => {\n                write!(f, r#\"<span class=\"nested\">\"#)?;\n                self.generate_tokens(f, nested, flags)?;\n                write!(f, \"</span>\")\n            }\n        })\n    }\n\n    fn generate_type(&self, f: &mut Formatter, tokens: &TokenStream<'_>, flags: Flags) -> Result {\n        if !flags.contains(Flags::EXPAND_TRAIT) {\n            return self.generate_tokens(f, tokens, flags);\n        }\n        let matched = self.trait_impls.iter().find_map(|trait_impl| {\n            match tokens.matches(&trait_impl.pat, trait_impl.generic) {\n                Ok(replacement) => Some((trait_impl, replacement)),\n                Err(()) => None,\n            }\n        });\n        let (trait_impl, replacement) = match matched {\n            Some(matched) => matched,\n            None => return self.generate_tokens(f, tokens, flags),\n        };\n        write!(f, r#\"<span class=\"trait-matched\">\"#)?;\n        self.generate_tokens(f, tokens, flags & !(Flags::LINKIFY | Flags::EXPAND_TRAIT))?;\n        write!(f, r#\"<aside class=\"impls\">\"#)?;\n        let flags = flags & !Flags::EXPAND_TRAIT;\n        write!(f, r#\"<h4 class=\"impls-title\">\"#)?;\n        self.generate_tokens(f, tokens, flags)?;\n        write!(f, r#\"</h4><ul class=\"impls-list\">\"#)?;\n        trait_impl.impls.iter().try_for_each(|ty| {\n            let replaced = match (trait_impl.generic, replacement) {\n                (Some(generic), Some(replacement)) => {\n                    Some(build_tokens_with_replacement(ty, generic, replacement))\n                }\n                _ => None,\n            };\n            let ty = replaced.as_ref().unwrap_or(ty);\n            write!(f, \"<li>\")?;\n            self.generate_tokens(f, ty, flags)?;\n            write!(f, \"</li>\")?;\n            Ok(())\n        })?;\n        write!(f, \"</ul></aside></span>\")?;\n        Ok(())\n    }\n\n    fn generate_identifier(&self, f: &mut Formatter, ident: &str, flags: Flags) -> Result {\n        match self.references.get(ident) {\n            Some(r) => {\n                let kind = r.kind.to_str();\n                if flags.contains(Flags::LINKIFY) {\n                    write!(f, r#\"<a href=\"{}\" class=\"{}\">{}</a>\"#, r.url, kind, ident)\n                } else {\n                    write!(f, r#\"<span class=\"{}\">{}</span>\"#, kind, ident)\n                }\n            }\n            None => write!(f, \"{}\", ident),\n        }\n    }\n\n    fn generate_primitive(\n        &self,\n        f: &mut Formatter,\n        primitive: &Primitive<'_>,\n        flags: Flags,\n    ) -> Result {\n        if flags.contains(Flags::LINKIFY) {\n            let url = self.get_primitive_url(primitive);\n            write!(\n                f,\n                r#\"<a href=\"{}\" class=\"primitive\">{}</a>\"#,\n                url, primitive,\n            )\n        } else {\n            write!(f, r#\"<span class=\"primitive\">{}</span>\"#, primitive)\n        }\n    }\n\n    fn get_primitive_url(&self, primitive: &Primitive<'_>) -> String {\n        let name = match primitive {\n            Primitive::SliceStart | Primitive::SliceEnd => \"slice\",\n            Primitive::TupleStart | Primitive::TupleEnd => \"tuple\",\n            Primitive::Unit => \"unit\",\n            Primitive::Ref(_) => \"reference\",\n            Primitive::Ptr(_) => \"pointer\",\n            Primitive::Named(name) => name,\n        };\n        let std_url = self.base.get_url_for(\"std\").unwrap();\n        format!(\"{}primitive.{}.html\", std_url, name)\n    }\n\n    fn generate_range(&self, f: &mut Formatter, range: RangeToken, flags: Flags) -> Result {\n        if flags.contains(Flags::LINKIFY) {\n            let name = match range {\n                RangeToken::Range => \"Range\",\n                RangeToken::RangeFrom => \"RangeFrom\",\n                RangeToken::RangeFull => \"RangeFull\",\n                RangeToken::RangeInclusive => \"RangeInclusive\",\n                RangeToken::RangeTo => \"RangeTo\",\n                RangeToken::RangeToInclusive => \"RangeToInclusive\",\n            };\n            write!(\n                f,\n                r#\"<a href=\"{}ops/struct.{}.html\">{}</a>\"#,\n                self.base.get_url_for(\"std\").unwrap(),\n                name,\n                range\n            )\n        } else {\n            write!(f, \"{}\", range)\n        }\n    }\n}\n\nfn parse_type(ty: &str) -> TokenStream<'_> {\n    parser::parse_type(ty)\n        .map_err(|_| format!(\"failed to parse `{}`\", ty))\n        .unwrap()\n}\n\nfn build_type_url(base: &BaseUrlMap, path: &[&str], kind: Kind, name: &str) -> String {\n    let mut url = build_path_url(base, path);\n    write!(url, \"{}.{}.html\", kind.to_str(), name).unwrap();\n    url\n}\n\nfn build_path_url(base: &BaseUrlMap, path: &[&str]) -> String {\n    let (crate_name, path) = path.split_first().expect(\"zero-length path\");\n    let mut url = base\n        .get_url_for(crate_name)\n        .expect(\"unknown crate\")\n        .to_string();\n    for s in path.iter() {\n        url.push_str(s);\n        url.push('/');\n    }\n    url\n}\n\nfn build_tokens_with_replacement<'a>(\n    tokens: &'a TokenStream<'a>,\n    generic: &str,\n    replacement: &'a TokenStream<'a>,\n) -> TokenStream<'a> {\n    tokens\n        .0\n        .iter()\n        .map(|token| match token {\n            Token::Type(nested) => Token::Type(match nested.0.as_slice() {\n                [Token::Identifier(ident)] if *ident == generic => replacement.clone(),\n                _ => build_tokens_with_replacement(nested, generic, replacement),\n            }),\n            Token::Nested(nested) => {\n                Token::Nested(build_tokens_with_replacement(nested, generic, replacement))\n            }\n            _ => token.clone(),\n        })\n        .collect()\n}\n\n#[derive(Debug)]\nstruct Reference {\n    kind: Kind,\n    url: String,\n}\n\nstruct PartInfo<'a> {\n    title: &'a str,\n    base_url: String,\n    constraints: Option<TokenStream<'a>>,\n    groups: &'a [Group],\n    fn_type: FunctionType,\n}\n\nfn parse_path(s: &str) -> (Box<[&str]>, &str) {\n    let mut path: Vec<_> = s.split(\"::\").collect();\n    let name = path.pop().unwrap();\n    let path = path.into_boxed_slice();\n    (path, name)\n}\n\n#[derive(Clone, Copy)]\nenum FunctionType {\n    Function,\n    Method,\n}\n\nbitflags! {\n    struct Flags: u8 {\n        /// Linkify identifiers and symbols when possible\n        const LINKIFY = 0b0001;\n        /// Expand trait to list of types when available\n        const EXPAND_TRAIT = 0b0010;\n    }\n}\n"
  },
  {
    "path": "src/parser.rs",
    "content": "use crate::token::{Primitive, RangeToken, Token, TokenStream};\nuse combine::error::StringStreamError;\nuse combine::parser::{\n    char::{alpha_num, char, letter, spaces, string},\n    choice::{choice, optional},\n    combinator::attempt,\n    range::recognize,\n    repeat::{many, skip_many1},\n    Parser,\n};\nuse either_n::{Either2, Either3, Either7};\nuse std::iter;\n\npub struct ParsedItem<'a> {\n    pub takes_self: bool,\n    pub name: &'a str,\n    pub tokens: TokenStream<'a>,\n}\n\nimpl<'a> ParsedItem<'a> {\n    pub fn parse(input: &'a str) -> Result<Self, ()> {\n        let parser = (optional(string(\"::\")), identifier_str(), item_after_name());\n        parse(parser, input).map(|(prefix, name, rest)| ParsedItem {\n            takes_self: prefix.is_none(),\n            name,\n            tokens: TokenStream(rest.collect()),\n        })\n    }\n}\n\npub fn parse_constraints(input: &str) -> Result<TokenStream<'_>, ()> {\n    parse(where_clause(), input).map(Iterator::collect)\n}\n\npub fn parse_type(input: &str) -> Result<TokenStream<'_>, ()> {\n    parse(single_type_like_token(), input).map(|token| match token {\n        Token::Type(inner) => inner,\n        _ => unreachable!(),\n    })\n}\n\npub fn parse_impl(input: &str) -> Result<TokenStream<'_>, ()> {\n    let parser = chain2(\n        single_type_like(),\n        optional_tokens(chain2(lex(\"=>\"), sep1_by_lex(assoc_type_param, \",\"))),\n    );\n    parse(parser, input).map(Iterator::collect)\n}\n\npub fn parse_trait_impl(input: &str) -> Result<TokenStream<'_>, ()> {\n    let parser = chain2(\n        single_type_like(),\n        optional_tokens(chain2(lex(\"=>\"), sep1_by_lex(assoc_type_param, \",\"))),\n    );\n    parse(parser, input).map(Iterator::collect)\n}\n\n// TODO: Replace this macro with named existential type when it's available.\n// See https://github.com/rust-lang/rust/issues/34511\nmacro_rules! parser_str_to_iter_token {\n    ($a:lifetime) => {\n        parser_str_to!($a, impl Iterator<Item = Token<$a>>)\n    };\n}\n\nmacro_rules! parser_str_to {\n    ($a:lifetime, $ty:ty) => {\n        impl Parser<&$a str, Output = $ty>\n    }\n}\n\nfn parse<'a, T>(mut parser: parser_str_to!('a, T), input: &'a str) -> Result<T, ()> {\n    parser\n        .parse(input)\n        .map_err(|_| ())\n        .and_then(|(result, remaining)| match remaining {\n            \"\" => Ok(result),\n            _ => Err(()),\n        })\n}\n\nfn item_after_name<'a>() -> parser_str_to_iter_token!('a) {\n    chain5(\n        lex(\"(\"),\n        nested_type_like_list(),\n        lex(\")\"),\n        optional_tokens(chain2(lex(\"->\"), single_type_like())),\n        optional_tokens(where_clause()),\n    )\n}\n\nfn where_clause<'a>() -> parser_str_to_iter_token!('a) {\n    chain2(\n        wrap(\"where\", Token::Where),\n        sep1_by_lex(single_where_constraint, \",\"),\n    )\n}\n\nfn single_where_constraint<'a>() -> parser_str_to_iter_token!('a) {\n    chain3(\n        single_type_like(),\n        lex(\":\"),\n        sep1_by_lex(simple_named_type, \"+\"),\n    )\n}\n\ntype BoxedTokenIter<'a> = Box<dyn Iterator<Item = Token<'a>> + 'a>;\n\n// Add an extra wrapper for this parser so that it can be invoked recursively.\nparser! {\n    fn type_like['a]()(&'a str) -> BoxedTokenIter<'a> where [] {\n        type_like_inner()\n    }\n}\n\nfn type_like_inner<'a>() -> parser_str_to!('a, BoxedTokenIter<'a>) {\n    sep1_by_lex(single_type_like, \"|\").map(to_boxed_iter)\n}\n\n// Add an extra wrapper for this parser so that we don't have too deep type name.\nparser! {\n    fn single_type_like['a]()(&'a str) -> BoxedTokenIter<'a> where [] {\n        single_type_like_inner()\n    }\n}\n\nfn single_type_like_inner<'a>() -> parser_str_to!('a, BoxedTokenIter<'a>) {\n    single_type_like_token().map(iter::once).map(to_boxed_iter)\n}\n\nfn to_boxed_iter<'a, T>(iter: impl Iterator<Item = T> + 'a) -> Box<dyn Iterator<Item = T> + 'a> {\n    Box::new(iter)\n}\n\nfn single_type_like_token<'a>() -> parser_str_to!('a, Token<'a>) {\n    to_type_token(choice((\n        attempt(ref_type()).map(Either7::One),\n        attempt(ptr_type()).map(Either7::Two),\n        attempt(slice_type()).map(Either7::Three),\n        attempt(fn_type()).map(Either7::Four),\n        attempt(tuple_type()).map(Either7::Five),\n        attempt(range_type()).map(Either7::Six),\n        named_type().map(Either7::Seven),\n    )))\n}\n\nfn ref_type<'a>() -> parser_str_to_iter_token!('a) {\n    chain3(\n        recognize((\n            char('&'),\n            optional(string(\"mut\")),\n            optional(attempt((spaces(), lifetime()))),\n        ))\n        .map(|s| iter::once(Token::Primitive(Primitive::Ref(s)))),\n        maybe_spaces(),\n        single_type_like(),\n    )\n}\n\nfn ptr_type<'a>() -> parser_str_to_iter_token!('a) {\n    chain3(\n        recognize((char('*'), choice((string(\"const\"), string(\"mut\")))))\n            .map(|s| iter::once(Token::Primitive(Primitive::Ptr(s)))),\n        maybe_spaces(),\n        single_type_like(),\n    )\n}\n\nfn slice_type<'a>() -> parser_str_to_iter_token!('a) {\n    chain3(\n        wrap_start(\"[\", Primitive::SliceStart),\n        type_like(),\n        wrap_end(\"]\", Primitive::SliceEnd),\n    )\n}\n\nfn fn_type<'a>() -> parser_str_to_iter_token!('a) {\n    chain4(\n        text((char('('), spaces())),\n        nested_type_like_list(),\n        text((spaces(), char(')'), spaces(), string(\"->\"), spaces())),\n        type_like(),\n    )\n}\n\nfn tuple_type<'a>() -> parser_str_to_iter_token!('a) {\n    choice((\n        attempt(wrap(\"()\", Primitive::Unit)).map(Either2::One),\n        chain3(\n            wrap_start(\"(\", Primitive::TupleStart),\n            choice((\n                attempt(chain2(\n                    type_like(),\n                    text((spaces(), char(','), spaces(), string(\"...\"), spaces())),\n                ))\n                .map(|tokens| Either2::One(iter::once(Token::Nested(tokens.collect())))),\n                nested_type_like_list().map(Either2::Two),\n            )),\n            wrap_end(\")\", Primitive::TupleEnd),\n        )\n        .map(Either2::Two),\n    ))\n}\n\nfn nested_type_like_list<'a>() -> parser_str_to_iter_token!('a) {\n    optional(\n        sep1_by_lex(type_like, \",\")\n            .map(Iterator::collect)\n            .map(Token::Nested),\n    )\n    .map(IntoIterator::into_iter)\n}\n\nfn range_type<'a>() -> parser_str_to_iter_token!('a) {\n    (\n        optional(named_type()),\n        choice((attempt(lex_str(\"..=\")), attempt(lex_str(\"..\")))),\n        optional(named_type()),\n    )\n        .and_then(|(start, op, end)| {\n            let range = match (&start, op.trim(), &end) {\n                (None, \"..\", None) => RangeToken::RangeFull,\n                (None, \"..\", Some(_)) => RangeToken::RangeTo,\n                (None, \"..=\", Some(_)) => RangeToken::RangeToInclusive,\n                (Some(_), \"..\", None) => RangeToken::RangeFrom,\n                (Some(_), \"..\", Some(_)) => RangeToken::Range,\n                (Some(_), \"..=\", Some(_)) => RangeToken::RangeInclusive,\n                _ => return Err(StringStreamError::UnexpectedParse),\n            };\n            let start = start.into_iter().flatten();\n            let end = end.into_iter().flatten();\n            Ok(iter::empty()\n                .chain(start)\n                .chain(range_token(op, range))\n                .chain(end))\n        })\n}\n\nfn range_token(s: &str, range: RangeToken) -> impl Iterator<Item = Token<'_>> {\n    let start = match &s[..s.len() - s.trim_start().len()] {\n        \"\" => None,\n        spaces => Some(Token::Text(spaces)),\n    };\n    let end = match &s[s.trim_end().len()..] {\n        \"\" => None,\n        spaces => Some(Token::Text(spaces)),\n    };\n    iter::empty()\n        .chain(start)\n        .chain(iter::once(Token::Range(range)))\n        .chain(end)\n}\n\nfn named_type<'a>() -> parser_str_to_iter_token!('a) {\n    chain4(\n        optional_tokens(lex(\"dyn \")),\n        simple_named_type(),\n        // Associated items\n        many::<TokenStream<'_>, _, _>(attempt(chain2(\n            lex(\"::\"),\n            identifier_str().map(Token::AssocType).map(iter::once),\n        ))),\n        // Additional bounds\n        optional_tokens(chain2(lex(\"+\"), sep1_by_lex(simple_named_type, \"+\"))),\n    )\n}\n\n// Add an extra wrapper for this parser so that we don't have too deep type name.\nparser! {\n    fn simple_named_type['a]()(&'a str) -> BoxedTokenIter<'a> where [] {\n        simple_named_type_inner()\n    }\n}\n\nfn simple_named_type_inner<'a>() -> parser_str_to!('a, BoxedTokenIter<'a>) {\n    chain2(\n        // Name\n        identifier_str().map(|ident| {\n            iter::once(if is_primitive(ident) {\n                Token::Primitive(Primitive::Named(ident))\n            } else {\n                Token::Identifier(ident)\n            })\n        }),\n        // Optional parameters\n        optional_tokens(chain3(\n            lex(\"<\"),\n            sep1_by_lex(type_param, \",\"),\n            text((spaces(), char('>'))),\n        )),\n    )\n    .map(|ty| to_boxed_iter(iter::once(Token::Type(ty.collect()))))\n}\n\nfn to_type_token<'a>(inner: parser_str_to_iter_token!('a)) -> parser_str_to!('a, Token<'a>) {\n    inner.map(|ty| {\n        let mut inner: Vec<_> = ty.collect();\n        match inner.as_ref() as &[_] {\n            [Token::Type(_)] => inner.remove(0),\n            _ => Token::Type(TokenStream(inner)),\n        }\n    })\n}\n\n#[rustfmt::skip]\nfn is_primitive(ident: &str) -> bool {\n    matches!(\n        ident,\n        \"bool\" | \"char\" | \"str\" |\n        \"i8\" | \"i16\" | \"i32\" | \"i64\" | \"i128\" | \"isize\" |\n        \"u8\" | \"u16\" | \"u32\" | \"u64\" | \"u128\" | \"usize\"\n    )\n}\n\nfn type_param<'a>() -> parser_str_to_iter_token!('a) {\n    choice((\n        attempt(lifetime_param()).map(Either3::One),\n        attempt(assoc_type_param()).map(Either3::Two),\n        type_like().map(Either3::Three),\n    ))\n}\n\nfn lifetime_param<'a>() -> parser_str_to_iter_token!('a) {\n    text(lifetime())\n}\n\nfn assoc_type_param<'a>() -> parser_str_to_iter_token!('a) {\n    chain3(\n        identifier_str().map(Token::AssocType).map(iter::once),\n        lex(\"=\"),\n        type_like(),\n    )\n}\n\nfn optional_tokens<'a>(inner: parser_str_to_iter_token!('a)) -> parser_str_to_iter_token!('a) {\n    optional(attempt(inner))\n        .map(IntoIterator::into_iter)\n        .map(Iterator::flatten)\n}\n\nfn sep1_by_lex<'a, P, I>(\n    parser_fn: impl Fn() -> P,\n    sep: &'static str,\n) -> parser_str_to_iter_token!('a)\nwhere\n    P: Parser<&'a str, Output = I>,\n    I: Iterator<Item = Token<'a>>,\n{\n    chain2(\n        parser_fn(),\n        many::<TokenStream<'a>, _, _>(attempt(chain2(lex(sep), parser_fn()))),\n    )\n}\n\nfn lex<'a>(s: &'static str) -> parser_str_to_iter_token!('a) {\n    text(lex_str(s))\n}\n\nfn lex_str<'a>(s: &'static str) -> parser_str_to!('a, &'a str) {\n    recognize((spaces(), string(s), spaces()))\n}\n\nfn wrap_start<'a>(\n    inner: &'static str,\n    token: impl Into<Token<'a>>,\n) -> parser_str_to_iter_token!('a) {\n    let token = token.into();\n    chain2(\n        string(inner).map(move |_| iter::once(token.clone())),\n        maybe_spaces(),\n    )\n}\n\nfn wrap_end<'a>(inner: &'static str, token: impl Into<Token<'a>>) -> parser_str_to_iter_token!('a) {\n    let token = token.into();\n    chain2(\n        maybe_spaces(),\n        string(inner).map(move |_| iter::once(token.clone())),\n    )\n}\n\nfn wrap<'a>(inner: &'static str, token: impl Into<Token<'a>>) -> parser_str_to_iter_token!('a) {\n    let token = token.into();\n    chain3(\n        maybe_spaces(),\n        string(inner).map(move |_| iter::once(token.clone())),\n        maybe_spaces(),\n    )\n}\n\nfn maybe_spaces<'a>() -> parser_str_to_iter_token!('a) {\n    recognize(spaces()).map(|s| match s {\n        \"\" => None.into_iter(),\n        s => Some(Token::Text(s)).into_iter(),\n    })\n}\n\nfn text<'a>(inner: impl Parser<&'a str>) -> parser_str_to_iter_token!('a) {\n    text_token(inner).map(iter::once)\n}\n\nfn text_token<'a>(inner: impl Parser<&'a str>) -> impl Parser<&'a str, Output = Token<'a>> {\n    recognize(inner).map(Token::Text)\n}\n\nfn lifetime<'a>() -> parser_str_to!('a, &'a str) {\n    recognize((char('\\''), skip_many1(letter())))\n}\n\nfn identifier_str<'a>() -> parser_str_to!('a, &'a str) {\n    recognize(skip_many1(choice((alpha_num(), char('_')))))\n}\n\nmacro_rules! impl_chain {\n    ($name:ident: $($v:ident)+) => {\n        fn $name<'a>($(\n            $v: parser_str_to!('a, impl IntoIterator<Item = Token<'a>>),\n        )+) -> parser_str_to_iter_token!('a) {\n            ($($v),+).map(|($($v),+)| {\n                iter::empty() $(.chain($v.into_iter()))+\n            })\n        }\n    }\n}\n\nimpl_chain!(chain2: a b);\nimpl_chain!(chain3: a b c);\nimpl_chain!(chain4: a b c d);\nimpl_chain!(chain5: a b c d e);\n\n#[cfg(test)]\nmod tests {\n    use combine::Parser;\n    use pretty_assertions::assert_eq;\n\n    macro_rules! test {\n        ($parser:ident: [$($input:literal => [$($expected:tt)*],)*]) => {\n            #[test]\n            fn $parser() {\n                $(\n                    let (tokens, remaining) = super::$parser().parse($input)\n                        .expect(\"failed to parse\");\n                    assert_eq!(remaining, \"\", \"unparsed content\");\n                    assert_eq!(tokens.collect::<Vec<_>>(), tokens!($($expected)*));\n                )*\n            }\n        };\n    }\n\n    test!(item_after_name: [\n        \" ((T) -> ())\" => [\" (\" { ^[\"(\" { ^T } \") -> \" @()] } \")\"],\n        \" ((&T) -> bool) -> (B, B) where B: Default + Extend<T>\" => [\n            \" (\" { ^[\"(\" { ^[&\"\" ^T] } \") -> \" @bool] } \") \" \"-> \" @( ^B \", \" ^B )\n            \" \" where \" \" ^B \": \" ^Default \" + \" ^[ Extend \"<\" ^T \">\" ]\n        ],\n        \" (S, T) -> S where S: Default + Clone, Tz::Offset: Display\" => [\n            \" (\" { ^S \", \" ^T } \") \" \"-> \" ^S \" \" where \" \"\n            ^S \": \" ^Default \" + \" ^Clone \", \" ^[ ^Tz \"::\" +Offset ] \": \" ^Display\n        ],\n    ]);\n\n    test!(type_like: [\n        // Named\n        \"Foo\" => [^Foo],\n        \"Option<Foo>\" => [^[Option \"<\" ^Foo \">\"]],\n        \"Foo::Err\" => [^[^Foo \"::\" +Err]],\n        \"Box<dyn Foo>\" => [^[Box \"<\" ^[\"dyn \" ^Foo] \">\"]],\n        \"Iterator<Item = T> + Add<Rhs = Self> + Clone\" => [\n            ^[^[Iterator \"<\" +Item \" = \" ^T \">\"] \" + \" ^[Add \"<\" +Rhs \" = \" ^Self \">\"] \" + \" ^Clone]\n        ],\n        // References\n        \"&Foo\" => [^[&\"\" ^Foo]],\n        \"&'a Foo\" => [^[&\"'a\" \" \" ^Foo]],\n        \"&mut Foo\" => [^[&\"mut\" \" \" ^Foo]],\n        \"&mut 'a Foo\" => [^[&\"mut 'a\" \" \" ^Foo]],\n        \"&[Foo]\" => [^[&\"\" @[^Foo]]],\n        \"&dyn Foo\" => [^[&\"\" ^[\"dyn \" ^Foo]]],\n        // Pointers\n        \"*const Foo\" => [^[*\"const\" \" \" ^Foo]],\n        \"*mut Foo\" => [^[*\"mut\" \" \" ^Foo]],\n        \"*const [Foo]\" => [^[*\"const\" \" \" @[^Foo]]],\n        // Tuple-like\n        \"()\" => [@()],\n        \"(Foo, &Bar)\" => [@(^Foo \", \" ^[&\"\" ^Bar])],\n        \"(Foo, ...)\" => [@(^Foo \", ...\")],\n        // Range\n        \"usize.. usize\" => [^[@usize ~Range \" \" @usize]],\n        \"usize..=usize\" => [^[@usize ~RangeInclusive @usize]],\n        \"     .. usize\" => [^[\"     \" ~RangeTo \" \" @usize]],\n        \"     ..=usize\" => [^[\"     \" ~RangeToInclusive @usize]],\n        \"usize..      \" => [^[@usize ~RangeFrom \"      \"]],\n        \"     ..      \" => [^[\"     \" ~RangeFull \"      \"]],\n        // Function\n        \"() -> Foo\" => [^[\"(\" \") -> \" ^Foo]],\n        \"(Iterator<Item = T>) -> Result<(), T>\" => [\n            ^[\"(\" { ^[Iterator \"<\" +Item \" = \" ^T \">\"] } \") -> \" ^[Result \"<\" @() \", \" ^T \">\"]]\n        ],\n        \"(Foo, &(Bar, &mut 'a [Baz])) -> T\" => [\n            ^[\"(\" { ^Foo \", \" ^[&\"\" @(^Bar \", \" ^[&\"mut 'a\" \" \" @[^Baz]])] } \") -> \" ^T]\n        ],\n        // Union (pseudo-type)\n        \"Foo | &Bar<T> | (Baz) -> bool\" => [\n            ^Foo \" | \" ^[&\"\" ^[Bar \"<\" ^T \">\"]] \" | \" ^[\"(\" { ^Baz } \") -> \" @bool]\n        ],\n    ]);\n}\n"
  },
  {
    "path": "src/template.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>{title} - Rust cheat sheet</title>\n<link rel=\"stylesheet\" href=\"style.css\">\n<link rel=\"stylesheet\" id=\"theme\" href=\"theme-light.css\">\n<script src=\"script.js\"></script>\n<body>\n{content}\n<footer>\n  <ul>\n    <li><a href=\"?dark\">Dark</a>\n    <li><a href=\"?single\">Single</a>\n    <li><a href=\"?large\">Large</a>\n    <li><a href=\"https://github.com/upsuper/rust-cheatsheet\">GitHub</a>\n  </ul>\n</footer>\n</body>\n"
  },
  {
    "path": "src/token.rs",
    "content": "use std::fmt::{self, Display, Write as _};\nuse std::iter::FromIterator;\n\n#[derive(Clone, Debug, Default, Eq, PartialEq)]\npub struct TokenStream<'a>(pub Vec<Token<'a>>);\n\nimpl<'a> TokenStream<'a> {\n    pub fn matches(\n        &'a self,\n        pat: &TokenStream<'_>,\n        generic: Option<&str>,\n    ) -> Result<Option<&'a TokenStream<'a>>, ()> {\n        let mut replacement = None;\n        if tokens_match(self, pat, generic, &mut replacement) {\n            Ok(replacement)\n        } else {\n            Err(())\n        }\n    }\n}\n\nfn tokens_match<'a>(\n    tokens: &'a TokenStream<'a>,\n    pat: &TokenStream<'_>,\n    generic: Option<&str>,\n    replacement: &mut Option<&'a TokenStream<'a>>,\n) -> bool {\n    tokens\n        .0\n        .iter()\n        .zip(pat.0.iter())\n        .all(|(token, pat)| match (token, pat) {\n            (Token::Where, Token::Where) => true,\n            (Token::Identifier(this), Token::Identifier(pat)) => this == pat,\n            (Token::Primitive(this), Token::Primitive(pat)) => this == pat,\n            (Token::Range(this), Token::Range(pat)) => this == pat,\n            (Token::AssocType(this), Token::AssocType(pat)) => this == pat,\n            (Token::Nested(this), Token::Nested(pat)) => {\n                tokens_match(this, pat, generic, replacement)\n            }\n            (Token::Text(this), Token::Text(pat)) => this\n                .split_ascii_whitespace()\n                .zip(pat.split_ascii_whitespace())\n                .all(|(this, pat)| this == pat),\n            (Token::Type(this), Token::Type(pat)) => match (pat.0.as_slice(), generic) {\n                ([Token::Identifier(ident)], Some(generic)) if *ident == generic => {\n                    if let Some(replacement) = replacement {\n                        tokens_match(this, replacement, None, &mut None)\n                    } else {\n                        *replacement = Some(this);\n                        true\n                    }\n                }\n                _ => tokens_match(this, pat, generic, replacement),\n            },\n            _ => false,\n        })\n}\n\nimpl Display for TokenStream<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        self.0.iter().try_for_each(|token| write!(f, \"{}\", token))\n    }\n}\n\nimpl<'a> FromIterator<Token<'a>> for TokenStream<'a> {\n    fn from_iter<I: IntoIterator<Item = Token<'a>>>(iter: I) -> Self {\n        TokenStream(Vec::from_iter(iter))\n    }\n}\n\nimpl<'a> IntoIterator for TokenStream<'a> {\n    type Item = Token<'a>;\n    type IntoIter = <Vec<Token<'a>> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.into_iter()\n    }\n}\n\nimpl<'a> Extend<Token<'a>> for TokenStream<'a> {\n    fn extend<I: IntoIterator<Item = Token<'a>>>(&mut self, iter: I) {\n        self.0.extend(iter);\n    }\n}\n\nimpl<'a, Iter> Extend<Iter> for TokenStream<'a>\nwhere\n    Iter: IntoIterator<Item = Token<'a>>,\n{\n    fn extend<I: IntoIterator<Item = Iter>>(&mut self, iter: I) {\n        self.0.extend(iter.into_iter().flatten())\n    }\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum Token<'a> {\n    Text(&'a str),\n    Nested(TokenStream<'a>),\n    Type(TokenStream<'a>),\n    Primitive(Primitive<'a>),\n    Identifier(&'a str),\n    AssocType(&'a str),\n    Range(RangeToken),\n    Where,\n}\n\nimpl Token<'_> {\n    pub fn is_whitespace_only(&self) -> bool {\n        match self {\n            Token::Text(text) => text.trim().is_empty(),\n            _ => false,\n        }\n    }\n}\n\nimpl<'a> From<Primitive<'a>> for Token<'a> {\n    fn from(primitive: Primitive<'a>) -> Self {\n        Token::Primitive(primitive)\n    }\n}\n\nimpl Display for Token<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match self {\n            Token::Text(s) | Token::Identifier(s) | Token::AssocType(s) => f.write_str(s),\n            Token::Nested(inner) | Token::Type(inner) => write!(f, \"{}\", inner),\n            Token::Primitive(p) => write!(f, \"{}\", p),\n            Token::Range(r) => write!(f, \"{}\", r),\n            Token::Where => f.write_str(\"where\"),\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum Primitive<'a> {\n    Ref(&'a str),\n    Ptr(&'a str),\n    SliceStart,\n    SliceEnd,\n    TupleStart,\n    TupleEnd,\n    Unit,\n    Named(&'a str),\n}\n\nimpl Display for Primitive<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match self {\n            Primitive::Ref(s) | Primitive::Ptr(s) | Primitive::Named(s) => f.write_str(s),\n            Primitive::SliceStart => f.write_char('['),\n            Primitive::SliceEnd => f.write_char(']'),\n            Primitive::TupleStart => f.write_char('('),\n            Primitive::TupleEnd => f.write_char(')'),\n            Primitive::Unit => f.write_str(\"()\"),\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum RangeToken {\n    Range,\n    RangeFrom,\n    RangeFull,\n    RangeInclusive,\n    RangeTo,\n    RangeToInclusive,\n}\n\nimpl Display for RangeToken {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(match self {\n            RangeToken::Range\n            | RangeToken::RangeFrom\n            | RangeToken::RangeFull\n            | RangeToken::RangeTo => \"..\",\n            RangeToken::RangeInclusive | RangeToken::RangeToInclusive => \"..=\",\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::Token;\n    use crate::parser::parse_type;\n\n    #[test]\n    fn token_stream_matches() {\n        fn check_match(\n            pat: &str,\n            generic: Option<&str>,\n            cases: &[(&str, Result<Option<&[Token<'_>]>, ()>)],\n        ) {\n            let pat = parse_type(pat).unwrap();\n            for (ty, expected) in cases.iter() {\n                let ty = parse_type(ty).unwrap();\n                let actual = ty.matches(&pat, generic);\n                let expected = match expected {\n                    Ok(Some([Token::Type(tokens)])) => Ok(Some(tokens)),\n                    Ok(None) => Ok(None),\n                    Err(()) => Err(()),\n                    _ => unreachable!(\"unexpected `expected`: `{:?}`\", expected),\n                };\n                assert_eq!(actual, expected);\n            }\n        }\n        check_match(\n            \"Try<Ok = T>\",\n            Some(\"T\"),\n            &[\n                (\"Try <Ok=usize>\", Ok(Some(&tokens!(@usize)))),\n                (\n                    \"Try <Ok= Option<T> >\",\n                    Ok(Some(&tokens!(^[Option \"<\" ^T \">\"]))),\n                ),\n                (\n                    \"Try<Ok= () -> Option<T> >\",\n                    Ok(Some(&tokens!(^[\"(\" \") -> \" ^[Option \"<\" ^T \">\"]]))),\n                ),\n                (\"Try<Err = T>\", Err(())),\n                (\"Result<Ok = T>\", Err(())),\n                (\"&Try<Ok = T>\", Err(())),\n            ],\n        );\n        check_match(\n            \"SliceIndex<[T]>\",\n            Some(\"T\"),\n            &[\n                (\"SliceIndex<[usize]>\", Ok(Some(&tokens!(@usize)))),\n                (\"SliceIndex<[()]>\", Ok(Some(&tokens!(@())))),\n                (\"SliceIndex<[[T]]>\", Ok(Some(&tokens!(@[^T])))),\n                (\"SliceIndex<T>\", Err(())),\n            ],\n        );\n        check_match(\n            \"RangeBounds<usize>\",\n            None,\n            &[\n                (\"RangeBounds<usize>\", Ok(None)),\n                (\"RangeBounds < usize >\", Ok(None)),\n                (\"RangeBounds<u8>\", Err(())),\n            ],\n        );\n    }\n}\n"
  },
  {
    "path": "static/script.js",
    "content": "const root = document.documentElement;\nconst args = location.search.slice(1).split(',').filter(arg => !!arg);\nfor (const arg of args) {\n  switch (arg) {\n    case 'dark':\n      document.getElementById('theme').href = 'theme-dark.css';\n      break;\n    case 'large':\n    case 'single':\n      root.classList.add(arg);\n      break;\n    default:\n      console.warn(`Unknown argument ${arg}`);\n  }\n}\n\nwindow.addEventListener(\"DOMContentLoaded\", () => {\n  const footer = document.querySelector('footer');\n  const modeSwitches = footer.querySelectorAll('li > a[href^=\"?\"]');\n  for (const a of modeSwitches) {\n    const mode = a.getAttribute('href').slice(1);\n    if (args.includes(mode)) {\n      a.parentNode.classList.add('on');\n      a.href = '?' + args.filter(arg => arg !== mode).join(',');\n    } else {\n      a.href = '?' + [...args, mode].join(',');\n    }\n  }\n}, { once: true });\n"
  },
  {
    "path": "static/style.css",
    "content": "@import url(https://cdn.jsdelivr.net/gh/tonsky/FiraCode@2/distr/fira_code.css);\n\nbody {\n  font: 16px/1.5 'Fira Code', monospace;\n  --opacity: 1;\n}\nmain {\n  position: absolute;\n  width: auto;\n  display: flex;\n  white-space: pre;\n  transform-origin: 0 0;\n  transform: scale(0.5);\n}\n.single main {\n  display: unset;\n}\n.large main {\n  transform: unset;\n}\na {\n  text-decoration: none;\n  color: inherit;\n}\na:hover {\n  text-decoration: underline;\n}\nul {\n  list-style: none;\n}\nul, li {\n  padding: 0;\n  margin: 0;\n}\n.section {\n  padding: 0 2em 5em;\n}\n.part-title-group {\n  margin: 1.5em 0 1em;\n}\n.part-title {\n  font-size: 2em;\n  line-height: normal;\n}\n.part-title, .part-subtitle {\n  margin: 0;\n}\n.part-subtitle {\n  font-size: 1.5em;\n}\n.group-title {\n  font: inherit;\n  margin: 1em 0 0;\n}\n.group-title::before {\n  content: \"// \";\n}\n.group-list, .type-impls {\n  margin-bottom: 1em;\n}\n.group-list {\n  padding-left: 2em;\n}\n.item::before {\n  content: \"=> \";\n  margin-left: -2em;\n}\n.item-fn::before {\n  content: \":: \";\n}\n.type-impls a, .group-list a {\n  font-weight: 500;\n}\n.prefix-fn {\n  margin-left: -1.8em;\n  opacity: 0;\n  -moz-user-select: none;\n  user-select: none;\n}\n.nested { --opacity: 0.6; }\n.nested .nested { --opacity: 0.4; }\n.trait-matched {\n  position: relative;\n  display: inline-block;\n  cursor: pointer;\n  outline: 0 none;\n}\n.trait-matched:hover .impls {\n  visibility: visible;\n}\n.impls {\n  visibility: hidden;\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1;\n  cursor: auto;\n  pointer-events: none;\n  padding-bottom: 2em;\n  --opacity: 1;\n}\n.impls .nested { --opacity: 0.6; }\n.impls .nested .nested { --opacity: 0.4; }\n.impls-title {\n  font: inherit;\n  margin: 0;\n  width: max-content;\n  pointer-events: auto;\n}\n.impls-list {\n  padding: 2px 10px;\n  margin: 0 -10px;\n  border: 1px solid;\n  border-radius: 5px;\n  width: max-content;\n  pointer-events: auto;\n}\nfooter {\n  font-size: 0.6em;\n  position: fixed;\n  bottom: 16px;\n  right: 16px;\n  border: 1px solid;\n  border-radius: 5px;\n  opacity: .8;\n}\nfooter ul {\n  display: flex;\n}\nfooter li:first-child {\n  margin-left: 1px;\n}\nfooter li a {\n  display: block;\n  padding: 5px .5em;\n  border-radius: 5px;\n  margin-right: 1px;\n}\nfooter li:last-child {\n  border-left: 1px solid;\n}\nfooter a:hover {\n  text-decoration: none;\n}\n"
  },
  {
    "path": "static/theme-dark.css",
    "content": "body, .impls h4, .impls ul {\n  color: #ddd;\n  background: #353535;\n}\n.group-title {\n  color: #8d8d8b;\n}\nmain a, main span { color: rgba(221, 221, 221, var(--opacity)); }\n.method, .fn { color: rgba(43, 171, 99, var(--opacity)); }\n.trait { color: rgba(183, 140, 242, var(--opacity)); }\n.enum { color: rgba(130, 176, 137, var(--opacity)); }\n.primitive { color: rgba(67, 174, 199, var(--opacity)); }\n.struct { color: rgba(45, 191, 184, var(--opacity)); }\n.type, .assoc-type { color: rgba(255, 127, 0, var(--opacity)); }\n.union { color: rgba(166, 174, 55, var(--opacity)); }\n.where { color: rgba(221, 221, 221, var(--opacity)); }\nfooter {\n  background: #353535;\n  color: #ccc;\n}\nfooter, footer li:last-child {\n  border-color: #999;\n}\nfooter li.on a {\n  color: #333;\n  background: #ccc;\n}\n"
  },
  {
    "path": "static/theme-light.css",
    "content": "body, .impls h4, .impls ul {\n  color: black;\n  background: white;\n}\n.group-title {\n  color: #8e908c;\n}\nmain a, main span { color: rgba(0, 0, 0, var(--opacity)); }\n.method, .fn { color: rgba(154, 110, 49, var(--opacity)); }\n.trait { color: rgba(124, 90, 243, var(--opacity)); }\n.enum { color: rgba(80, 129, 87, var(--opacity)); }\n.primitive { color: rgba(44, 128, 147, var(--opacity)); }\n.struct { color: rgba(173, 68, 142, var(--opacity)); }\n.type, .assoc-type { color: rgba(186, 93, 0, var(--opacity)); }\n.union { color: rgba(118, 123, 39, var(--opacity)); }\n.where { color: rgba(78, 76, 76, var(--opacity)); }\nfooter {\n  background: white;\n  color: #999;\n}\nfooter, footer li:last-child {\n  border-color: #ccc;\n}\nfooter li.on a {\n  color: #333;\n  background: #ccc;\n}\n"
  }
]