mirror of
https://github.com/starship/starship.git
synced 2026-06-24 02:01:36 +07:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b05177713 | |||
| ff2d7d415f | |||
| 2f75c0ed34 | |||
| a765690f30 | |||
| cd75be40f3 | |||
| 1111230289 | |||
| b78f271fee | |||
| 682fa5f312 | |||
| 671f23140e | |||
| 2ae09e226a | |||
| 91efd25889 | |||
| 49cf2b016f | |||
| dc60c13b0d | |||
| fea4e5155a | |||
| f595899021 | |||
| 2b03c4e721 | |||
| 3dd8c14144 | |||
| 033f20b461 | |||
| da264890e3 | |||
| 57bb99bd0d | |||
| 9a17d3a4e2 | |||
| f28f7791a9 | |||
| 4197977efe | |||
| a496165316 | |||
| 166d7bb30a | |||
| 528332ee61 | |||
| cfd5e7619e | |||
| 26ce2ccf4c | |||
| 712210ecfe | |||
| 3df4a67591 | |||
| dc5c898dd1 | |||
| eb54ec12c8 | |||
| 831cffbb22 | |||
| dacc8e9408 | |||
| db39a6539d | |||
| 96c1f90eeb | |||
| 05ee20141e | |||
| 0d8ca602ad | |||
| 7499e283b3 | |||
| 54e861221c | |||
| 14a5f870af | |||
| e521f39078 | |||
| 17cf5317eb | |||
| 3ebbeae1a7 | |||
| 5162c4159b | |||
| b76e16c38c | |||
| 7d9cbeb5b7 | |||
| 41f2030db1 | |||
| e1d8dea865 | |||
| fe0296bc8f | |||
| ffe59d6787 | |||
| b85b7b9c52 | |||
| d455255e7b | |||
| d0e246802c | |||
| a54b62bfb1 | |||
| e5a1f9c33d | |||
| ed582dd038 | |||
| cd0b573326 | |||
| 10daba00c0 | |||
| c9bacab812 | |||
| 30c7f513d3 | |||
| bea2843088 | |||
| 6a2096608e | |||
| 387c2f6e28 |
+2
-2
@@ -25,9 +25,9 @@
|
|||||||
"target/"
|
"target/"
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"https://github.com/dprint/dprint-plugin-typescript/releases/download/0.95.15/plugin.wasm",
|
"https://github.com/dprint/dprint-plugin-typescript/releases/download/0.96.1/plugin.wasm",
|
||||||
"https://github.com/dprint/dprint-plugin-json/releases/download/0.21.3/plugin.wasm",
|
"https://github.com/dprint/dprint-plugin-json/releases/download/0.21.3/plugin.wasm",
|
||||||
"https://github.com/dprint/dprint-plugin-markdown/releases/download/0.21.1/plugin.wasm",
|
"https://github.com/dprint/dprint-plugin-markdown/releases/download/0.22.1/plugin.wasm",
|
||||||
"https://github.com/dprint/dprint-plugin-toml/releases/download/0.7.0/plugin.wasm"
|
"https://github.com/dprint/dprint-plugin-toml/releases/download/0.7.0/plugin.wasm"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,27 @@ Closes #
|
|||||||
<!--- Please describe in detail how you tested your changes. -->
|
<!--- Please describe in detail how you tested your changes. -->
|
||||||
<!--- Include details of your testing environment, tests ran to see how -->
|
<!--- Include details of your testing environment, tests ran to see how -->
|
||||||
<!--- your change affects other areas of the code, etc. -->
|
<!--- your change affects other areas of the code, etc. -->
|
||||||
|
<!--- Most changes do not need to be tested on multiple operating systems. -->
|
||||||
- [ ] I have tested using **MacOS**
|
- [ ] I have tested using **MacOS**
|
||||||
- [ ] I have tested using **Linux**
|
- [ ] I have tested using **Linux**
|
||||||
- [ ] I have tested using **Windows**
|
- [ ] I have tested using **Windows**
|
||||||
|
|
||||||
|
#### AI-Assistance
|
||||||
|
<!-- Per our AI Policy (AI_POLICY.md), all AI usage must be declared. -->
|
||||||
|
Have you used AI-assistance to author this PR?
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] No
|
||||||
|
|
||||||
|
If **yes**, describe the scope of assistance:
|
||||||
|
<!--- Describe how you used AI-Assistance -->
|
||||||
|
<!--- Disclosure is required if you have used AI-assistance -->
|
||||||
|
<!--- For example: answering project questions, writing tests, implementation, or documentation -->
|
||||||
|
N/A
|
||||||
|
|
||||||
#### Checklist:
|
#### Checklist:
|
||||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||||
- [ ] I have updated the documentation accordingly.
|
- [ ] I have updated the documentation accordingly.
|
||||||
- [ ] I have updated the tests accordingly.
|
- [ ] I have updated the tests accordingly.
|
||||||
|
- [ ] I understand and have read the code I contribute and can answer questions about it.
|
||||||
|
|||||||
@@ -1076,9 +1076,7 @@
|
|||||||
"detect_files": [
|
"detect_files": [
|
||||||
"pom.xml"
|
"pom.xml"
|
||||||
],
|
],
|
||||||
"detect_folders": [
|
"detect_folders": []
|
||||||
".mvn"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"memory_usage": {
|
"memory_usage": {
|
||||||
@@ -1211,7 +1209,10 @@
|
|||||||
".nvmrc",
|
".nvmrc",
|
||||||
"!bunfig.toml",
|
"!bunfig.toml",
|
||||||
"!bun.lock",
|
"!bun.lock",
|
||||||
"!bun.lockb"
|
"!bun.lockb",
|
||||||
|
"!deno.json",
|
||||||
|
"!deno.jsonc",
|
||||||
|
"!deno.lock"
|
||||||
],
|
],
|
||||||
"detect_folders": [
|
"detect_folders": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
@@ -4846,9 +4847,7 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"default": [
|
"default": []
|
||||||
".mvn"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -5203,7 +5202,10 @@
|
|||||||
".nvmrc",
|
".nvmrc",
|
||||||
"!bunfig.toml",
|
"!bunfig.toml",
|
||||||
"!bun.lock",
|
"!bun.lock",
|
||||||
"!bun.lockb"
|
"!bun.lockb",
|
||||||
|
"!deno.json",
|
||||||
|
"!deno.jsonc",
|
||||||
|
"!deno.lock"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"detect_folders": {
|
"detect_folders": {
|
||||||
|
|||||||
@@ -135,5 +135,16 @@
|
|||||||
],
|
],
|
||||||
datasourceTemplate: 'crate',
|
datasourceTemplate: 'crate',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
customType: 'regex',
|
||||||
|
managerFilePatterns: [
|
||||||
|
'/^(workflow-templates|\\.github\\/workflows)\\/[^/]+\\.ya?ml$/',
|
||||||
|
],
|
||||||
|
matchStrings: [
|
||||||
|
'ziglang==(?<currentValue>\\S+)',
|
||||||
|
],
|
||||||
|
datasourceTemplate: 'pypi',
|
||||||
|
depNameTemplate: 'ziglang',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
- name: Docs | Format
|
- name: Docs | Format
|
||||||
uses: dprint/check@9cb3a2b17a8e606d37aae341e49df3654933fc23 # v2.3
|
uses: dprint/check@9cb3a2b17a8e606d37aae341e49df3654933fc23 # v2.3
|
||||||
|
|
||||||
@@ -22,9 +22,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
- name: Install | Taplo
|
- name: Install | Taplo
|
||||||
uses: taiki-e/install-action@7a4939c09608b2a1986b484eca1d16fd0db8ebef # v2.75.5
|
uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10
|
||||||
with:
|
with:
|
||||||
tool: taplo-cli@0.10.0
|
tool: taplo-cli@0.10.0
|
||||||
- name: Presets | Validate with schema
|
- name: Presets | Validate with schema
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref != 'i18n_master' }}
|
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref != 'i18n_master' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Prevent File Change
|
- name: Prevent File Change
|
||||||
@@ -64,12 +64,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Node
|
- name: Setup | Node
|
||||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version: 24.15.0
|
node-version: 24.16.0
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: docs/package-lock.json
|
cache-dependency-path: docs/package-lock.json
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Lint | Run shellcheck
|
- name: Lint | Run shellcheck
|
||||||
run: shellcheck --severity=warning install/**/*.sh
|
run: shellcheck --severity=warning install/**/*.sh
|
||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Install shfmt
|
- name: Setup | Install shfmt
|
||||||
run: |
|
run: |
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Test | Piped execution with curl
|
- name: Test | Piped execution with curl
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Node
|
- name: Setup | Node
|
||||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version: 24.15.0
|
node-version: 24.16.0
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: docs/package-lock.json
|
cache-dependency-path: docs/package-lock.json
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: Release
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
env:
|
env:
|
||||||
CARGO_INCREMENTAL: 0
|
CARGO_INCREMENTAL: 0
|
||||||
CARGO_NET_RETRY: 10
|
CARGO_NET_RETRY: 10
|
||||||
@@ -58,6 +58,7 @@ jobs:
|
|||||||
- target: riscv64gc-unknown-linux-musl
|
- target: riscv64gc-unknown-linux-musl
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
name: starship-riscv64gc-unknown-linux-musl.tar.gz
|
name: starship-riscv64gc-unknown-linux-musl.tar.gz
|
||||||
|
use_zigbuild: true
|
||||||
|
|
||||||
- target: x86_64-apple-darwin
|
- target: x86_64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
@@ -92,7 +93,7 @@ jobs:
|
|||||||
RUSTFLAGS: ${{ matrix.rustflags || '' }}
|
RUSTFLAGS: ${{ matrix.rustflags || '' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }} --no-self-update
|
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }} --no-self-update
|
||||||
@@ -106,19 +107,33 @@ jobs:
|
|||||||
RUSTFLAGS: ""
|
RUSTFLAGS: ""
|
||||||
|
|
||||||
- name: Setup | Install cross [Linux]
|
- name: Setup | Install cross [Linux]
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest' && !matrix.use_zigbuild
|
||||||
uses: taiki-e/install-action@7a4939c09608b2a1986b484eca1d16fd0db8ebef # v2.75.5
|
uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10
|
||||||
with:
|
with:
|
||||||
tool: cross@0.2.5
|
tool: cross@0.2.5
|
||||||
|
|
||||||
|
- name: Setup | Install Zig [riscv64]
|
||||||
|
if: matrix.use_zigbuild
|
||||||
|
run: pip install ziglang==0.16.0
|
||||||
|
|
||||||
|
- name: Setup | Install cargo-zigbuild [riscv64]
|
||||||
|
if: matrix.use_zigbuild
|
||||||
|
uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10
|
||||||
|
with:
|
||||||
|
tool: cargo-zigbuild
|
||||||
|
|
||||||
- name: Build | Build [Cargo]
|
- name: Build | Build [Cargo]
|
||||||
if: matrix.os != 'ubuntu-latest'
|
if: matrix.os != 'ubuntu-latest'
|
||||||
run: cargo build --release --locked --target ${{ matrix.target }}
|
run: cargo build --release --locked --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Build | Build [Cross]
|
- name: Build | Build [Cross]
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest' && !matrix.use_zigbuild
|
||||||
run: cross build --release --locked --target ${{ matrix.target }}
|
run: cross build --release --locked --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Build | Build [Zigbuild]
|
||||||
|
if: matrix.use_zigbuild
|
||||||
|
run: cargo zigbuild --release --locked --target ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Build | Installer [Windows]
|
- name: Build | Installer [Windows]
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
@@ -141,7 +156,7 @@ jobs:
|
|||||||
- name: Sign | Sign [Windows]
|
- name: Sign | Sign [Windows]
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
uses: signpath/github-action-submit-signing-request@bc66d86b015a46e9c6d9700de73143a82f9570ff # v2.1
|
uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2
|
||||||
with:
|
with:
|
||||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||||
organization-id: '${{ vars.SIGNPATH_ORGANIZATION_ID }}'
|
organization-id: '${{ vars.SIGNPATH_ORGANIZATION_ID }}'
|
||||||
@@ -204,10 +219,10 @@ jobs:
|
|||||||
STARSHIP_VERSION: ${{ needs.release_please.outputs.tag_name }}
|
STARSHIP_VERSION: ${{ needs.release_please.outputs.tag_name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
# Required to include the recently merged Crowdin PR
|
# Required to include the recently merged Crowdin PR
|
||||||
ref: master
|
ref: main
|
||||||
|
|
||||||
- name: Notarize | Set up secrets
|
- name: Notarize | Set up secrets
|
||||||
env:
|
env:
|
||||||
@@ -241,9 +256,9 @@ jobs:
|
|||||||
xcrun notarytool store-credentials "$KEYCHAIN_ENTRY" --team-id "$APPLEID_TEAMID" --apple-id "$APPLEID_USERNAME" --password "$APPLEID_PASSWORD" --keychain "$KEYCHAIN_PATH"
|
xcrun notarytool store-credentials "$KEYCHAIN_ENTRY" --team-id "$APPLEID_TEAMID" --apple-id "$APPLEID_USERNAME" --password "$APPLEID_PASSWORD" --keychain "$KEYCHAIN_PATH"
|
||||||
|
|
||||||
- name: Setup | Node
|
- name: Setup | Node
|
||||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version: 24.15.0
|
node-version: 24.16.0
|
||||||
|
|
||||||
- name: Notarize | Build docs
|
- name: Notarize | Build docs
|
||||||
run: |
|
run: |
|
||||||
@@ -317,13 +332,13 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
run: rustup toolchain install stable --profile minimal --no-self-update
|
run: rustup toolchain install stable --profile minimal --no-self-update
|
||||||
|
|
||||||
- name: Build | Authenticate
|
- name: Build | Authenticate
|
||||||
uses: rust-lang/crates-io-auth-action@b7e9a28eded4986ec6b1fa40eeee8f8f165559ec # v1.0.3
|
uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe # v1.0.4
|
||||||
id: auth
|
id: auth
|
||||||
|
|
||||||
- name: Build | Publish
|
- name: Build | Publish
|
||||||
@@ -375,7 +390,7 @@ jobs:
|
|||||||
if: ${{ needs.release_please.outputs.release_created == 'true' }}
|
if: ${{ needs.release_please.outputs.release_created == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
- name: Setup | Artifacts
|
- name: Setup | Artifacts
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
- run: pwsh ./install/windows/choco/update.ps1
|
- run: pwsh ./install/windows/choco/update.ps1
|
||||||
@@ -391,7 +406,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
- name: Merge | Merge Crowdin PR
|
- name: Merge | Merge Crowdin PR
|
||||||
run: gh pr merge i18n_master --squash --repo=starship/starship
|
run: gh pr merge i18n_master --squash --repo=starship/starship
|
||||||
env:
|
env:
|
||||||
@@ -403,7 +418,7 @@ jobs:
|
|||||||
needs: merge_crowdin_pr
|
needs: merge_crowdin_pr
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
- name: Trigger workflow dispatch
|
- name: Trigger workflow dispatch
|
||||||
run: gh workflow run publish-docs.yml
|
run: gh workflow run publish-docs.yml
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
- name: Test | Security Audit
|
- name: Test | Security Audit
|
||||||
uses: EmbarkStudios/cargo-deny-action@91bf2b620e09e18d6eb78b92e7861937469acedb # v2.0.17
|
uses: EmbarkStudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2.0.20
|
||||||
with:
|
with:
|
||||||
command: check ${{ matrix.checks }}
|
command: check ${{ matrix.checks }}
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ jobs:
|
|||||||
name: Spell Check with Typos
|
name: Spell Check with Typos
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
- uses: crate-ci/typos@7c572958218557a3272c2d6719629443b5cc26fd # v1.45.2
|
- uses: crate-ci/typos@37bb98842b0d8c4ffebdb75301a13db0267cef89 # v1.47.2
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
run: rustup toolchain install stable --profile minimal --component rustfmt --no-self-update
|
run: rustup toolchain install stable --profile minimal --component rustfmt --no-self-update
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
run: rustup toolchain install stable --profile minimal --component clippy --no-self-update
|
run: rustup toolchain install stable --profile minimal --component clippy --no-self-update
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
run: rustup toolchain install stable --profile minimal --no-self-update
|
run: rustup toolchain install stable --profile minimal --no-self-update
|
||||||
@@ -76,7 +76,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
run: rustup toolchain install stable --profile minimal --no-self-update
|
run: rustup toolchain install stable --profile minimal --no-self-update
|
||||||
@@ -94,7 +94,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
run: rustup toolchain install stable --profile minimal --no-self-update
|
run: rustup toolchain install stable --profile minimal --no-self-update
|
||||||
@@ -114,7 +114,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
run: rustup toolchain install stable --profile minimal --no-self-update
|
run: rustup toolchain install stable --profile minimal --no-self-update
|
||||||
@@ -150,7 +150,7 @@ jobs:
|
|||||||
RUSTFLAGS: ${{ matrix.rustflags || '' }}
|
RUSTFLAGS: ${{ matrix.rustflags || '' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup | Checkout
|
- name: Setup | Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
|
||||||
# Install all the required dependencies for testing
|
# Install all the required dependencies for testing
|
||||||
- name: Setup | Rust
|
- name: Setup | Rust
|
||||||
@@ -162,7 +162,7 @@ jobs:
|
|||||||
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
||||||
|
|
||||||
- name: Install cargo-llvm-cov
|
- name: Install cargo-llvm-cov
|
||||||
uses: taiki-e/install-action@7a4939c09608b2a1986b484eca1d16fd0db8ebef # v2.75.5
|
uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2.81.10
|
||||||
with:
|
with:
|
||||||
tool: cargo-llvm-cov@0.8.5
|
tool: cargo-llvm-cov@0.8.5
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ jobs:
|
|||||||
target/debug/starship-x86_64-pc-windows-msvc.msi
|
target/debug/starship-x86_64-pc-windows-msvc.msi
|
||||||
|
|
||||||
- name: Sign | Sign [Windows]
|
- name: Sign | Sign [Windows]
|
||||||
uses: signpath/github-action-submit-signing-request@bc66d86b015a46e9c6d9700de73143a82f9570ff # v2.1
|
uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
if: matrix.os == 'windows-latest' && matrix.rust == 'stable' && github.event_name == 'push' && github.repository == 'starship/starship'
|
if: matrix.os == 'windows-latest' && matrix.rust == 'stable' && github.event_name == 'push' && github.repository == 'starship/starship'
|
||||||
with:
|
with:
|
||||||
@@ -247,7 +247,7 @@ jobs:
|
|||||||
output-artifact-directory: target/debug
|
output-artifact-directory: target/debug
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
|
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||||
if: github.repository == 'starship/starship'
|
if: github.repository == 'starship/starship'
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# AI Usage Policy
|
||||||
|
|
||||||
|
This policy supplements our [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
|
This project is maintained by volunteers.
|
||||||
|
This policy exists to keep review burden reasonable.
|
||||||
|
The policy is inspired by the [Ghostty](https://github.com/ghostty-org/ghostty/blob/main/AI_POLICY.md) and [LLVM](https://llvm.org/docs/AIToolPolicy.html) AI policies.
|
||||||
|
|
||||||
|
Contributions that violate this policy may be closed without further notice.
|
||||||
|
|
||||||
|
## Mandatory Disclosure
|
||||||
|
|
||||||
|
Every Pull Request that utilizes AI-assisted tooling (including but not limited to Claude Code, Cursor, GitHub Copilot, ChatGPT, or local LLMs) must disclose its usage.
|
||||||
|
|
||||||
|
### PR Description
|
||||||
|
|
||||||
|
You must complete the **AI-Assistance** section in our Pull Request Template.
|
||||||
|
|
||||||
|
## Human-in-the-Loop
|
||||||
|
|
||||||
|
Contributors must fully understand all submitted contributions.
|
||||||
|
|
||||||
|
### Contributions
|
||||||
|
|
||||||
|
- You must be able to explain what your changes do and defend your implementation choices.
|
||||||
|
- You are expected to have read and understood every line of code you submit.
|
||||||
|
- If your response to a maintainer's question is an unedited copy-paste from an LLM, or if you cannot explain the mechanics of your PR, the PR will be closed.
|
||||||
|
|
||||||
|
### Issue Triage and Discussions
|
||||||
|
|
||||||
|
You are not allowed to reply to user issues or discussions with unverified or raw AI-generated information.
|
||||||
|
|
||||||
|
## "Good First Issue" Protections
|
||||||
|
|
||||||
|
You may not submit contributions to close a `🌱 good first issue` if they were authored with substantial AI assistance.
|
||||||
|
These issues are intentionally triaged as learning opportunities for new developers navigating the codebase for the first time.
|
||||||
|
|
||||||
|
## Low-Effort Contributions & Prohibition of Autonomous Agents
|
||||||
|
|
||||||
|
- Contributions that are overly verbose, contain unsupported or hallucinated claims, or otherwise show the hallmarks of low-effort LLM usage may be closed without further notice.
|
||||||
|
- Contributions via OpenClaw, or any other unsupervised autonomous agent operating in an automated loop, are strictly prohibited.
|
||||||
+2
-2
@@ -273,10 +273,10 @@ Once setup is complete, you can refer to VitePress documentation on the actual i
|
|||||||
This is our preferred process for opening a PR on GitHub:
|
This is our preferred process for opening a PR on GitHub:
|
||||||
|
|
||||||
1. Fork this repository
|
1. Fork this repository
|
||||||
2. Create a branch off of `master` for your work: `git checkout -b my-feature-branch`
|
2. Create a branch off of `main` for your work: `git checkout -b my-feature-branch`
|
||||||
3. Make some changes, committing them along the way
|
3. Make some changes, committing them along the way
|
||||||
4. When your changes are ready for review, push your branch: `git push origin my-feature-branch`
|
4. When your changes are ready for review, push your branch: `git push origin my-feature-branch`
|
||||||
5. Create a pull request from your branch to `starship/master`
|
5. Create a pull request from your branch to `starship/main`
|
||||||
6. No need to assign the pull request to anyone, we'll review it when we can
|
6. No need to assign the pull request to anyone, we'll review it when we can
|
||||||
7. When the changes have been reviewed and approved, someone will squash and merge for you
|
7. When the changes have been reviewed and approved, someone will squash and merge for you
|
||||||
|
|
||||||
|
|||||||
Generated
+378
-639
File diff suppressed because it is too large
Load Diff
+18
-18
@@ -35,53 +35,54 @@ config-schema = ["schemars"]
|
|||||||
notify = ["notify-rust"]
|
notify = ["notify-rust"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4.44", default-features = false, features = ["clock", "std", "wasmbind"] }
|
|
||||||
clap = { version = "4.6.1", features = ["derive", "cargo", "unicode"] }
|
clap = { version = "4.6.1", features = ["derive", "cargo", "unicode"] }
|
||||||
clap_complete = "4.6.3"
|
clap_complete = "4.6.5"
|
||||||
clap_complete_nushell = "4.6.0"
|
clap_complete_nushell = "4.6.0"
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
dunce = "1.0.5"
|
dunce = "1.0.5"
|
||||||
# default feature restriction addresses https://github.com/starship/starship/issues/4251
|
# default feature restriction addresses https://github.com/starship/starship/issues/4251
|
||||||
gix = { version = "0.83.0", default-features = false, features = ["max-performance-safe", "revision", "zlib-rs", "status", "sha1"] }
|
gix = { version = "0.84.0", default-features = false, features = ["max-performance-safe", "revision", "zlib-rs", "status", "sha1"] }
|
||||||
indexmap = { version = "2.14.0", features = ["serde"] }
|
indexmap = { version = "2.14.0", features = ["serde"] }
|
||||||
jsonc-parser = { version = "0.32.3", features = ["serde"] }
|
jiff = { version = "0.2.28", features = ["serde"] }
|
||||||
log = { version = "0.4.29", features = ["std"] }
|
jsonc-parser = { version = "0.32.4", features = ["serde"] }
|
||||||
|
log = { version = "0.4.32", features = ["std"] }
|
||||||
# notify-rust is optional (on by default) because the crate doesn't currently build for darwin with nix
|
# notify-rust is optional (on by default) because the crate doesn't currently build for darwin with nix
|
||||||
# see: https://github.com/NixOS/nixpkgs/issues/160876
|
# see: https://github.com/NixOS/nixpkgs/issues/160876
|
||||||
notify-rust = { version = "4.16.1", optional = true }
|
notify-rust = { version = "4.18.0", optional = true }
|
||||||
nu-ansi-term = "0.50.3"
|
nu-ansi-term = "0.50.3"
|
||||||
open = "5.3.4"
|
open = "5.3.5"
|
||||||
# update os module config and tests when upgrading os_info
|
# update os module config and tests when upgrading os_info
|
||||||
os_info = { version = "3.14.0", features = ["schemars"] }
|
os_info = { version = "3.15.0", features = ["schemars"] }
|
||||||
# for efficient shared state between `git_status` and `git_metrics`, allowing parallel printing. This is for poison-free locks.
|
# for efficient shared state between `git_status` and `git_metrics`, allowing parallel printing. This is for poison-free locks.
|
||||||
parking_lot = "0.12.5"
|
parking_lot = "0.12.5"
|
||||||
path-slash = "0.2.1"
|
path-slash = "0.2.1"
|
||||||
pest = "2.8.6"
|
pest = "2.8.6"
|
||||||
pest_derive = "2.8.6"
|
pest_derive = "2.8.6"
|
||||||
process_control = "5.2.0"
|
process_control = "5.2.0"
|
||||||
quick-xml = "0.39.2"
|
quick-xml = "0.40.1"
|
||||||
rand = "0.10.1"
|
rand = "0.10.1"
|
||||||
rayon = "1.12.0"
|
rayon = "1.12.0"
|
||||||
regex = { version = "1.12.3", default-features = false, features = ["perf", "std", "unicode-perl"] }
|
regex = { version = "1.12.4", default-features = false, features = ["perf", "std", "unicode-perl"] }
|
||||||
rust-ini = "0.21.3"
|
rust-ini = "0.21.3"
|
||||||
semver = "1.0.28"
|
semver = "1.0.28"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.150"
|
||||||
sha1 = "0.11.0"
|
sha1 = "0.11.0"
|
||||||
shadow-rs = { version = "2.0.0", default-features = false, features = ["build"] }
|
shadow-rs = { version = "2.0.0", default-features = false, features = ["build"] }
|
||||||
# battery is optional (on by default) because the crate doesn't currently build for Termux
|
# battery is optional (on by default) because the crate doesn't currently build for Termux
|
||||||
# see: https://github.com/svartalf/rust-battery/issues/33
|
# see: https://github.com/svartalf/rust-battery/issues/33
|
||||||
starship-battery = { version = "0.11.0", optional = true }
|
starship-battery = { version = "0.11.0", optional = true }
|
||||||
strsim = "0.11.1"
|
strsim = "0.11.1"
|
||||||
systemstat = "=0.2.6"
|
systemstat = "=0.2.7"
|
||||||
|
tempfile = "3.27.0"
|
||||||
terminal_size = "0.4.4"
|
terminal_size = "0.4.4"
|
||||||
toml = { version = "1.1.2", features = ["preserve_order"] }
|
toml = { version = "1.1.2", features = ["preserve_order"] }
|
||||||
toml_edit = "0.25.11"
|
toml_edit = "0.25.12"
|
||||||
unicode-segmentation = "1.13.2"
|
unicode-segmentation = "1.13.3"
|
||||||
unicode-width = "0.2.2"
|
unicode-width = "0.2.2"
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
versions = "7.0.0"
|
versions = "7.0.0"
|
||||||
which = "8.0.2"
|
which = "8.0.4"
|
||||||
whoami = { version = "2.1.2", default-features = false, features = ["std", "wasi-wasite"] }
|
whoami = { version = "2.1.2", default-features = false, features = ["std", "wasi-wasite"] }
|
||||||
yaml-rust2 = "0.11.0"
|
yaml-rust2 = "0.11.0"
|
||||||
|
|
||||||
@@ -108,7 +109,7 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
nix = { version = "0.31.2", default-features = false, features = ["feature", "fs", "user"] }
|
nix = { version = "0.31.3", default-features = false, features = ["feature", "fs", "user"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "2.0.0", default-features = false, features = ["build"] }
|
shadow-rs = { version = "2.0.0", default-features = false, features = ["build"] }
|
||||||
@@ -118,8 +119,7 @@ dunce = "1.0.5"
|
|||||||
winres = "0.1.12"
|
winres = "0.1.12"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
mockall = "0.14.0"
|
mockall = "=0.14.0"
|
||||||
tempfile = "3.27.0"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img
|
<img
|
||||||
width="400"
|
width="400"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/logo.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/logo.png"
|
||||||
alt="Starship – Cross-shell prompt"
|
alt="Starship – Cross-shell prompt"
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/starship/starship/actions"
|
<a href="https://github.com/starship/starship/actions"
|
||||||
><img
|
><img
|
||||||
src="https://img.shields.io/github/actions/workflow/status/starship/starship/workflow.yml?branch=master&label=workflow&style=flat-square"
|
src="https://img.shields.io/github/actions/workflow/status/starship/starship/workflow.yml?branch=main&label=workflow&style=flat-square"
|
||||||
alt="GitHub Actions workflow status"
|
alt="GitHub Actions workflow status"
|
||||||
/></a>
|
/></a>
|
||||||
<a href="https://crates.io/crates/starship"
|
<a href="https://crates.io/crates/starship"
|
||||||
@@ -48,114 +48,125 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/starship/starship/blob/master/README.md"
|
<a href="https://github.com/starship/starship/blob/main/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-us.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-us.png"
|
||||||
alt="English"
|
alt="English"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/de-DE/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/de-DE/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-de.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-de.png"
|
||||||
alt="Deutsch"
|
alt="Deutsch"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/es-ES/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/es-ES/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-es.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-es.png"
|
||||||
alt="Español"
|
alt="Español"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/fr-FR/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/fr-FR/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-fr.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-fr.png"
|
||||||
alt="Français"
|
alt="Français"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/id-ID/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/id-ID/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-id.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-id.png"
|
||||||
alt="Bahasa Indonesia"
|
alt="Bahasa Indonesia"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/it-IT/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/it-IT/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-it.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-it.png"
|
||||||
alt="Italiano"
|
alt="Italiano"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/ja-JP/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/ja-JP/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-jp.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-jp.png"
|
||||||
alt="日本語"
|
alt="日本語"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/pt-BR/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/pt-BR/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-br.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-br.png"
|
||||||
alt="Português do Brasil"
|
alt="Português do Brasil"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/ru-RU/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/ru-RU/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-ru.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-ru.png"
|
||||||
alt="Русский"
|
alt="Русский"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/uk-UA/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/uk-UA/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-ua.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-ua.png"
|
||||||
alt="Українська"
|
alt="Українська"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/vi-VN/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/vi-VN/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-vn.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-vn.png"
|
||||||
alt="Tiếng Việt"
|
alt="Tiếng Việt"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/zh-CN/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/zh-CN/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-cn.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-cn.png"
|
||||||
alt="简体中文"
|
alt="简体中文"
|
||||||
/></a>
|
/></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/starship/starship/blob/master/docs/zh-TW/guide/README.md"
|
href="https://github.com/starship/starship/blob/main/docs/zh-TW/guide/README.md"
|
||||||
><img
|
><img
|
||||||
height="20"
|
height="20"
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/flag-tw.png"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/flag-tw.png"
|
||||||
alt="繁體中文"
|
alt="繁體中文"
|
||||||
/></a>
|
/></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1></h1>
|
<h1></h1>
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> **The default branch has been renamed from `master` to `main`.**
|
||||||
|
> If you have a local clone, update it by running:
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> git branch -m master main
|
||||||
|
> git fetch origin
|
||||||
|
> git branch -u origin/main main
|
||||||
|
> git remote set-head origin -a
|
||||||
|
> ```
|
||||||
|
|
||||||
<img
|
<img
|
||||||
src="https://raw.githubusercontent.com/starship/starship/master/media/demo.gif"
|
src="https://raw.githubusercontent.com/starship/starship/main/media/demo.gif"
|
||||||
alt="Starship with iTerm2 and the Snazzy theme"
|
alt="Starship with iTerm2 and the Snazzy theme"
|
||||||
width="50%"
|
width="50%"
|
||||||
align="right"
|
align="right"
|
||||||
@@ -414,7 +425,7 @@ We are always looking for contributors of **all skill levels**! If you're lookin
|
|||||||
|
|
||||||
If you are fluent in a non-English language, we greatly appreciate any help keeping our docs translated and up-to-date in other languages. If you would like to help, translations can be contributed on the [Starship Crowdin](https://translate.starship.rs/).
|
If you are fluent in a non-English language, we greatly appreciate any help keeping our docs translated and up-to-date in other languages. If you would like to help, translations can be contributed on the [Starship Crowdin](https://translate.starship.rs/).
|
||||||
|
|
||||||
If you are interested in helping contribute to starship, please take a look at our [Contributing Guide](https://github.com/starship/starship/blob/master/CONTRIBUTING.md). Also, feel free to drop into our [Discord server](https://discord.gg/8Jzqu3T) and say hi. 👋
|
If you are interested in helping contribute to starship, please take a look at our [Contributing Guide](https://github.com/starship/starship/blob/main/CONTRIBUTING.md). Also, feel free to drop into our [Discord server](https://discord.gg/8Jzqu3T) and say hi. 👋
|
||||||
|
|
||||||
## 💭 Inspired By
|
## 💭 Inspired By
|
||||||
|
|
||||||
@@ -443,13 +454,13 @@ This program will not transfer any information to other networked systems unless
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<br>
|
<br>
|
||||||
<img width="100" src="https://raw.githubusercontent.com/starship/starship/master/media/icon.png" alt="Starship rocket icon">
|
<img width="100" src="https://raw.githubusercontent.com/starship/starship/main/media/icon.png" alt="Starship rocket icon">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 📝 License
|
## 📝 License
|
||||||
|
|
||||||
Copyright © 2019-present, [Starship Contributors](https://github.com/starship/starship/graphs/contributors).<br>
|
Copyright © 2019-present, [Starship Contributors](https://github.com/starship/starship/graphs/contributors).<br>
|
||||||
This project is [ISC](https://github.com/starship/starship/blob/master/LICENSE) licensed.
|
This project is [ISC](https://github.com/starship/starship/blob/main/LICENSE) licensed.
|
||||||
|
|
||||||
[alpine linux packages]: https://pkgs.alpinelinux.org/packages?name=starship
|
[alpine linux packages]: https://pkgs.alpinelinux.org/packages?name=starship
|
||||||
[arch linux extra]: https://archlinux.org/packages/extra/x86_64/starship
|
[arch linux extra]: https://archlinux.org/packages/extra/x86_64/starship
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ fn gen_presets_hook(mut file: &File) -> SdResult<()> {
|
|||||||
.and_then(|v| v.strip_suffix(".toml"))
|
.and_then(|v| v.strip_suffix(".toml"))
|
||||||
.expect("Failed to process filename");
|
.expect("Failed to process filename");
|
||||||
presets.push_str(format!("print::Preset(\"{name}\"),\n").as_str());
|
presets.push_str(format!("print::Preset(\"{name}\"),\n").as_str());
|
||||||
match_arms.push_str(format!(r#""{name}" => include_bytes!(r"{full_path}"),"#).as_str());
|
match_arms.push_str(format!(r#""{name}" => include_str!(r"{full_path}"),"#).as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(
|
writeln!(
|
||||||
@@ -51,7 +51,7 @@ pub fn get_preset_list<'a>() -> &'a [print::Preset] {{
|
|||||||
]
|
]
|
||||||
}}
|
}}
|
||||||
|
|
||||||
pub fn get_preset_content(name: &str) -> &[u8] {{
|
pub fn get_preset_content(name: &str) -> &str {{
|
||||||
match name {{
|
match name {{
|
||||||
{match_arms}
|
{match_arms}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
|||||||
@@ -315,6 +315,12 @@ Produces a prompt like the following:
|
|||||||
▶ starship on rprompt [!] is 📦 v0.57.0 via 🦀 v1.54.0 took 17s
|
▶ starship on rprompt [!] is 📦 v0.57.0 via 🦀 v1.54.0 took 17s
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When using `zsh` (v5.0.5+), the shell adds a default trailing space to the right prompt. This can cause alignment issues specifically when using the Starship `$fill` module. To remove this gap, add the following to your `.zshrc`:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
ZLE_RPROMPT_INDENT=0
|
||||||
|
```
|
||||||
|
|
||||||
## Continuation Prompt
|
## Continuation Prompt
|
||||||
|
|
||||||
Some shells support a continuation prompt along with the normal prompt. This prompt is rendered instead of the normal prompt when the user has entered an incomplete statement (such as a single left parenthesis or quote).
|
Some shells support a continuation prompt along with the normal prompt. This prompt is rendered instead of the normal prompt when the user has entered an incomplete statement (such as a single left parenthesis or quote).
|
||||||
|
|||||||
+41
-24
@@ -1824,6 +1824,7 @@ format = '[+$added]($added_style)/[-$deleted]($deleted_style) '
|
|||||||
|
|
||||||
The `gcloud` module shows the current configuration for [`gcloud`](https://cloud.google.com/sdk/gcloud) CLI.
|
The `gcloud` module shows the current configuration for [`gcloud`](https://cloud.google.com/sdk/gcloud) CLI.
|
||||||
This is based on the `~/.config/gcloud/active_config` file and the `~/.config/gcloud/configurations/config_{CONFIG NAME}` file and the `CLOUDSDK_CONFIG` env var.
|
This is based on the `~/.config/gcloud/active_config` file and the `~/.config/gcloud/configurations/config_{CONFIG NAME}` file and the `CLOUDSDK_CONFIG` env var.
|
||||||
|
The `CLOUDSDK_CORE_PROJECT` and `CLOUDSDK_COMPUTE_REGION` environment variables, when set, override the `project` and `region` values from the active configuration, mirroring the behavior of `gcloud` itself.
|
||||||
|
|
||||||
When the module is enabled it will always be active, unless `detect_env_vars` has
|
When the module is enabled it will always be active, unless `detect_env_vars` has
|
||||||
been set in which case the module will only be active when one of the
|
been set in which case the module will only be active when one of the
|
||||||
@@ -3343,12 +3344,13 @@ The module will be shown when inside a nix-shell environment.
|
|||||||
|
|
||||||
### Variables
|
### Variables
|
||||||
|
|
||||||
| Variable | Example | Description |
|
| Variable | Example | Description |
|
||||||
| -------- | ------- | ------------------------------------ |
|
| -------- | ------- | ----------------------------------------------------------------------------- |
|
||||||
| state | `pure` | The state of the nix-shell |
|
| state | `pure` | The state of the nix-shell |
|
||||||
| name | `lorri` | The name of the nix-shell |
|
| name | `lorri` | The name of the nix-shell |
|
||||||
| symbol | | Mirrors the value of option `symbol` |
|
| level | `1` | The depth level of the nix-shell (Only when using [Lix](https://lix.systems)) |
|
||||||
| style\* | | Mirrors the value of option `style` |
|
| symbol | | Mirrors the value of option `symbol` |
|
||||||
|
| style\* | | Mirrors the value of option `style` |
|
||||||
|
|
||||||
*: This variable can only be used as a part of a style string
|
*: This variable can only be used as a part of a style string
|
||||||
|
|
||||||
@@ -3377,7 +3379,7 @@ By default the module will be shown if any of the following conditions are met:
|
|||||||
- The current directory contains a file with the `.js`, `.mjs` or `.cjs` extension
|
- The current directory contains a file with the `.js`, `.mjs` or `.cjs` extension
|
||||||
- The current directory contains a file with the `.ts`, `.mts` or `.cts` extension
|
- The current directory contains a file with the `.ts`, `.mts` or `.cts` extension
|
||||||
|
|
||||||
Additionally, the module will be hidden by default if the directory contains a `bunfig.toml`, `bun.lock`, or `bun.lockb` file, overriding the above conditions.
|
Additionally, the module will be hidden by default if the directory contains a `deno.json`, `deno.jsonc`, `deno.lock`, `bunfig.toml`, `bun.lock`, or `bun.lockb` file, overriding the above conditions.
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
@@ -3856,7 +3858,8 @@ The `pijul_channel` module shows the active channel of the repo in your current
|
|||||||
|
|
||||||
## Pixi
|
## Pixi
|
||||||
|
|
||||||
The `pixi` module shows the installed [pixi](https://pixi.sh) version as well as the activated environment, if `$PIXI_ENVIRONMENT_NAME` is set.
|
The `pixi` module shows the installed [pixi](https://pixi.sh) version as well as the activated
|
||||||
|
environment and project name, if `$PIXI_ENVIRONMENT_NAME` is set.
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> This does not suppress pixi's own prompt modifier, you may want to run `pixi config set shell.change-ps1 false`.
|
> This does not suppress pixi's own prompt modifier, you may want to run `pixi config set shell.change-ps1 false`.
|
||||||
@@ -3878,12 +3881,13 @@ The `pixi` module shows the installed [pixi](https://pixi.sh) version as well as
|
|||||||
|
|
||||||
### Variables
|
### Variables
|
||||||
|
|
||||||
| Variable | Example | Description |
|
| Variable | Example | Description |
|
||||||
| ----------- | --------- | ------------------------------------ |
|
| ------------ | ------------ | ------------------------------------ |
|
||||||
| version | `v0.33.0` | The version of `pixi` |
|
| version | `v0.33.0` | The version of `pixi` |
|
||||||
| environment | `py311` | The current pixi environment |
|
| environment | `py311` | The current pixi environment |
|
||||||
| symbol | | Mirrors the value of option `symbol` |
|
| project_name | `my-project` | The current pixi project name |
|
||||||
| style | | Mirrors the value of option `style` |
|
| symbol | | Mirrors the value of option `symbol` |
|
||||||
|
| style | | Mirrors the value of option `style` |
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -4810,7 +4814,7 @@ format = 'via [$symbol$workspace]($style) '
|
|||||||
## Time
|
## Time
|
||||||
|
|
||||||
The `time` module shows the current **local** time.
|
The `time` module shows the current **local** time.
|
||||||
The `format` configuration value is used by the [`chrono`](https://crates.io/crates/chrono) crate to control how the time is displayed. Take a look [at the chrono strftime docs](https://docs.rs/chrono/0.4.7/chrono/format/strftime/index.html) to see what options are available.
|
The `format` configuration value is used by the [`jiff`](https://crates.io/crates/jiff) crate to control how the time is displayed. Take a look [at the jiff strftime docs](https://docs.rs/jiff/latest/jiff/fmt/strtime/index.html) to see what options are available.
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> This module is disabled by default.
|
> This module is disabled by default.
|
||||||
@@ -4818,15 +4822,15 @@ The `format` configuration value is used by the [`chrono`](https://crates.io/cra
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
| Option | Default | Description |
|
| Option | Default | Description |
|
||||||
| ----------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
| ----------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `format` | `'at [$time]($style) '` | The format string for the module. |
|
| `format` | `'at [$time]($style) '` | The format string for the module. |
|
||||||
| `use_12hr` | `false` | Enables 12 hour formatting |
|
| `use_12hr` | `false` | Enables 12 hour formatting |
|
||||||
| `time_format` | see below | The [chrono format string](https://docs.rs/chrono/0.4.7/chrono/format/strftime/index.html) used to format the time. |
|
| `time_format` | see below | The [jiff format string](https://docs.rs/jiff/latest/jiff/fmt/strtime/index.html) used to format the time. |
|
||||||
| `style` | `'bold yellow'` | The style for the module time |
|
| `style` | `'bold yellow'` | The style for the module time |
|
||||||
| `utc_time_offset` | `'local'` | Sets the UTC offset to use. Range from -24 < x < 24. Allows floats to accommodate 30/45 minute timezone offsets. |
|
| `utc_time_offset` | `'local'` | Sets the UTC offset to use. Either an IANA time zone name or a range from -24 < x < 24. Allows floats to accommodate 30/45 minute timezone offsets. |
|
||||||
| `disabled` | `true` | Disables the `time` module. |
|
| `disabled` | `true` | Disables the `time` module. |
|
||||||
| `time_range` | `'-'` | Sets the time range during which the module will be shown. Times must be specified in 24-hours format |
|
| `time_range` | `'-'` | Sets the time range during which the module will be shown. Times must be specified in 24-hours format |
|
||||||
|
|
||||||
If `use_12hr` is `true`, then `time_format` defaults to `'%r'`. Otherwise, it defaults to `'%T'`.
|
If `use_12hr` is `true`, then `time_format` defaults to `'%r'`. Otherwise, it defaults to `'%T'`.
|
||||||
Manually setting `time_format` will override the `use_12hr` setting.
|
Manually setting `time_format` will override the `use_12hr` setting.
|
||||||
@@ -4842,6 +4846,8 @@ Manually setting `time_format` will override the `use_12hr` setting.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
|
#### With UTC offset
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# ~/.config/starship.toml
|
# ~/.config/starship.toml
|
||||||
|
|
||||||
@@ -4853,6 +4859,17 @@ utc_time_offset = '-5'
|
|||||||
time_range = '10:00:00-14:00:00'
|
time_range = '10:00:00-14:00:00'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### With Timezone name
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# ~/.config/starship.toml
|
||||||
|
|
||||||
|
[time]
|
||||||
|
disabled = false
|
||||||
|
time_format = '%T'
|
||||||
|
utc_time_offset = 'Europe/Berlin'
|
||||||
|
```
|
||||||
|
|
||||||
## Typst
|
## Typst
|
||||||
|
|
||||||
The `typst` module shows the current installed version of Typst used in a project.
|
The `typst` module shows the current installed version of Typst used in a project.
|
||||||
|
|||||||
Generated
+300
-300
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -5,6 +5,6 @@
|
|||||||
"preview": "vitepress preview"
|
"preview": "vitepress preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.6.4"
|
"vitepress": "1.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ impl Default for MavenConfig<'_> {
|
|||||||
recursive: false,
|
recursive: false,
|
||||||
detect_extensions: vec![],
|
detect_extensions: vec![],
|
||||||
detect_files: vec!["pom.xml"],
|
detect_files: vec!["pom.xml"],
|
||||||
detect_folders: vec![".mvn"],
|
detect_folders: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ impl Default for NodejsConfig<'_> {
|
|||||||
"!bunfig.toml",
|
"!bunfig.toml",
|
||||||
"!bun.lock",
|
"!bun.lock",
|
||||||
"!bun.lockb",
|
"!bun.lockb",
|
||||||
|
"!deno.json",
|
||||||
|
"!deno.jsonc",
|
||||||
|
"!deno.lock",
|
||||||
],
|
],
|
||||||
detect_folders: vec!["node_modules"],
|
detect_folders: vec!["node_modules"],
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -1,5 +1,13 @@
|
|||||||
|
use crate::config::Either;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// Wrapper struct to enable serde serialization/deserialization for jiff::tz::TimeZone
|
||||||
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct TimezoneWrapper(
|
||||||
|
#[serde(with = "jiff::fmt::serde::tz::required")] pub jiff::tz::TimeZone,
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "config-schema",
|
feature = "config-schema",
|
||||||
@@ -14,7 +22,8 @@ pub struct TimeConfig<'a> {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub time_format: Option<&'a str>,
|
pub time_format: Option<&'a str>,
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
pub utc_time_offset: &'a str,
|
#[cfg_attr(feature = "config-schema", schemars(with = "String"))]
|
||||||
|
pub utc_time_offset: Either<TimezoneWrapper, &'a str>,
|
||||||
pub time_range: &'a str,
|
pub time_range: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +35,7 @@ impl Default for TimeConfig<'_> {
|
|||||||
use_12hr: false,
|
use_12hr: false,
|
||||||
time_format: None,
|
time_format: None,
|
||||||
disabled: true,
|
disabled: true,
|
||||||
utc_time_offset: "local",
|
utc_time_offset: Either::Second("local"),
|
||||||
time_range: "-",
|
time_range: "-",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-9
@@ -8,8 +8,7 @@ use crate::config::StarshipConfig;
|
|||||||
use crate::configs::PROMPT_ORDER;
|
use crate::configs::PROMPT_ORDER;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use std::fs::File;
|
use std::path::PathBuf;
|
||||||
use std::io::Write;
|
|
||||||
use toml_edit::DocumentMut;
|
use toml_edit::DocumentMut;
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@@ -240,16 +239,17 @@ pub fn get_configuration_edit(context: &Context) -> DocumentMut {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_configuration(context: &Context, doc: &DocumentMut) {
|
pub fn write_configuration(context: &Context, doc: &DocumentMut) {
|
||||||
let config_path = context.get_config_path_os().unwrap_or_else(|| {
|
let Some(config_path) = context.get_config_path_os() else {
|
||||||
eprintln!("config path required to write configuration");
|
eprintln!("config path required to write configuration");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
};
|
||||||
|
|
||||||
let config_str = doc.to_string();
|
let config_path = PathBuf::from(config_path);
|
||||||
|
|
||||||
File::create(config_path)
|
if let Err(e) = crate::utils::write_file_atomic(config_path, doc.to_string(), true) {
|
||||||
.and_then(|mut file| file.write_all(config_str.as_ref()))
|
eprintln!("Unable to write configuration: {e}");
|
||||||
.expect("Error writing starship config");
|
process::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit_configuration(
|
pub fn edit_configuration(
|
||||||
@@ -313,7 +313,11 @@ fn get_editor_internal(visual: Option<String>, editor: Option<String>) -> String
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{fs::create_dir, io, path::PathBuf};
|
use std::{
|
||||||
|
fs::{File, create_dir},
|
||||||
|
io::{self, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use toml_edit::Item;
|
use toml_edit::Item;
|
||||||
|
|||||||
+9
-1
@@ -107,6 +107,9 @@ enum Commands {
|
|||||||
/// Output the preset to a file instead of stdout
|
/// Output the preset to a file instead of stdout
|
||||||
#[clap(short, long, conflicts_with = "list")]
|
#[clap(short, long, conflicts_with = "list")]
|
||||||
output: Option<PathBuf>,
|
output: Option<PathBuf>,
|
||||||
|
/// Forcibly overwrite the output file if it already exists
|
||||||
|
#[clap(short, long, requires = "output")]
|
||||||
|
force: bool,
|
||||||
/// List out all preset names
|
/// List out all preset names
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
list: bool,
|
list: bool,
|
||||||
@@ -251,7 +254,12 @@ fn main() {
|
|||||||
print::module(&module_name, properties);
|
print::module(&module_name, properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Commands::Preset { name, list, output } => print::preset_command(name, output, list),
|
Commands::Preset {
|
||||||
|
name,
|
||||||
|
list,
|
||||||
|
output,
|
||||||
|
force,
|
||||||
|
} => print::preset_command(name, output, force, list),
|
||||||
Commands::Config { name, value } => {
|
Commands::Config { name, value } => {
|
||||||
let context = Context::default();
|
let context = Context::default();
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
|
|||||||
+20
-48
@@ -3,8 +3,8 @@ use std::collections::HashMap;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::DateTime;
|
|
||||||
use ini::Ini;
|
use ini::Ini;
|
||||||
|
use jiff::{Timestamp, Zoned};
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ fn get_credentials_duration(
|
|||||||
.find_map(|env_var| context.get_env(env_var))
|
.find_map(|env_var| context.get_env(env_var))
|
||||||
{
|
{
|
||||||
// get expiration from environment variables
|
// get expiration from environment variables
|
||||||
chrono::DateTime::parse_from_rfc3339(&expiration_date).ok()
|
expiration_date.parse::<Timestamp>().ok()
|
||||||
} else if let Some(section) =
|
} else if let Some(section) =
|
||||||
get_creds(context, aws_creds).and_then(|creds| get_profile_creds(creds, aws_profile))
|
get_creds(context, aws_creds).and_then(|creds| get_profile_creds(creds, aws_profile))
|
||||||
{
|
{
|
||||||
@@ -158,7 +158,7 @@ fn get_credentials_duration(
|
|||||||
expiration_keys
|
expiration_keys
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|expiration_key| section.get(expiration_key))
|
.find_map(|expiration_key| section.get(expiration_key))
|
||||||
.and_then(|expiration| DateTime::parse_from_rfc3339(expiration).ok())
|
.and_then(|expiration| expiration.parse::<Timestamp>().ok())
|
||||||
} else {
|
} else {
|
||||||
// get expiration from cached SSO credentials
|
// get expiration from cached SSO credentials
|
||||||
let config = get_config(context, aws_config)?;
|
let config = get_config(context, aws_config)?;
|
||||||
@@ -172,10 +172,10 @@ fn get_credentials_duration(
|
|||||||
let sso_cred_json: json::Value =
|
let sso_cred_json: json::Value =
|
||||||
json::from_str(&crate::utils::read_file(&sso_cred_path).ok()?).ok()?;
|
json::from_str(&crate::utils::read_file(&sso_cred_path).ok()?).ok()?;
|
||||||
let expires_at = sso_cred_json.get("expiresAt")?.as_str();
|
let expires_at = sso_cred_json.get("expiresAt")?.as_str();
|
||||||
DateTime::parse_from_rfc3339(expires_at?).ok()
|
expires_at?.parse::<Timestamp>().ok()
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
Some(expiration_date.timestamp() - chrono::Local::now().timestamp())
|
Some(expiration_date.as_second() - Zoned::now().timestamp().as_second())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alias_name(name: Option<String>, aliases: &HashMap<String, &str>) -> Option<String> {
|
fn alias_name(name: Option<String>, aliases: &HashMap<String, &str>) -> Option<String> {
|
||||||
@@ -332,6 +332,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
use crate::test::ModuleRenderer;
|
use crate::test::ModuleRenderer;
|
||||||
use nu_ansi_term::Color;
|
use nu_ansi_term::Color;
|
||||||
use std::fs::{File, create_dir};
|
use std::fs::{File, create_dir};
|
||||||
@@ -736,21 +737,16 @@ credential_process = /opt/bin/awscreds-retriever
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expiration_date_set() {
|
fn expiration_date_set() {
|
||||||
use chrono::{DateTime, SecondsFormat, Utc};
|
|
||||||
|
|
||||||
let expiration_env_vars = ["AWS_SESSION_EXPIRATION", "AWS_CREDENTIAL_EXPIRATION"];
|
let expiration_env_vars = ["AWS_SESSION_EXPIRATION", "AWS_CREDENTIAL_EXPIRATION"];
|
||||||
for env_var in expiration_env_vars {
|
for env_var in expiration_env_vars {
|
||||||
let now_plus_half_hour: DateTime<Utc> =
|
let now_plus_half_hour =
|
||||||
DateTime::from_timestamp(chrono::Local::now().timestamp() + 1800, 0).unwrap();
|
Timestamp::from_second(Zoned::now().timestamp().as_second() + 1800).unwrap();
|
||||||
|
|
||||||
let actual = ModuleRenderer::new("aws")
|
let actual = ModuleRenderer::new("aws")
|
||||||
.env("AWS_PROFILE", "astronauts")
|
.env("AWS_PROFILE", "astronauts")
|
||||||
.env("AWS_REGION", "ap-northeast-2")
|
.env("AWS_REGION", "ap-northeast-2")
|
||||||
.env("AWS_ACCESS_KEY_ID", "dummy")
|
.env("AWS_ACCESS_KEY_ID", "dummy")
|
||||||
.env(
|
.env(env_var, now_plus_half_hour.to_string())
|
||||||
env_var,
|
|
||||||
now_plus_half_hour.to_rfc3339_opts(SecondsFormat::Secs, true),
|
|
||||||
)
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let possible_values = [
|
let possible_values = [
|
||||||
@@ -772,16 +768,14 @@ credential_process = /opt/bin/awscreds-retriever
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expiration_date_set_from_file() -> io::Result<()> {
|
fn expiration_date_set_from_file() -> io::Result<()> {
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
|
|
||||||
let dir = tempfile::tempdir()?;
|
let dir = tempfile::tempdir()?;
|
||||||
let credentials_path = dir.path().join("credentials");
|
let credentials_path = dir.path().join("credentials");
|
||||||
let mut file = File::create(&credentials_path)?;
|
let mut file = File::create(&credentials_path)?;
|
||||||
|
|
||||||
let now_plus_half_hour: DateTime<Utc> =
|
let now_plus_half_hour =
|
||||||
DateTime::from_timestamp(chrono::Local::now().timestamp() + 1800, 0).unwrap();
|
Timestamp::from_second(Zoned::now().timestamp().as_second() + 1800).unwrap();
|
||||||
|
|
||||||
let expiration_date = now_plus_half_hour.to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
|
let expiration_date = now_plus_half_hour.to_string();
|
||||||
|
|
||||||
let expiration_keys = ["expiration", "x_security_token_expires"];
|
let expiration_keys = ["expiration", "x_security_token_expires"];
|
||||||
for key in expiration_keys {
|
for key in expiration_keys {
|
||||||
@@ -847,10 +841,7 @@ aws_secret_access_key=dummy
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expiration_date_set_expired() {
|
fn expiration_date_set_expired() {
|
||||||
use chrono::{DateTime, SecondsFormat, Utc};
|
let now = Timestamp::from_second(Zoned::now().timestamp().as_second() - 1800).unwrap();
|
||||||
|
|
||||||
let now: DateTime<Utc> =
|
|
||||||
DateTime::from_timestamp(chrono::Local::now().timestamp() - 1800, 0).unwrap();
|
|
||||||
|
|
||||||
let symbol = "!!!";
|
let symbol = "!!!";
|
||||||
|
|
||||||
@@ -862,10 +853,7 @@ aws_secret_access_key=dummy
|
|||||||
.env("AWS_PROFILE", "astronauts")
|
.env("AWS_PROFILE", "astronauts")
|
||||||
.env("AWS_REGION", "ap-northeast-2")
|
.env("AWS_REGION", "ap-northeast-2")
|
||||||
.env("AWS_ACCESS_KEY_ID", "dummy")
|
.env("AWS_ACCESS_KEY_ID", "dummy")
|
||||||
.env(
|
.env("AWS_SESSION_EXPIRATION", now.to_string())
|
||||||
"AWS_SESSION_EXPIRATION",
|
|
||||||
now.to_rfc3339_opts(SecondsFormat::Secs, true),
|
|
||||||
)
|
|
||||||
.collect();
|
.collect();
|
||||||
let expected = Some(format!(
|
let expected = Some(format!(
|
||||||
"on {}",
|
"on {}",
|
||||||
@@ -1056,8 +1044,6 @@ credential_process = /opt/bin/awscreds-for-tests
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sso_legacy_set() -> io::Result<()> {
|
fn sso_legacy_set() -> io::Result<()> {
|
||||||
use chrono::{DateTime, SecondsFormat, Utc};
|
|
||||||
|
|
||||||
let (module_renderer, dir) = ModuleRenderer::new_with_home("aws")?;
|
let (module_renderer, dir) = ModuleRenderer::new_with_home("aws")?;
|
||||||
std::fs::create_dir_all(dir.path().join(".aws/sso/cache"))?;
|
std::fs::create_dir_all(dir.path().join(".aws/sso/cache"))?;
|
||||||
|
|
||||||
@@ -1080,16 +1066,10 @@ sso_role_name = <AWS-ROLE-NAME>
|
|||||||
.join(".aws/sso/cache/a47a4e57aecc96b31b4f083543924bd6f828e65a.json"),
|
.join(".aws/sso/cache/a47a4e57aecc96b31b4f083543924bd6f828e65a.json"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let one_second_ago: DateTime<Utc> =
|
let one_second_ago =
|
||||||
DateTime::from_timestamp(chrono::Local::now().timestamp() - 1, 0).unwrap();
|
Timestamp::from_second(Zoned::now().timestamp().as_second() - 1).unwrap();
|
||||||
|
|
||||||
file.write_all(
|
file.write_all(format!(r#"{{"expiresAt": "{one_second_ago}"}}"#).as_bytes())?;
|
||||||
format!(
|
|
||||||
r#"{{"expiresAt": "{}"}}"#,
|
|
||||||
one_second_ago.to_rfc3339_opts(SecondsFormat::Secs, true)
|
|
||||||
)
|
|
||||||
.as_bytes(),
|
|
||||||
)?;
|
|
||||||
file.sync_all()?;
|
file.sync_all()?;
|
||||||
|
|
||||||
let actual = module_renderer.collect();
|
let actual = module_renderer.collect();
|
||||||
@@ -1104,8 +1084,6 @@ sso_role_name = <AWS-ROLE-NAME>
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sso_set() -> io::Result<()> {
|
fn sso_set() -> io::Result<()> {
|
||||||
use chrono::{DateTime, SecondsFormat, Utc};
|
|
||||||
|
|
||||||
let (module_renderer, dir) = ModuleRenderer::new_with_home("aws")?;
|
let (module_renderer, dir) = ModuleRenderer::new_with_home("aws")?;
|
||||||
std::fs::create_dir_all(dir.path().join(".aws/sso/cache"))?;
|
std::fs::create_dir_all(dir.path().join(".aws/sso/cache"))?;
|
||||||
|
|
||||||
@@ -1134,16 +1112,10 @@ sso_registration_scopes = sso:account:access
|
|||||||
.join(".aws/sso/cache/7505d64a54e061b7acd54ccd58b49dc43500b635.json"),
|
.join(".aws/sso/cache/7505d64a54e061b7acd54ccd58b49dc43500b635.json"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let one_second_ago: DateTime<Utc> =
|
let one_second_ago =
|
||||||
DateTime::from_timestamp(chrono::Local::now().timestamp() - 1, 0).unwrap();
|
Timestamp::from_second(Zoned::now().timestamp().as_second() - 1).unwrap();
|
||||||
|
|
||||||
cache_file.write_all(
|
cache_file.write_all(format!(r#"{{"expiresAt": "{one_second_ago}"}}"#).as_bytes())?;
|
||||||
format!(
|
|
||||||
r#"{{"expiresAt": "{}"}}"#,
|
|
||||||
one_second_ago.to_rfc3339_opts(SecondsFormat::Secs, true)
|
|
||||||
)
|
|
||||||
.as_bytes(),
|
|
||||||
)?;
|
|
||||||
cache_file.sync_all()?;
|
cache_file.sync_all()?;
|
||||||
|
|
||||||
let actual = module_renderer
|
let actual = module_renderer
|
||||||
|
|||||||
+68
-4
@@ -116,10 +116,17 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||||||
.and_then(|(_, domain)| domain)
|
.and_then(|(_, domain)| domain)
|
||||||
.map(Cow::Borrowed)
|
.map(Cow::Borrowed)
|
||||||
.map(Ok),
|
.map(Ok),
|
||||||
"region" => gcloud_context
|
"region" => context
|
||||||
.get_region()
|
.get_env("CLOUDSDK_COMPUTE_REGION")
|
||||||
.map(|region| config.region_aliases.get(region).copied().unwrap_or(region))
|
.map(Cow::Owned)
|
||||||
.map(Cow::Borrowed)
|
.or_else(|| gcloud_context.get_region().map(Cow::Borrowed))
|
||||||
|
.map(|region| {
|
||||||
|
config
|
||||||
|
.region_aliases
|
||||||
|
.get(region.as_ref())
|
||||||
|
.copied()
|
||||||
|
.map_or(region, Cow::Borrowed)
|
||||||
|
})
|
||||||
.map(Ok),
|
.map(Ok),
|
||||||
"project" => context
|
"project" => context
|
||||||
.get_env("CLOUDSDK_CORE_PROJECT")
|
.get_env("CLOUDSDK_CORE_PROJECT")
|
||||||
@@ -451,6 +458,63 @@ project = very-long-project-name
|
|||||||
dir.close()
|
dir.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn region_set_in_env() -> io::Result<()> {
|
||||||
|
let dir = tempfile::tempdir()?;
|
||||||
|
let active_config_path = dir.path().join("active_config");
|
||||||
|
let mut active_config_file = File::create(active_config_path)?;
|
||||||
|
active_config_file.write_all(b"default")?;
|
||||||
|
|
||||||
|
create_dir(dir.path().join("configurations"))?;
|
||||||
|
let config_default_path = dir.path().join("configurations").join("config_default");
|
||||||
|
let mut config_default_file = File::create(config_default_path)?;
|
||||||
|
config_default_file.write_all(
|
||||||
|
b"\
|
||||||
|
[compute]
|
||||||
|
region = us-central1
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let actual = ModuleRenderer::new("gcloud")
|
||||||
|
.env("CLOUDSDK_COMPUTE_REGION", "europe-west2")
|
||||||
|
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy())
|
||||||
|
.config(toml::toml! {
|
||||||
|
[gcloud]
|
||||||
|
format = "on [$symbol$region]($style) "
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let expected = Some(format!(
|
||||||
|
"on {} ",
|
||||||
|
Color::Blue.bold().paint("☁️ europe-west2")
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
dir.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn region_set_in_env_with_alias() -> io::Result<()> {
|
||||||
|
let dir = tempfile::tempdir()?;
|
||||||
|
let active_config_path = dir.path().join("active_config");
|
||||||
|
let mut active_config_file = File::create(active_config_path)?;
|
||||||
|
active_config_file.write_all(b"default")?;
|
||||||
|
|
||||||
|
let actual = ModuleRenderer::new("gcloud")
|
||||||
|
.env("CLOUDSDK_COMPUTE_REGION", "europe-west2")
|
||||||
|
.env("CLOUDSDK_CONFIG", dir.path().to_string_lossy())
|
||||||
|
.config(toml::toml! {
|
||||||
|
[gcloud]
|
||||||
|
format = "on [$symbol$region]($style) "
|
||||||
|
[gcloud.region_aliases]
|
||||||
|
europe-west2 = "ew2"
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let expected = Some(format!("on {} ", Color::Blue.bold().paint("☁️ ew2")));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
dir.close()
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn region_not_set_with_display_region() -> io::Result<()> {
|
fn region_not_set_with_display_region() -> io::Result<()> {
|
||||||
let dir = tempfile::tempdir()?;
|
let dir = tempfile::tempdir()?;
|
||||||
|
|||||||
+50
-13
@@ -75,24 +75,16 @@ fn get_state_description<'a>(
|
|||||||
current: None,
|
current: None,
|
||||||
total: None,
|
total: None,
|
||||||
}),
|
}),
|
||||||
InProgress::ApplyMailbox => Some(StateDescription {
|
InProgress::ApplyMailbox => Some(describe_rebase_apply(repo, config.am)),
|
||||||
label: config.am,
|
InProgress::ApplyMailboxRebase => Some(describe_rebase_apply(repo, config.am_or_rebase)),
|
||||||
current: None,
|
|
||||||
total: None,
|
|
||||||
}),
|
|
||||||
InProgress::ApplyMailboxRebase => Some(StateDescription {
|
|
||||||
label: config.am_or_rebase,
|
|
||||||
current: None,
|
|
||||||
total: None,
|
|
||||||
}),
|
|
||||||
InProgress::Rebase | InProgress::RebaseInteractive => {
|
InProgress::Rebase | InProgress::RebaseInteractive => {
|
||||||
Some(describe_rebase(repo, config.rebase))
|
Some(describe_rebase_apply(repo, config.rebase))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use future gitoxide API to get the state of the rebase
|
// TODO: Use future gitoxide API to get the state of the rebase
|
||||||
fn describe_rebase<'a>(repo: &'a Repo, rebase_config: &'a str) -> StateDescription<'a> {
|
fn describe_rebase_apply<'a>(repo: &'a Repo, state_label: &'a str) -> StateDescription<'a> {
|
||||||
/*
|
/*
|
||||||
* Sadly, libgit2 seems to have some issues with reading the state of
|
* Sadly, libgit2 seems to have some issues with reading the state of
|
||||||
* interactive rebases. So, instead, we'll poke a few of the .git files
|
* interactive rebases. So, instead, we'll poke a few of the .git files
|
||||||
@@ -134,7 +126,7 @@ fn describe_rebase<'a>(repo: &'a Repo, rebase_config: &'a str) -> StateDescripti
|
|||||||
};
|
};
|
||||||
|
|
||||||
StateDescription {
|
StateDescription {
|
||||||
label: rebase_config,
|
label: state_label,
|
||||||
current,
|
current,
|
||||||
total,
|
total,
|
||||||
}
|
}
|
||||||
@@ -186,6 +178,33 @@ mod tests {
|
|||||||
repo_dir.close()
|
repo_dir.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shows_applying_mailbox_progress() -> io::Result<()> {
|
||||||
|
let repo_dir = create_repo_with_conflict()?;
|
||||||
|
let path = repo_dir.path();
|
||||||
|
let patch_path = path.join("other-branch.patch");
|
||||||
|
let patch =
|
||||||
|
run_git_cmd_with_output(["format-patch", "-1", "other-branch", "--stdout"], path)?;
|
||||||
|
write_file(
|
||||||
|
&patch_path,
|
||||||
|
String::from_utf8(patch.stdout)
|
||||||
|
.map_err(|error| Error::new(ErrorKind::InvalidData, error))?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
run_git_cmd(
|
||||||
|
["am", patch_path.to_str().expect("Path was not UTF-8")],
|
||||||
|
Some(path),
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let actual = ModuleRenderer::new("git_state").path(path).collect();
|
||||||
|
|
||||||
|
let expected = Some(format!("({}) ", Color::Yellow.bold().paint("AM 1/1")));
|
||||||
|
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
repo_dir.close()
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shows_merging() -> io::Result<()> {
|
fn shows_merging() -> io::Result<()> {
|
||||||
let repo_dir = create_repo_with_conflict()?;
|
let repo_dir = create_repo_with_conflict()?;
|
||||||
@@ -274,6 +293,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_git_cmd_with_output<A, S>(args: A, dir: &Path) -> io::Result<std::process::Output>
|
||||||
|
where
|
||||||
|
A: IntoIterator<Item = S>,
|
||||||
|
S: AsRef<OsStr>,
|
||||||
|
{
|
||||||
|
let output = create_command("git")?
|
||||||
|
.args(args)
|
||||||
|
.current_dir(dir)
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
Ok(output)
|
||||||
|
} else {
|
||||||
|
Err(Error::from(ErrorKind::Other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_repo_with_conflict() -> io::Result<tempfile::TempDir> {
|
fn create_repo_with_conflict() -> io::Result<tempfile::TempDir> {
|
||||||
let repo_dir = tempfile::tempdir()?;
|
let repo_dir = tempfile::tempdir()?;
|
||||||
let path = repo_dir.path();
|
let path = repo_dir.path();
|
||||||
|
|||||||
+22
-8
@@ -12,18 +12,19 @@ use crate::{
|
|||||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
let mut module = context.new_module("maven");
|
let mut module = context.new_module("maven");
|
||||||
let config = MavenConfig::try_load(module.config);
|
let config = MavenConfig::try_load(module.config);
|
||||||
let is_maven_project = context
|
let wrapper_properties = get_wrapper_properties_file(context, config.recursive);
|
||||||
.try_begin_scan()?
|
let is_maven_project = wrapper_properties.is_some()
|
||||||
.set_files(&config.detect_files)
|
|| context
|
||||||
.set_extensions(&config.detect_extensions)
|
.try_begin_scan()?
|
||||||
.set_folders(&config.detect_folders)
|
.set_files(&config.detect_files)
|
||||||
.is_match();
|
.set_extensions(&config.detect_extensions)
|
||||||
|
.set_folders(&config.detect_folders)
|
||||||
|
.is_match();
|
||||||
|
|
||||||
if !is_maven_project {
|
if !is_maven_project {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let wrapper_properties = get_wrapper_properties_file(context, config.recursive);
|
|
||||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||||
formatter
|
formatter
|
||||||
.map_meta(|var, _| match var {
|
.map_meta(|var, _| match var {
|
||||||
@@ -123,6 +124,20 @@ mod tests {
|
|||||||
dir.close()
|
dir.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn folder_with_maven_config_does_not_trigger_module() -> io::Result<()> {
|
||||||
|
let dir = tempfile::tempdir()?;
|
||||||
|
let maven_config = dir.path().join(".mvn").join("maven.config");
|
||||||
|
fs::create_dir_all(maven_config.parent().unwrap())?;
|
||||||
|
File::create(maven_config)?.sync_all()?;
|
||||||
|
|
||||||
|
let actual = ModuleRenderer::new("maven").path(dir.path()).collect();
|
||||||
|
|
||||||
|
let expected = None;
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
dir.close()
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_with_maven_wrapper_properties() -> io::Result<()> {
|
fn folder_with_maven_wrapper_properties() -> io::Result<()> {
|
||||||
let dir = tempfile::tempdir()?;
|
let dir = tempfile::tempdir()?;
|
||||||
@@ -132,7 +147,6 @@ mod tests {
|
|||||||
.join("wrapper")
|
.join("wrapper")
|
||||||
.join("maven-wrapper.properties");
|
.join("maven-wrapper.properties");
|
||||||
fs::create_dir_all(properties.parent().unwrap())?;
|
fs::create_dir_all(properties.parent().unwrap())?;
|
||||||
File::create(dir.path().join("pom.xml"))?.sync_all()?;
|
|
||||||
let mut file = File::create(properties)?;
|
let mut file = File::create(properties)?;
|
||||||
file.write_all(
|
file.write_all(
|
||||||
b"\
|
b"\
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ impl NixShellType {
|
|||||||
/// The module will use the `$IN_NIX_SHELL` and `$name` environment variable to
|
/// The module will use the `$IN_NIX_SHELL` and `$name` environment variable to
|
||||||
/// determine if it's inside a nix-shell and the name of it.
|
/// determine if it's inside a nix-shell and the name of it.
|
||||||
///
|
///
|
||||||
|
///
|
||||||
/// The following options are available:
|
/// The following options are available:
|
||||||
/// - `impure_msg` (string) // change the impure msg
|
/// - `impure_msg` (string) // change the impure msg
|
||||||
/// - `pure_msg` (string) // change the pure msg
|
/// - `pure_msg` (string) // change the pure msg
|
||||||
@@ -57,11 +58,12 @@ impl NixShellType {
|
|||||||
/// - impure // $name == "" in an impure nix-shell
|
/// - impure // $name == "" in an impure nix-shell
|
||||||
/// - unknown (name) // $name == "name" in an unknown nix-shell
|
/// - unknown (name) // $name == "name" in an unknown nix-shell
|
||||||
/// - unknown // $name == "" in an unknown nix-shell
|
/// - unknown // $name == "" in an unknown nix-shell
|
||||||
|
///
|
||||||
|
/// When using Lix 2.95+, will also have $level from `$NIX_SHELL_LEVEL`
|
||||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||||
let mut module = context.new_module("nix_shell");
|
let mut module = context.new_module("nix_shell");
|
||||||
let config: NixShellConfig = NixShellConfig::try_load(module.config);
|
let config: NixShellConfig = NixShellConfig::try_load(module.config);
|
||||||
|
|
||||||
let shell_name = context.get_env("name");
|
|
||||||
let shell_type = NixShellType::detect_shell_type(config.heuristic, context)?;
|
let shell_type = NixShellType::detect_shell_type(config.heuristic, context)?;
|
||||||
let shell_type_format = match shell_type {
|
let shell_type_format = match shell_type {
|
||||||
NixShellType::Pure => config.pure_msg,
|
NixShellType::Pure => config.pure_msg,
|
||||||
@@ -81,7 +83,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.map(|variable| match variable {
|
.map(|variable| match variable {
|
||||||
"name" => shell_name.as_ref().map(Ok),
|
"name" => context.get_env("name").map(Ok),
|
||||||
|
"level" => context.get_env("NIX_SHELL_LEVEL").map(Ok),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.parse(None, Some(context))
|
.parse(None, Some(context))
|
||||||
@@ -226,4 +229,19 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nix_shell_level() {
|
||||||
|
let actual = ModuleRenderer::new("nix_shell")
|
||||||
|
.env("IN_NIX_SHELL", "impure")
|
||||||
|
.env("NIX_SHELL_LEVEL", "3")
|
||||||
|
.config(toml::toml! {
|
||||||
|
[nix_shell]
|
||||||
|
format = "via [$symbol$state( \\($name\\)) $level]($style) "
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let expected = Some(format!("via {} ", Color::Blue.bold().paint("❄️ impure 3")));
|
||||||
|
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||||||
})
|
})
|
||||||
.map(|variable| match variable {
|
.map(|variable| match variable {
|
||||||
"environment" => pixi_environment_name.clone().map(Ok),
|
"environment" => pixi_environment_name.clone().map(Ok),
|
||||||
|
"project_name" => context.get_env("PIXI_PROJECT_NAME").map(Ok),
|
||||||
"version" => {
|
"version" => {
|
||||||
let pixi_version = get_pixi_version(context, &config)?;
|
let pixi_version = get_pixi_version(context, &config)?;
|
||||||
VersionFormatter::format_module_version(
|
VersionFormatter::format_module_version(
|
||||||
@@ -125,6 +126,45 @@ mod tests {
|
|||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_name_set() {
|
||||||
|
let actual = ModuleRenderer::new("pixi")
|
||||||
|
.env("PIXI_ENVIRONMENT_NAME", "py312")
|
||||||
|
.env("PIXI_PROJECT_NAME", "my-project")
|
||||||
|
.config(toml::toml! {
|
||||||
|
[pixi]
|
||||||
|
format = "via [$symbol($version )(\\($environment\\) )(\\[$project_name\\] )]($style)"
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let expected = Some(format!(
|
||||||
|
"via {}",
|
||||||
|
Color::Yellow
|
||||||
|
.bold()
|
||||||
|
.paint("🧚 v0.33.0 (py312) [my-project] ")
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn project_name_not_set() {
|
||||||
|
let actual = ModuleRenderer::new("pixi")
|
||||||
|
.env("PIXI_ENVIRONMENT_NAME", "py312")
|
||||||
|
.config(toml::toml! {
|
||||||
|
[pixi]
|
||||||
|
format = "via [$symbol($version )(\\($environment\\) )(\\[$project_name\\] )]($style)"
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let expected = Some(format!(
|
||||||
|
"via {}",
|
||||||
|
Color::Yellow.bold().paint("🧚 v0.33.0 (py312) ")
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_with_pixi_toml() -> io::Result<()> {
|
fn folder_with_pixi_toml() -> io::Result<()> {
|
||||||
let dir = tempfile::tempdir()?;
|
let dir = tempfile::tempdir()?;
|
||||||
|
|||||||
+258
-111
@@ -1,7 +1,12 @@
|
|||||||
use chrono::{DateTime, FixedOffset, Local, NaiveTime, Utc};
|
use jiff::{
|
||||||
|
Timestamp, Zoned,
|
||||||
|
civil::Time,
|
||||||
|
tz::{Offset, TimeZone},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{Context, Module, ModuleConfig};
|
use super::{Context, Module, ModuleConfig};
|
||||||
use crate::configs::time::TimeConfig;
|
use crate::config::Either;
|
||||||
|
use crate::configs::time::{TimeConfig, TimezoneWrapper};
|
||||||
use crate::formatter::StringFormatter;
|
use crate::formatter::StringFormatter;
|
||||||
|
|
||||||
/// Outputs the current time
|
/// Outputs the current time
|
||||||
@@ -17,7 +22,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||||||
|
|
||||||
// Hide prompt if current time is not inside time_range
|
// Hide prompt if current time is not inside time_range
|
||||||
let (display_start, display_end) = parse_time_range(config.time_range);
|
let (display_start, display_end) = parse_time_range(config.time_range);
|
||||||
let time_now = Local::now().time();
|
let time_now = Zoned::now().time();
|
||||||
if !is_inside_time_range(time_now, display_start, display_end) {
|
if !is_inside_time_range(time_now, display_start, display_end) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -27,17 +32,26 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||||||
|
|
||||||
log::trace!("Timer module is enabled with format string: {time_format}");
|
log::trace!("Timer module is enabled with format string: {time_format}");
|
||||||
|
|
||||||
let formatted_time_string = if config.utc_time_offset != "local" {
|
let formatted_time_string = match &config.utc_time_offset {
|
||||||
create_offset_time_string(Utc::now(), config.utc_time_offset, time_format).unwrap_or_else(
|
Either::First(TimezoneWrapper(tz)) => {
|
||||||
|_| {
|
// Use IANA timezone name
|
||||||
log::warn!(
|
let target_time = Timestamp::now().to_zoned(tz.clone());
|
||||||
"Invalid utc_time_offset configuration provided! Falling back to \"local\"."
|
format_time_fixed_offset(time_format, target_time)
|
||||||
);
|
}
|
||||||
format_time(time_format, Local::now())
|
Either::Second("local") => {
|
||||||
},
|
// Use local timezone
|
||||||
)
|
format_time(time_format, Zoned::now())
|
||||||
} else {
|
}
|
||||||
format_time(time_format, Local::now())
|
Either::Second(utc_time_offset) => {
|
||||||
|
// Use numeric offset
|
||||||
|
create_offset_time_string(Timestamp::now(), utc_time_offset, time_format)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
log::warn!(
|
||||||
|
"Invalid utc_time_offset configuration provided! Falling back to \"local\"."
|
||||||
|
);
|
||||||
|
format_time(time_format, Zoned::now())
|
||||||
|
})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||||
@@ -65,10 +79,10 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_offset_time_string(
|
fn create_offset_time_string(
|
||||||
utc_time: DateTime<Utc>,
|
utc_time: Timestamp,
|
||||||
utc_time_offset_str: &str,
|
utc_time_offset_str: &str,
|
||||||
time_format: &str,
|
time_format: &str,
|
||||||
) -> Result<String, &'static str> {
|
) -> Result<String, String> {
|
||||||
// Using floats to allow 30/45 minute offsets: https://www.timeanddate.com/time/time-zones-interesting.html
|
// Using floats to allow 30/45 minute offsets: https://www.timeanddate.com/time/time-zones-interesting.html
|
||||||
let utc_time_offset_in_hours = utc_time_offset_str.parse::<f32>().unwrap_or(
|
let utc_time_offset_in_hours = utc_time_offset_str.parse::<f32>().unwrap_or(
|
||||||
// Passing out of range value to force falling back to "local"
|
// Passing out of range value to force falling back to "local"
|
||||||
@@ -76,38 +90,34 @@ fn create_offset_time_string(
|
|||||||
);
|
);
|
||||||
if utc_time_offset_in_hours < 24_f32 && utc_time_offset_in_hours > -24_f32 {
|
if utc_time_offset_in_hours < 24_f32 && utc_time_offset_in_hours > -24_f32 {
|
||||||
let utc_offset_in_seconds: i32 = (utc_time_offset_in_hours * 3600_f32) as i32;
|
let utc_offset_in_seconds: i32 = (utc_time_offset_in_hours * 3600_f32) as i32;
|
||||||
let Some(timezone_offset) = FixedOffset::east_opt(utc_offset_in_seconds) else {
|
let timezone_offset = Offset::from_seconds(utc_offset_in_seconds)
|
||||||
return Err("Invalid offset");
|
.map_err(|err| format!("Invalid timezone offset: {err:?}"))?;
|
||||||
};
|
let tz = TimeZone::fixed(timezone_offset);
|
||||||
log::trace!("Target timezone offset is {timezone_offset}");
|
log::trace!("Target timezone offset is {timezone_offset}");
|
||||||
|
|
||||||
let target_time = utc_time.with_timezone(&timezone_offset);
|
let target_time = utc_time.to_zoned(tz);
|
||||||
log::trace!("Time in target timezone now is {target_time}");
|
log::trace!("Time in target timezone now is {target_time}");
|
||||||
|
|
||||||
Ok(format_time_fixed_offset(time_format, target_time))
|
Ok(format_time_fixed_offset(time_format, target_time))
|
||||||
} else {
|
} else {
|
||||||
Err("Invalid timezone offset.")
|
Err("Invalid timezone offset.".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format a given time into the given string. This function should be referentially
|
/// Format a given time into the given string. This function should be referentially
|
||||||
/// transparent, which makes it easy to test (unlike anything involving the actual time)
|
/// transparent, which makes it easy to test (unlike anything involving the actual time)
|
||||||
fn format_time(time_format: &str, local_time: DateTime<Local>) -> String {
|
fn format_time(time_format: &str, local_time: Zoned) -> String {
|
||||||
local_time.format(time_format).to_string()
|
local_time.strftime(time_format).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_time_fixed_offset(time_format: &str, utc_time: DateTime<FixedOffset>) -> String {
|
fn format_time_fixed_offset(time_format: &str, zoned_time: Zoned) -> String {
|
||||||
utc_time.format(time_format).to_string()
|
zoned_time.strftime(time_format).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if `time_now` is between `time_start` and `time_end`.
|
/// Returns true if `time_now` is between `time_start` and `time_end`.
|
||||||
/// If one of these values is not given, then it is ignored.
|
/// If one of these values is not given, then it is ignored.
|
||||||
/// It also handles cases where `time_start` and `time_end` have a midnight in between
|
/// It also handles cases where `time_start` and `time_end` have a midnight in between
|
||||||
fn is_inside_time_range(
|
fn is_inside_time_range(time_now: Time, time_start: Option<Time>, time_end: Option<Time>) -> bool {
|
||||||
time_now: NaiveTime,
|
|
||||||
time_start: Option<NaiveTime>,
|
|
||||||
time_end: Option<NaiveTime>,
|
|
||||||
) -> bool {
|
|
||||||
match (time_start, time_end) {
|
match (time_start, time_end) {
|
||||||
(None, None) => true,
|
(None, None) => true,
|
||||||
(Some(i), None) => time_now > i,
|
(Some(i), None) => time_now > i,
|
||||||
@@ -127,7 +137,7 @@ fn is_inside_time_range(
|
|||||||
///
|
///
|
||||||
/// If one of the ranges is invalid or not provided, then the corresponding field in the output
|
/// If one of the ranges is invalid or not provided, then the corresponding field in the output
|
||||||
/// tuple is None
|
/// tuple is None
|
||||||
fn parse_time_range(time_range: &str) -> (Option<NaiveTime>, Option<NaiveTime>) {
|
fn parse_time_range(time_range: &str) -> (Option<Time>, Option<Time>) {
|
||||||
let value = String::from(time_range);
|
let value = String::from(time_range);
|
||||||
|
|
||||||
// Check if there is exactly one hyphen, and fail otherwise
|
// Check if there is exactly one hyphen, and fail otherwise
|
||||||
@@ -140,8 +150,8 @@ fn parse_time_range(time_range: &str) -> (Option<NaiveTime>, Option<NaiveTime>)
|
|||||||
let end = &end[1..];
|
let end = &end[1..];
|
||||||
|
|
||||||
// Parse the ranges
|
// Parse the ranges
|
||||||
let start_time = NaiveTime::parse_from_str(start, "%H:%M:%S").ok();
|
let start_time = start.parse::<Time>().ok();
|
||||||
let end_time = NaiveTime::parse_from_str(end, "%H:%M:%S").ok();
|
let end_time = end.parse::<Time>().ok();
|
||||||
|
|
||||||
(start_time, end_time)
|
(start_time, end_time)
|
||||||
}
|
}
|
||||||
@@ -152,140 +162,151 @@ tests become extra important */
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test::ModuleRenderer;
|
use crate::test::ModuleRenderer;
|
||||||
use chrono::offset::TimeZone;
|
use jiff::civil::date;
|
||||||
|
|
||||||
const FMT_12: &str = "%r";
|
const FMT_12: &str = "%r";
|
||||||
const FMT_24: &str = "%T";
|
const FMT_24: &str = "%T";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_midnight_12hr() {
|
fn test_midnight_12hr() {
|
||||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 0, 0, 0).unwrap();
|
let time = date(2014, 7, 8)
|
||||||
|
.at(0, 0, 0, 0)
|
||||||
|
.to_zoned(TimeZone::system())
|
||||||
|
.unwrap();
|
||||||
let formatted = format_time(FMT_12, time);
|
let formatted = format_time(FMT_12, time);
|
||||||
assert_eq!(formatted, "12:00:00 AM");
|
assert_eq!(formatted, "12:00:00 AM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_midnight_24hr() {
|
fn test_midnight_24hr() {
|
||||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 0, 0, 0).unwrap();
|
let time = date(2014, 7, 8)
|
||||||
|
.at(0, 0, 0, 0)
|
||||||
|
.to_zoned(TimeZone::system())
|
||||||
|
.unwrap();
|
||||||
let formatted = format_time(FMT_24, time);
|
let formatted = format_time(FMT_24, time);
|
||||||
assert_eq!(formatted, "00:00:00");
|
assert_eq!(formatted, "00:00:00");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_noon_12hr() {
|
fn test_noon_12hr() {
|
||||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 12, 0, 0).unwrap();
|
let time = date(2014, 7, 8)
|
||||||
|
.at(12, 0, 0, 0)
|
||||||
|
.to_zoned(TimeZone::system())
|
||||||
|
.unwrap();
|
||||||
let formatted = format_time(FMT_12, time);
|
let formatted = format_time(FMT_12, time);
|
||||||
assert_eq!(formatted, "12:00:00 PM");
|
assert_eq!(formatted, "12:00:00 PM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_noon_24hr() {
|
fn test_noon_24hr() {
|
||||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 12, 0, 0).unwrap();
|
let time = date(2014, 7, 8)
|
||||||
|
.at(12, 0, 0, 0)
|
||||||
|
.to_zoned(TimeZone::system())
|
||||||
|
.unwrap();
|
||||||
let formatted = format_time(FMT_24, time);
|
let formatted = format_time(FMT_24, time);
|
||||||
assert_eq!(formatted, "12:00:00");
|
assert_eq!(formatted, "12:00:00");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_arbtime_12hr() {
|
fn test_arbtime_12hr() {
|
||||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::system())
|
||||||
|
.unwrap();
|
||||||
let formatted = format_time(FMT_12, time);
|
let formatted = format_time(FMT_12, time);
|
||||||
assert_eq!(formatted, "03:36:47 PM");
|
assert_eq!(formatted, "3:36:47 PM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_arbtime_24hr() {
|
fn test_arbtime_24hr() {
|
||||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::system())
|
||||||
|
.unwrap();
|
||||||
let formatted = format_time(FMT_24, time);
|
let formatted = format_time(FMT_24, time);
|
||||||
assert_eq!(formatted, "15:36:47");
|
assert_eq!(formatted, "15:36:47");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_with_paren() {
|
fn test_format_with_paren() {
|
||||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::system())
|
||||||
|
.unwrap();
|
||||||
let formatted = format_time("[%T]", time);
|
let formatted = format_time("[%T]", time);
|
||||||
assert_eq!(formatted, "[15:36:47]");
|
assert_eq!(formatted, "[15:36:47]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_midnight_12hr_fixed_offset() {
|
fn test_midnight_12hr_fixed_offset() {
|
||||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||||
let time = Utc
|
let tz = TimeZone::fixed(timezone_offset);
|
||||||
.with_ymd_and_hms(2014, 7, 8, 0, 0, 0)
|
let time = date(2014, 7, 8).at(0, 0, 0, 0).to_zoned(tz).unwrap();
|
||||||
.unwrap()
|
|
||||||
.with_timezone(&timezone_offset);
|
|
||||||
let formatted = format_time_fixed_offset(FMT_12, time);
|
let formatted = format_time_fixed_offset(FMT_12, time);
|
||||||
assert_eq!(formatted, "12:00:00 AM");
|
assert_eq!(formatted, "12:00:00 AM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_midnight_24hr_fixed_offset() {
|
fn test_midnight_24hr_fixed_offset() {
|
||||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||||
let time = Utc
|
let tz = TimeZone::fixed(timezone_offset);
|
||||||
.with_ymd_and_hms(2014, 7, 8, 0, 0, 0)
|
let time = date(2014, 7, 8).at(0, 0, 0, 0).to_zoned(tz).unwrap();
|
||||||
.unwrap()
|
|
||||||
.with_timezone(&timezone_offset);
|
|
||||||
let formatted = format_time_fixed_offset(FMT_24, time);
|
let formatted = format_time_fixed_offset(FMT_24, time);
|
||||||
assert_eq!(formatted, "00:00:00");
|
assert_eq!(formatted, "00:00:00");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_noon_12hr_fixed_offset() {
|
fn test_noon_12hr_fixed_offset() {
|
||||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||||
let time = Utc
|
let tz = TimeZone::fixed(timezone_offset);
|
||||||
.with_ymd_and_hms(2014, 7, 8, 12, 0, 0)
|
let time = date(2014, 7, 8).at(12, 0, 0, 0).to_zoned(tz).unwrap();
|
||||||
.unwrap()
|
|
||||||
.with_timezone(&timezone_offset);
|
|
||||||
let formatted = format_time_fixed_offset(FMT_12, time);
|
let formatted = format_time_fixed_offset(FMT_12, time);
|
||||||
assert_eq!(formatted, "12:00:00 PM");
|
assert_eq!(formatted, "12:00:00 PM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_noon_24hr_fixed_offset() {
|
fn test_noon_24hr_fixed_offset() {
|
||||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||||
let time = Utc
|
let tz = TimeZone::fixed(timezone_offset);
|
||||||
.with_ymd_and_hms(2014, 7, 8, 12, 0, 0)
|
let time = date(2014, 7, 8).at(12, 0, 0, 0).to_zoned(tz).unwrap();
|
||||||
.unwrap()
|
|
||||||
.with_timezone(&timezone_offset);
|
|
||||||
let formatted = format_time_fixed_offset(FMT_24, time);
|
let formatted = format_time_fixed_offset(FMT_24, time);
|
||||||
assert_eq!(formatted, "12:00:00");
|
assert_eq!(formatted, "12:00:00");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_arbtime_12hr_fixed_offset() {
|
fn test_arbtime_12hr_fixed_offset() {
|
||||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||||
let time = Utc
|
let tz = TimeZone::fixed(timezone_offset);
|
||||||
.with_ymd_and_hms(2014, 7, 8, 15, 36, 47)
|
let time = date(2014, 7, 8).at(15, 36, 47, 0).to_zoned(tz).unwrap();
|
||||||
.unwrap()
|
|
||||||
.with_timezone(&timezone_offset);
|
|
||||||
let formatted = format_time_fixed_offset(FMT_12, time);
|
let formatted = format_time_fixed_offset(FMT_12, time);
|
||||||
assert_eq!(formatted, "03:36:47 PM");
|
assert_eq!(formatted, "3:36:47 PM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_arbtime_24hr_fixed_offset() {
|
fn test_arbtime_24hr_fixed_offset() {
|
||||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||||
let time = Utc
|
let tz = TimeZone::fixed(timezone_offset);
|
||||||
.with_ymd_and_hms(2014, 7, 8, 15, 36, 47)
|
let time = date(2014, 7, 8).at(15, 36, 47, 0).to_zoned(tz).unwrap();
|
||||||
.unwrap()
|
|
||||||
.with_timezone(&timezone_offset);
|
|
||||||
let formatted = format_time_fixed_offset(FMT_24, time);
|
let formatted = format_time_fixed_offset(FMT_24, time);
|
||||||
assert_eq!(formatted, "15:36:47");
|
assert_eq!(formatted, "15:36:47");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_with_paren_fixed_offset() {
|
fn test_format_with_paren_fixed_offset() {
|
||||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||||
let time = Utc
|
let tz = TimeZone::fixed(timezone_offset);
|
||||||
.with_ymd_and_hms(2014, 7, 8, 15, 36, 47)
|
let time = date(2014, 7, 8).at(15, 36, 47, 0).to_zoned(tz).unwrap();
|
||||||
.unwrap()
|
|
||||||
.with_timezone(&timezone_offset);
|
|
||||||
let formatted = format_time_fixed_offset("[%T]", time);
|
let formatted = format_time_fixed_offset("[%T]", time);
|
||||||
assert_eq!(formatted, "[15:36:47]");
|
assert_eq!(formatted, "[15:36:47]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_minus_3() {
|
fn test_create_formatted_time_string_with_minus_3() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "-3";
|
let utc_time_offset_str = "-3";
|
||||||
|
|
||||||
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
||||||
@@ -294,34 +315,50 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_plus_5() {
|
fn test_create_formatted_time_string_with_plus_5() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "+5";
|
let utc_time_offset_str = "+5";
|
||||||
|
|
||||||
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
||||||
assert_eq!(actual, "08:36:47 PM");
|
assert_eq!(actual, "8:36:47 PM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_plus_9_30() {
|
fn test_create_formatted_time_string_with_plus_9_30() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "+9.5";
|
let utc_time_offset_str = "+9.5";
|
||||||
|
|
||||||
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
||||||
assert_eq!(actual, "01:06:47 AM");
|
assert_eq!(actual, "1:06:47 AM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_plus_5_45() {
|
fn test_create_formatted_time_string_with_plus_5_45() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "+5.75";
|
let utc_time_offset_str = "+5.75";
|
||||||
|
|
||||||
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
||||||
assert_eq!(actual, "09:21:47 PM");
|
assert_eq!(actual, "9:21:47 PM");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_plus_24() {
|
fn test_create_formatted_time_string_with_plus_24() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "+24";
|
let utc_time_offset_str = "+24";
|
||||||
|
|
||||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||||
@@ -330,7 +367,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_minus_24() {
|
fn test_create_formatted_time_string_with_minus_24() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "-24";
|
let utc_time_offset_str = "-24";
|
||||||
|
|
||||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||||
@@ -339,7 +380,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_plus_9001() {
|
fn test_create_formatted_time_string_with_plus_9001() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "+9001";
|
let utc_time_offset_str = "+9001";
|
||||||
|
|
||||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||||
@@ -348,7 +393,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_minus_4242() {
|
fn test_create_formatted_time_string_with_minus_4242() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "-4242";
|
let utc_time_offset_str = "-4242";
|
||||||
|
|
||||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||||
@@ -357,7 +406,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_formatted_time_string_with_invalid_string() {
|
fn test_create_formatted_time_string_with_invalid_string() {
|
||||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
let utc_time = date(2014, 7, 8)
|
||||||
|
.at(15, 36, 47, 0)
|
||||||
|
.to_zoned(TimeZone::UTC)
|
||||||
|
.unwrap()
|
||||||
|
.timestamp();
|
||||||
let utc_time_offset_str = "completely wrong config";
|
let utc_time_offset_str = "completely wrong config";
|
||||||
|
|
||||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||||
@@ -379,7 +432,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_time_range(time_range),
|
parse_time_range(time_range),
|
||||||
(Some(NaiveTime::from_hms_opt(10, 00, 00).unwrap()), None)
|
(Some(Time::new(10, 0, 0, 0).unwrap()), None)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,7 +442,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_time_range(time_range),
|
parse_time_range(time_range),
|
||||||
(None, Some(NaiveTime::from_hms_opt(22, 00, 00).unwrap()))
|
(None, Some(Time::new(22, 0, 0, 0).unwrap()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,8 +453,8 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_time_range(time_range),
|
parse_time_range(time_range),
|
||||||
(
|
(
|
||||||
Some(NaiveTime::from_hms_opt(10, 00, 00).unwrap()),
|
Some(Time::new(10, 0, 0, 0).unwrap()),
|
||||||
Some(NaiveTime::from_hms_opt(16, 00, 00).unwrap())
|
Some(Time::new(16, 0, 0, 0).unwrap())
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -410,16 +463,16 @@ mod tests {
|
|||||||
fn test_is_inside_time_range_with_no_range() {
|
fn test_is_inside_time_range_with_no_range() {
|
||||||
let time_start = None;
|
let time_start = None;
|
||||||
let time_end = None;
|
let time_end = None;
|
||||||
let time_now = NaiveTime::from_hms_opt(10, 00, 00).unwrap();
|
let time_now = Time::new(10, 0, 0, 0).unwrap();
|
||||||
|
|
||||||
assert!(is_inside_time_range(time_now, time_start, time_end));
|
assert!(is_inside_time_range(time_now, time_start, time_end));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_inside_time_range_with_start_range() {
|
fn test_is_inside_time_range_with_start_range() {
|
||||||
let time_start = Some(NaiveTime::from_hms_opt(10, 00, 00).unwrap());
|
let time_start = Some(Time::new(10, 0, 0, 0).unwrap());
|
||||||
let time_now = NaiveTime::from_hms_opt(12, 00, 00).unwrap();
|
let time_now = Time::new(12, 0, 0, 0).unwrap();
|
||||||
let time_now2 = NaiveTime::from_hms_opt(8, 00, 00).unwrap();
|
let time_now2 = Time::new(8, 0, 0, 0).unwrap();
|
||||||
|
|
||||||
assert!(is_inside_time_range(time_now, time_start, None));
|
assert!(is_inside_time_range(time_now, time_start, None));
|
||||||
assert!(!is_inside_time_range(time_now2, time_start, None));
|
assert!(!is_inside_time_range(time_now2, time_start, None));
|
||||||
@@ -427,9 +480,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_inside_time_range_with_end_range() {
|
fn test_is_inside_time_range_with_end_range() {
|
||||||
let time_end = Some(NaiveTime::from_hms_opt(16, 00, 00).unwrap());
|
let time_end = Some(Time::new(16, 0, 0, 0).unwrap());
|
||||||
let time_now = NaiveTime::from_hms_opt(15, 00, 00).unwrap();
|
let time_now = Time::new(15, 0, 0, 0).unwrap();
|
||||||
let time_now2 = NaiveTime::from_hms_opt(19, 00, 00).unwrap();
|
let time_now2 = Time::new(19, 0, 0, 0).unwrap();
|
||||||
|
|
||||||
assert!(is_inside_time_range(time_now, None, time_end));
|
assert!(is_inside_time_range(time_now, None, time_end));
|
||||||
assert!(!is_inside_time_range(time_now2, None, time_end));
|
assert!(!is_inside_time_range(time_now2, None, time_end));
|
||||||
@@ -437,11 +490,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_inside_time_range_with_complete_range() {
|
fn test_is_inside_time_range_with_complete_range() {
|
||||||
let time_start = Some(NaiveTime::from_hms_opt(9, 00, 00).unwrap());
|
let time_start = Some(Time::new(9, 0, 0, 0).unwrap());
|
||||||
let time_end = Some(NaiveTime::from_hms_opt(18, 00, 00).unwrap());
|
let time_end = Some(Time::new(18, 0, 0, 0).unwrap());
|
||||||
let time_now = NaiveTime::from_hms_opt(3, 00, 00).unwrap();
|
let time_now = Time::new(3, 0, 0, 0).unwrap();
|
||||||
let time_now2 = NaiveTime::from_hms_opt(13, 00, 00).unwrap();
|
let time_now2 = Time::new(13, 0, 0, 0).unwrap();
|
||||||
let time_now3 = NaiveTime::from_hms_opt(20, 00, 00).unwrap();
|
let time_now3 = Time::new(20, 0, 0, 0).unwrap();
|
||||||
|
|
||||||
assert!(!is_inside_time_range(time_now, time_start, time_end));
|
assert!(!is_inside_time_range(time_now, time_start, time_end));
|
||||||
assert!(is_inside_time_range(time_now2, time_start, time_end));
|
assert!(is_inside_time_range(time_now2, time_start, time_end));
|
||||||
@@ -450,11 +503,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_inside_time_range_with_complete_range_passing_midnight() {
|
fn test_is_inside_time_range_with_complete_range_passing_midnight() {
|
||||||
let time_start = Some(NaiveTime::from_hms_opt(19, 00, 00).unwrap());
|
let time_start = Some(Time::new(19, 0, 0, 0).unwrap());
|
||||||
let time_end = Some(NaiveTime::from_hms_opt(12, 00, 00).unwrap());
|
let time_end = Some(Time::new(12, 0, 0, 0).unwrap());
|
||||||
let time_now = NaiveTime::from_hms_opt(3, 00, 00).unwrap();
|
let time_now = Time::new(3, 0, 0, 0).unwrap();
|
||||||
let time_now2 = NaiveTime::from_hms_opt(13, 00, 00).unwrap();
|
let time_now2 = Time::new(13, 0, 0, 0).unwrap();
|
||||||
let time_now3 = NaiveTime::from_hms_opt(20, 00, 00).unwrap();
|
let time_now3 = Time::new(20, 0, 0, 0).unwrap();
|
||||||
|
|
||||||
assert!(is_inside_time_range(time_now, time_start, time_end));
|
assert!(is_inside_time_range(time_now, time_start, time_end));
|
||||||
assert!(!is_inside_time_range(time_now2, time_start, time_end));
|
assert!(!is_inside_time_range(time_now2, time_start, time_end));
|
||||||
@@ -503,4 +556,98 @@ mod tests {
|
|||||||
assert!(actual.starts_with(&col_prefix));
|
assert!(actual.starts_with(&col_prefix));
|
||||||
assert!(actual.ends_with(&col_suffix));
|
assert!(actual.ends_with(&col_suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_check_invalid_tz() {
|
||||||
|
let actual = ModuleRenderer::new("time")
|
||||||
|
.config(toml::toml! {
|
||||||
|
[time]
|
||||||
|
disabled = false
|
||||||
|
time_format = "%T"
|
||||||
|
utc_time_offset = "invalid"
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert!(actual.is_some(), "Falls back to local time");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_tz() {
|
||||||
|
use nu_ansi_term::Color;
|
||||||
|
|
||||||
|
let actual = ModuleRenderer::new("time")
|
||||||
|
.config(toml::toml! {
|
||||||
|
[time]
|
||||||
|
disabled = false
|
||||||
|
time_format = "%:z"
|
||||||
|
utc_time_offset = "Asia/Kolkata"
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let expected = Some(format!("at {} ", Color::Yellow.bold().paint("+05:30")));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected, "Uses timezone");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_offset() {
|
||||||
|
use nu_ansi_term::Color;
|
||||||
|
|
||||||
|
let actual = ModuleRenderer::new("time")
|
||||||
|
.config(toml::toml! {
|
||||||
|
[time]
|
||||||
|
disabled = false
|
||||||
|
time_format = "%:z"
|
||||||
|
utc_time_offset = "-1.75"
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let expected = Some(format!("at {} ", Color::Yellow.bold().paint("-01:45")));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected, "Uses timezone offset");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_tz_abbreviation() {
|
||||||
|
let actual = ModuleRenderer::new("time")
|
||||||
|
.config(toml::toml! {
|
||||||
|
[time]
|
||||||
|
disabled = false
|
||||||
|
time_format = "%Z"
|
||||||
|
utc_time_offset = "America/New_York"
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Should output a timezone abbreviation like "EST" or "EDT"
|
||||||
|
assert!(
|
||||||
|
actual.is_some(),
|
||||||
|
"Timezone abbreviation should be displayed"
|
||||||
|
);
|
||||||
|
let output = actual.unwrap();
|
||||||
|
assert!(
|
||||||
|
output.contains("EST") || output.contains("EDT"),
|
||||||
|
"Should contain timezone abbreviation"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_tz_identifier() {
|
||||||
|
use nu_ansi_term::Color;
|
||||||
|
|
||||||
|
let actual = ModuleRenderer::new("time")
|
||||||
|
.config(toml::toml! {
|
||||||
|
[time]
|
||||||
|
disabled = false
|
||||||
|
time_format = "%Q"
|
||||||
|
utc_time_offset = "America/New_York"
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let expected = Some(format!(
|
||||||
|
"at {} ",
|
||||||
|
Color::Yellow.bold().paint("America/New_York")
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected, "Uses IANA timezone identifier");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-7
@@ -527,7 +527,7 @@ impl ValueEnum for Preset {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preset_command(name: Option<Preset>, output: Option<PathBuf>, list: bool) {
|
pub fn preset_command(name: Option<Preset>, output: Option<PathBuf>, force: bool, list: bool) {
|
||||||
if list {
|
if list {
|
||||||
println!("{}", preset_list());
|
println!("{}", preset_list());
|
||||||
return;
|
return;
|
||||||
@@ -535,11 +535,11 @@ pub fn preset_command(name: Option<Preset>, output: Option<PathBuf>, list: bool)
|
|||||||
let variant = name.expect("name argument must be specified");
|
let variant = name.expect("name argument must be specified");
|
||||||
let content = shadow::get_preset_content(variant.0);
|
let content = shadow::get_preset_content(variant.0);
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
if let Err(err) = std::fs::write(output, content) {
|
if let Err(e) = crate::utils::write_file_atomic(&output, content, force) {
|
||||||
eprintln!("Error writing preset to file: {err}");
|
eprintln!("Error writing preset to {output:?}: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
} else if let Err(err) = std::io::stdout().write_all(content) {
|
} else if let Err(err) = std::io::stdout().write_all(content.as_bytes()) {
|
||||||
eprintln!("Error writing preset to stdout: {err}");
|
eprintln!("Error writing preset to stdout: {err}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
@@ -681,9 +681,9 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn preset_command_does_not_panic_on_correct_inputs() {
|
fn preset_command_does_not_panic_on_correct_inputs() {
|
||||||
preset_command(None, None, true);
|
preset_command(None, None, false, true);
|
||||||
for v in Preset::value_variants() {
|
for v in Preset::value_variants() {
|
||||||
preset_command(Some(v.clone()), None, false);
|
preset_command(Some(v.clone()), None, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,12 +691,35 @@ mod test {
|
|||||||
fn preset_command_output_to_file() -> std::io::Result<()> {
|
fn preset_command_output_to_file() -> std::io::Result<()> {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
let path = dir.path().join("preset.toml");
|
let path = dir.path().join("preset.toml");
|
||||||
preset_command(Some(Preset("nerd-font-symbols")), Some(path.clone()), false);
|
preset_command(
|
||||||
|
Some(Preset("nerd-font-symbols")),
|
||||||
|
Some(path.clone()),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
let actual = utils::read_file(&path)?;
|
let actual = utils::read_file(&path)?;
|
||||||
let expected = include_str!("../docs/public/presets/toml/nerd-font-symbols.toml");
|
let expected = include_str!("../docs/public/presets/toml/nerd-font-symbols.toml");
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
dir.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preset_command_output_existing_file_force() -> io::Result<()> {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let path = dir.path().join("preset.toml");
|
||||||
|
utils::write_file(&path, "existing content")?;
|
||||||
|
|
||||||
|
preset_command(
|
||||||
|
Some(Preset("nerd-font-symbols")),
|
||||||
|
Some(path.clone()),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let actual = utils::read_file(&path).unwrap();
|
||||||
|
let expected = include_str!("../docs/public/presets/toml/nerd-font-symbols.toml");
|
||||||
|
assert_eq!(actual, expected);
|
||||||
dir.close()
|
dir.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+119
-5
@@ -5,8 +5,8 @@ pub mod statusline;
|
|||||||
use process_control::{ChildExt, Control};
|
use process_control::{ChildExt, Control};
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fs::read_to_string;
|
use std::fs;
|
||||||
use std::io::{Error, ErrorKind, Result};
|
use std::io::{Error, ErrorKind, Result, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@@ -44,7 +44,7 @@ pub fn context_path<S: AsRef<OsStr> + ?Sized>(context: &Context, s: &S) -> PathB
|
|||||||
pub fn read_file<P: AsRef<Path> + Debug>(file_name: P) -> Result<String> {
|
pub fn read_file<P: AsRef<Path> + Debug>(file_name: P) -> Result<String> {
|
||||||
log::trace!("Trying to read from {file_name:?}");
|
log::trace!("Trying to read from {file_name:?}");
|
||||||
|
|
||||||
let result = read_to_string(file_name);
|
let result = fs::read_to_string(file_name);
|
||||||
|
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
log::debug!("Error reading file: {result:?}");
|
log::debug!("Error reading file: {result:?}");
|
||||||
@@ -58,8 +58,6 @@ pub fn read_file<P: AsRef<Path> + Debug>(file_name: P) -> Result<String> {
|
|||||||
/// Write a string to a file
|
/// Write a string to a file
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn write_file<P: AsRef<Path>, S: AsRef<str>>(file_name: P, text: S) -> Result<()> {
|
pub fn write_file<P: AsRef<Path>, S: AsRef<str>>(file_name: P, text: S) -> Result<()> {
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
let file_name = file_name.as_ref();
|
let file_name = file_name.as_ref();
|
||||||
let text = text.as_ref();
|
let text = text.as_ref();
|
||||||
|
|
||||||
@@ -89,6 +87,73 @@ pub fn write_file<P: AsRef<Path>, S: AsRef<str>>(file_name: P, text: S) -> Resul
|
|||||||
file.sync_all()
|
file.sync_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write contents to a file by first writing to a temporary file
|
||||||
|
/// and then move it to the target location in place
|
||||||
|
/// Only overwrites existing files if `force` is true
|
||||||
|
pub fn write_file_atomic<P: AsRef<Path>, S: AsRef<str>>(
|
||||||
|
target_path: P,
|
||||||
|
text: S,
|
||||||
|
force: bool,
|
||||||
|
) -> std::result::Result<(), String> {
|
||||||
|
let target_path = target_path.as_ref();
|
||||||
|
let text = text.as_ref();
|
||||||
|
|
||||||
|
log::trace!("Trying to write {text:?} to {target_path:?}");
|
||||||
|
|
||||||
|
#[cfg_attr(not(unix), allow(unused_mut))]
|
||||||
|
let mut builder = tempfile::Builder::new();
|
||||||
|
|
||||||
|
// On Unix, the default permissions are too restrictive, so we need to relax them
|
||||||
|
// This should be safe because we're creating a temporary file in the same directory
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let permissions = target_path
|
||||||
|
.metadata()
|
||||||
|
.as_ref()
|
||||||
|
.map(fs::Metadata::permissions)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
let all_read_write = 0o666;
|
||||||
|
std::fs::Permissions::from_mode(all_read_write)
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.permissions(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(parent_dir) = target_path.parent() else {
|
||||||
|
return Err(format!(
|
||||||
|
"Unable to determine parent directory of {target_path:?}"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut temp_file = builder
|
||||||
|
.tempfile_in(parent_dir)
|
||||||
|
.map_err(|e| format!("Error creating temporary file: {}", e))?;
|
||||||
|
|
||||||
|
if let Err(err) = temp_file.write_all(text.as_bytes()) {
|
||||||
|
return Err(format!("Error writing to temporary file: {}", err));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = if force {
|
||||||
|
temp_file.persist(target_path)
|
||||||
|
} else {
|
||||||
|
temp_file.persist_noclobber(target_path)
|
||||||
|
};
|
||||||
|
|
||||||
|
result.map_err(|e| {
|
||||||
|
if !force && e.error.kind() == ErrorKind::AlreadyExists {
|
||||||
|
"Error saving file, use --force to overwrite existing configuration file".to_string()
|
||||||
|
} else {
|
||||||
|
format!("Error moving temporary file to target location: {e}")
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
log::trace!("File {target_path:?} written successfully");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads command output from stderr or stdout depending on to which stream program streamed it's output
|
/// Reads command output from stderr or stdout depending on to which stream program streamed it's output
|
||||||
pub fn get_command_string_output(command: CommandOutput) -> String {
|
pub fn get_command_string_output(command: CommandOutput) -> String {
|
||||||
if command.stdout.is_empty() {
|
if command.stdout.is_empty() {
|
||||||
@@ -819,6 +884,8 @@ impl PathExt for Path {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use tempfile::tempdir;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1033,4 +1100,51 @@ mod tests {
|
|||||||
"080d09bd815e".to_string()
|
"080d09bd815e".to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_file_atomic() -> Result<()> {
|
||||||
|
// Create a temporary file for testing
|
||||||
|
let tmp_dir = tempdir()?;
|
||||||
|
let path = tmp_dir.path().join("test_config.toml");
|
||||||
|
|
||||||
|
let expected = "test data";
|
||||||
|
write_file_atomic(&path, expected, false).unwrap();
|
||||||
|
|
||||||
|
let actual_data = read_file(&path)?;
|
||||||
|
assert_eq!(actual_data, expected);
|
||||||
|
|
||||||
|
tmp_dir.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_file_atomic_already_exists() -> Result<()> {
|
||||||
|
let tmp_dir = tempdir()?;
|
||||||
|
let tmp_file_path = tmp_dir.path().join("test_config.toml");
|
||||||
|
|
||||||
|
write_file(&tmp_file_path, "existing data")?;
|
||||||
|
|
||||||
|
let err = write_file_atomic(&tmp_file_path, "should not contain this", false).unwrap_err();
|
||||||
|
assert!(err.contains("--force"));
|
||||||
|
|
||||||
|
let actual_data = read_file(&tmp_file_path)?;
|
||||||
|
assert_eq!(actual_data, "existing data");
|
||||||
|
|
||||||
|
tmp_dir.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_file_atomic_overwrite() -> Result<()> {
|
||||||
|
let tmp_dir = tempdir()?;
|
||||||
|
let path = tmp_dir.path().join("test_config.toml");
|
||||||
|
|
||||||
|
write_file(&path, "existing data")?;
|
||||||
|
|
||||||
|
let expected = "test data";
|
||||||
|
write_file_atomic(&path, expected, true).unwrap();
|
||||||
|
|
||||||
|
let actual = read_file(&path)?;
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
|
||||||
|
tmp_dir.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-2
@@ -1,6 +1,6 @@
|
|||||||
use crate::module::ALL_MODULES;
|
use crate::module::ALL_MODULES;
|
||||||
use serde::de::{
|
use serde::de::{
|
||||||
Deserializer, Error, IntoDeserializer, Visitor,
|
self, Deserializer, Error, IntoDeserializer, Visitor,
|
||||||
value::{Error as ValueError, MapDeserializer, SeqDeserializer},
|
value::{Error as ValueError, MapDeserializer, SeqDeserializer},
|
||||||
};
|
};
|
||||||
use std::{cmp::Ordering, fmt};
|
use std::{cmp::Ordering, fmt};
|
||||||
@@ -100,6 +100,20 @@ impl ValueDeserializer<'_> {
|
|||||||
_ => ValueError::custom(msg),
|
_ => ValueError::custom(msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the fitting `de::Unexpected` type description for the given value.
|
||||||
|
/// For use with `Error::invalid_type`.
|
||||||
|
fn serde_unexpected(&self) -> de::Unexpected<'_> {
|
||||||
|
match self.value {
|
||||||
|
ValueRef::Boolean(b) => de::Unexpected::Bool(b),
|
||||||
|
ValueRef::Integer(i) => de::Unexpected::Signed(i),
|
||||||
|
ValueRef::Float(f) => de::Unexpected::Float(f),
|
||||||
|
ValueRef::String(s) => de::Unexpected::Str(s),
|
||||||
|
ValueRef::Array(_v) => de::Unexpected::Other("array"),
|
||||||
|
ValueRef::Table(_v) => de::Unexpected::Other("table"),
|
||||||
|
ValueRef::Datetime(_v) => de::Unexpected::Other("datetime"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> IntoDeserializer<'de> for ValueDeserializer<'de> {
|
impl<'de> IntoDeserializer<'de> for ValueDeserializer<'de> {
|
||||||
@@ -215,10 +229,29 @@ impl<'de> Deserializer<'de> for ValueDeserializer<'de> {
|
|||||||
visitor.visit_newtype_struct(self)
|
visitor.visit_newtype_struct(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deserialize_enum<V>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
_variants: &'static [&'static str],
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Self::Error>
|
||||||
|
where
|
||||||
|
V: Visitor<'de>,
|
||||||
|
{
|
||||||
|
match self.value {
|
||||||
|
// de::Value::StrDeserializer implements de::EnumAccess, so we can just use it.
|
||||||
|
ValueRef::String(s) => visitor.visit_enum(s.into_deserializer()),
|
||||||
|
_ => Err(Self::Error::invalid_type(
|
||||||
|
self.serde_unexpected(),
|
||||||
|
&"string",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle most deserialization cases by deferring to `deserialize_any`.
|
// Handle most deserialization cases by deferring to `deserialize_any`.
|
||||||
serde::forward_to_deserialize_any! {
|
serde::forward_to_deserialize_any! {
|
||||||
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit seq
|
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit seq
|
||||||
bytes byte_buf map unit_struct tuple_struct enum tuple identifier
|
bytes byte_buf map unit_struct tuple_struct tuple identifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +383,72 @@ mod test {
|
|||||||
assert_eq!(result.foo.0, "bar".to_owned());
|
assert_eq!(result.foo.0, "bar".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
enum SampleEnum {
|
||||||
|
FirstItem,
|
||||||
|
Second,
|
||||||
|
ThirdItem,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_enum() {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Sample {
|
||||||
|
foo: SampleEnum,
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = toml::toml! {
|
||||||
|
foo = "first_item"
|
||||||
|
};
|
||||||
|
|
||||||
|
let deserializer = ValueDeserializer::new(&value);
|
||||||
|
|
||||||
|
let result = Sample::deserialize(deserializer).unwrap();
|
||||||
|
assert_eq!(result.foo, SampleEnum::FirstItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_enum_unknown() {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct Sample {
|
||||||
|
foo: SampleEnum,
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = toml::toml! {
|
||||||
|
foo = "unknown"
|
||||||
|
};
|
||||||
|
|
||||||
|
let deserializer = ValueDeserializer::new(&value);
|
||||||
|
|
||||||
|
let result = Sample::deserialize(deserializer);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_enum_invalid_type() {
|
||||||
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct Sample {
|
||||||
|
foo: SampleEnum,
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = toml::toml! {
|
||||||
|
foo = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
let deserializer = ValueDeserializer::new(&value);
|
||||||
|
|
||||||
|
let result = Sample::deserialize(deserializer);
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
Err(serde::de::Error::custom(
|
||||||
|
"invalid type: integer `1`, expected string"
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_deserialize_unknown() {
|
fn test_deserialize_unknown() {
|
||||||
let value = toml::toml! {
|
let value = toml::toml! {
|
||||||
|
|||||||
Reference in New Issue
Block a user