├── .gitignore ├── LICENSE ├── PoshRedisViewer ├── .gitignore ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── PoshRedisViewer.sln ├── PoshRedisViewer │ ├── App.fs │ ├── AsyncEnumerable.fs │ ├── PoshRedisViewer.fsproj │ ├── Redis.fs │ ├── Tasks.fs │ ├── UI.fs │ ├── UILogic.fs │ └── UIUtil.fs ├── PoshRedisViewerHost │ ├── PoshRedisViewerHost.fsproj │ └── Program.fs ├── PoshRedisViewerModule │ ├── GetRedisViewerCommand.fs │ ├── PoshRedisViewerModule.fsproj │ └── PoshRedisViewerModule.psd1 └── global.json ├── README.md ├── Releases ├── 0.0.5 │ ├── En3Tho.FSharp.ComputationExpressions.dll │ ├── En3Tho.FSharp.Extensions.dll │ ├── FSharp.Core.dll │ ├── NStack.dll │ ├── Pipelines.Sockets.Unofficial.dll │ ├── Ply.dll │ ├── PoshRedisViewer.dll │ ├── PoshRedisViewerModule.dll │ ├── PoshRedisViewerModule.psd1 │ ├── StackExchange.Redis.dll │ ├── System.IO.Pipelines.dll │ └── Terminal.Gui.dll ├── 0.0.6 │ ├── En3Tho.FSharp.ComputationExpressions.dll │ ├── En3Tho.FSharp.Extensions.dll │ ├── FSharp.Core.dll │ ├── NStack.dll │ ├── Pipelines.Sockets.Unofficial.dll │ ├── PoshRedisViewer.dll │ ├── PoshRedisViewerModule.dll │ ├── PoshRedisViewerModule.psd1 │ ├── StackExchange.Redis.dll │ ├── System.IO.Pipelines.dll │ └── Terminal.Gui.dll ├── 0.0.7 │ ├── En3Tho.FSharp.ComputationExpressions.dll │ ├── En3Tho.FSharp.Extensions.dll │ ├── FSharp.Core.dll │ ├── NStack.dll │ ├── Pipelines.Sockets.Unofficial.dll │ ├── PoshRedisViewer.dll │ ├── PoshRedisViewerModule.dll │ ├── PoshRedisViewerModule.psd1 │ ├── StackExchange.Redis.dll │ ├── System.IO.Pipelines.dll │ └── Terminal.Gui.dll └── 0.0.8 │ ├── En3Tho.FSharp.ComputationExpressions.dll │ ├── En3Tho.FSharp.Extensions.dll │ ├── FSharp.Core.dll │ ├── NStack.dll │ ├── Pipelines.Sockets.Unofficial.dll │ ├── PoshRedisViewer.dll │ ├── PoshRedisViewerModule.dll │ ├── PoshRedisViewerModule.psd1 │ ├── StackExchange.Redis.dll │ ├── System.IO.Pipelines.dll │ └── Terminal.Gui.dll ├── fish-haddock.png └── img.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | .idea/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # StyleCop 67 | StyleCopReport.xml 68 | 69 | # Files built by Visual Studio 70 | *_i.c 71 | *_p.c 72 | *_h.h 73 | *.ilk 74 | *.meta 75 | *.obj 76 | *.iobj 77 | *.pch 78 | *.pdb 79 | *.ipdb 80 | *.pgc 81 | *.pgd 82 | *.rsp 83 | *.sbr 84 | *.tlb 85 | *.tli 86 | *.tlh 87 | *.tmp 88 | *.tmp_proj 89 | *_wpftmp.csproj 90 | *.log 91 | *.vspscc 92 | *.vssscc 93 | .builds 94 | *.pidb 95 | *.svclog 96 | *.scc 97 | 98 | # Chutzpah Test files 99 | _Chutzpah* 100 | 101 | # Visual C++ cache files 102 | ipch/ 103 | *.aps 104 | *.ncb 105 | *.opendb 106 | *.opensdf 107 | *.sdf 108 | *.cachefile 109 | *.VC.db 110 | *.VC.VC.opendb 111 | 112 | # Visual Studio profiler 113 | *.psess 114 | *.vsp 115 | *.vspx 116 | *.sap 117 | 118 | # Visual Studio Trace Files 119 | *.e2e 120 | 121 | # TFS 2012 Local Workspace 122 | $tf/ 123 | 124 | # Guidance Automation Toolkit 125 | *.gpState 126 | 127 | # ReSharper is a .NET coding add-in 128 | _ReSharper*/ 129 | *.[Rr]e[Ss]harper 130 | *.DotSettings.user 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | # tools/** 313 | # !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ 350 | 351 | # Ionide (cross platform F# VS Code tools) working folder 352 | .ionide/ 353 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Igor Bagdamyan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PoshRedisViewer/.gitignore: -------------------------------------------------------------------------------- 1 | .artifacts/ 2 | -------------------------------------------------------------------------------- /PoshRedisViewer/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 0.0.12 6 | 7 | 8 | 9 | false 10 | 11 | 9, 12 | 0760, 13 | 96 , 14 | 3535, 15 | 42, 16 | 77, 17 | 3513, 18 | 1204 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /PoshRedisViewer/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ModuleVersion) 5 | $(ModuleVersion) 6 | $(ModuleVersion) 7 | 8 | 9 | -------------------------------------------------------------------------------- /PoshRedisViewer/Directory.Packages.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | true 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PoshRedisViewer", "PoshRedisViewer\PoshRedisViewer.fsproj", "{BA3A07AF-CB3B-47BB-B373-36A139ED3249}" 4 | EndProject 5 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PoshRedisViewerHost", "PoshRedisViewerHost\PoshRedisViewerHost.fsproj", "{D24C16C7-1D8E-4510-82F1-F1B6A6EC1F89}" 6 | EndProject 7 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PoshRedisViewerModule", "PoshRedisViewerModule\PoshRedisViewerModule.fsproj", "{9519E9D2-FF3C-4D41-9E24-BC832B66EAB7}" 8 | EndProject 9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{226CB7CF-2C39-4603-8C69-4E64A1259F78}" 10 | ProjectSection(SolutionItems) = preProject 11 | ..\.gitignore = ..\.gitignore 12 | ..\LICENSE = ..\LICENSE 13 | ..\README.md = ..\README.md 14 | Directory.Packages.props = Directory.Packages.props 15 | Directory.Build.props = Directory.Build.props 16 | Directory.Build.targets = Directory.Build.targets 17 | global.json = global.json 18 | EndProjectSection 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {BA3A07AF-CB3B-47BB-B373-36A139ED3249}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {BA3A07AF-CB3B-47BB-B373-36A139ED3249}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {BA3A07AF-CB3B-47BB-B373-36A139ED3249}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {BA3A07AF-CB3B-47BB-B373-36A139ED3249}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {D24C16C7-1D8E-4510-82F1-F1B6A6EC1F89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {D24C16C7-1D8E-4510-82F1-F1B6A6EC1F89}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {D24C16C7-1D8E-4510-82F1-F1B6A6EC1F89}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {D24C16C7-1D8E-4510-82F1-F1B6A6EC1F89}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {9519E9D2-FF3C-4D41-9E24-BC832B66EAB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {9519E9D2-FF3C-4D41-9E24-BC832B66EAB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {9519E9D2-FF3C-4D41-9E24-BC832B66EAB7}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {9519E9D2-FF3C-4D41-9E24-BC832B66EAB7}.Release|Any CPU.Build.0 = Release|Any CPU 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer/App.fs: -------------------------------------------------------------------------------- 1 | module PoshRedisViewer.App 2 | 3 | open System.Threading 4 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.SemaphoreSlimTask 5 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.SynchronizationContextTask 6 | open PoshRedisViewer.UIUtil 7 | open StackExchange.Redis 8 | open Terminal.Gui 9 | 10 | let run(multiplexer: IConnectionMultiplexer) = 11 | Application.Init() 12 | 13 | UITask.Builder <- SynchronizationContextTaskBuilder(SynchronizationContext.Current) 14 | RedisTask.Builder <- SemaphoreSlimTaskBuilder(SemaphoreSlim(1)) 15 | Clipboard.MiniClipboard <- MiniClipboard(Application.Driver.Clipboard) 16 | 17 | UI.makeViews() 18 | |> UI.setupViewsPosition 19 | |> UILogic.setupViewsLogic multiplexer 20 | |> fun views -> 21 | let handler = UILogic.makeErrorHandler views 22 | Application.Run(views.Top, handler) 23 | 24 | let shutdown() = Application.Shutdown() -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer/AsyncEnumerable.fs: -------------------------------------------------------------------------------- 1 | module PoshRedisViewer.AsyncEnumerable 2 | open System 3 | open System.Threading 4 | open System.Threading.Tasks 5 | open En3Tho.FSharp.Extensions 6 | open En3Tho.FSharp.ComputationExpressions.Tasks 7 | open System.Collections.Generic 8 | 9 | let toResizeArray (enumerable: IAsyncEnumerable<'a>) = vtask { 10 | let result = ResizeArray() 11 | use enumerator = enumerable.GetAsyncEnumerator() 12 | 13 | let mutable goNext = true 14 | while goNext do 15 | match! enumerator.MoveNextAsync() with 16 | | true -> 17 | result.Add enumerator.Current 18 | | _ -> 19 | goNext <- false 20 | 21 | return result 22 | } 23 | 24 | let dispose2 (enumerator1: #IAsyncEnumerator<'a>)(enumerator2: #IAsyncEnumerator<'b>) = uvtask { 25 | let mutable exn1 = null 26 | let mutable exn2 = null 27 | try 28 | do! enumerator1.DisposeAsync() 29 | with e -> 30 | exn1 <- e 31 | try 32 | do! enumerator2.DisposeAsync() 33 | with e -> 34 | exn2 <- e 35 | 36 | match exn1, exn2 with 37 | | null, null -> 38 | () 39 | | exn, null 40 | | null, exn -> 41 | Exception.reraise exn 42 | | _ -> 43 | raise ^ AggregateException(exn1, exn2) 44 | } 45 | 46 | type [] private EmptyAsyncEnumerator<'a> = 47 | interface IAsyncEnumerator<'a> with 48 | member this.Current = invalidOp "Current should not be used with empty async enumerable" 49 | member this.DisposeAsync() = ValueTask() 50 | member this.MoveNextAsync() = ValueTask.FromResult(false) 51 | 52 | type private EmptyAsyncEnumerable<'a>() = 53 | static let cachedEnumerator = EmptyAsyncEnumerator<'a>() :> IAsyncEnumerator<'a> 54 | static let cachedInstance = EmptyAsyncEnumerable<'a>() 55 | static member Empty = cachedInstance 56 | 57 | interface IAsyncEnumerable<'a> with 58 | member this.GetAsyncEnumerator(_) = cachedEnumerator 59 | 60 | type private MapAsyncEnumerableEnumerator<'a, 'b>(source1: IAsyncEnumerable<'a>, map: 'a -> 'b) = 61 | let enumerator = source1.GetAsyncEnumerator() 62 | 63 | interface IAsyncEnumerator<'b> with 64 | member this.Current = enumerator.Current |> map 65 | member this.DisposeAsync() = enumerator.DisposeAsync() 66 | member this.MoveNextAsync() = enumerator.MoveNextAsync() 67 | 68 | type private AppendAsyncEnumerableEnumerator<'a>(source1: IAsyncEnumerable<'a>, source2: IAsyncEnumerable<'a>, cancellationToken: CancellationToken) = 69 | let mutable enumerator = source1.GetAsyncEnumerator() 70 | let mutable enumerator2 = source2.GetAsyncEnumerator() 71 | let mutable state = 0 72 | 73 | interface IAsyncEnumerator<'a> with 74 | member this.Current = 75 | match state with 76 | | 0 -> 77 | enumerator.Current 78 | | _ -> 79 | enumerator2.Current 80 | 81 | member this.DisposeAsync() = dispose2 enumerator enumerator2 82 | 83 | member this.MoveNextAsync() = vtask { 84 | cancellationToken.ThrowIfCancellationRequested() 85 | match state with 86 | | 0 -> 87 | match! enumerator.MoveNextAsync() with 88 | | true -> return true 89 | | _ -> 90 | state <- 1 91 | return! enumerator2.MoveNextAsync() 92 | | _ -> 93 | return! enumerator2.MoveNextAsync() 94 | } 95 | 96 | type private AppendAsyncEnumerable<'a>(source1: IAsyncEnumerable<'a>, source2: IAsyncEnumerable<'a>) = 97 | interface IAsyncEnumerable<'a> with 98 | member this.GetAsyncEnumerator(cancellationToken) = AppendAsyncEnumerableEnumerator<'a>(source1, source2, cancellationToken) 99 | 100 | let empty<'a> = EmptyAsyncEnumerable<'a>.Empty :> IAsyncEnumerable<'a> 101 | 102 | let append source1 source2 = 103 | AppendAsyncEnumerable<'a>(source1, source2) :> IAsyncEnumerable<'a> 104 | 105 | let map map source = { new IAsyncEnumerable<'b> with member _.GetAsyncEnumerator(_) = MapAsyncEnumerableEnumerator<'a, 'b>(source, map) } -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer/PoshRedisViewer.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | PowershellModule 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer/Redis.fs: -------------------------------------------------------------------------------- 1 | module PoshRedisViewer.Redis 2 | 3 | open System 4 | open System.Diagnostics 5 | open StackExchange.Redis 6 | open En3Tho.FSharp.Extensions 7 | open En3Tho.FSharp.ComputationExpressions.Tasks 8 | 9 | type StackExchangeRedisResult = RedisResult 10 | 11 | let inline toString x = x.ToString() 12 | let inline toStringOrEmpty x = if Object.ReferenceEquals(x, null) then "" else x.ToString() 13 | 14 | type RedisHashMember = { 15 | Field: string 16 | Value: string 17 | } 18 | 19 | type RedisSortedSetMember = { 20 | Score: float 21 | Value: string 22 | } 23 | 24 | type RedisListMember = { 25 | Index: int 26 | Value: string 27 | } 28 | 29 | type RedisResult = 30 | | RedisString of string 31 | | RedisList of RedisListMember array 32 | | RedisHash of RedisHashMember array 33 | | RedisSet of string array 34 | | RedisSortedSet of RedisSortedSetMember array 35 | | RedisStream 36 | | RedisError of exn 37 | | RedisMultiResult of RedisResult array 38 | | RedisNone 39 | 40 | module RedisResult = 41 | 42 | let fromRedisKey (redisKey: RedisKey) = 43 | RedisString (toString redisKey) 44 | 45 | let rec fromStackExchangeRedisResult (redisResult: StackExchangeRedisResult) = 46 | if redisResult.IsNull then 47 | RedisResult.RedisNone 48 | else 49 | match redisResult.Resp2Type with 50 | | ResultType.SimpleString -> 51 | RedisString (toString redisResult) 52 | | ResultType.None -> 53 | RedisNone 54 | | ResultType.Error -> 55 | RedisError (Exception(toString redisResult)) 56 | | ResultType.Integer -> 57 | RedisString (toString redisResult) 58 | | ResultType.BulkString -> 59 | RedisString (toString redisResult) 60 | | ResultType.Array -> 61 | let results = ecast<_, StackExchangeRedisResult[]> redisResult 62 | RedisMultiResult (results |> Array.map fromStackExchangeRedisResult) 63 | | _ -> 64 | RedisError (Exception("Unknown type of Enum")) 65 | 66 | type KeySearchDatabase = 67 | | Single of Database: int 68 | | Range of From: int * To: int 69 | 70 | module KeyFormatter = 71 | let getFormattedKeyString database redisKey = $"{database}. {toString redisKey}" 72 | 73 | let getDatabaseFromHeader (formattedKey: string) = 74 | let indexOfDot = formattedKey.IndexOf('.') 75 | match indexOfDot with 76 | | -1 -> 77 | Debug.Fail("Should not be happening") 78 | 0 79 | | _ -> 80 | Int32.Parse(formattedKey.AsSpan(0, indexOfDot)) 81 | 82 | let trimDatabaseHeader (formattedKey: string) = 83 | let indexOfDot = formattedKey.IndexOf('.') 84 | match indexOfDot with 85 | | -1 -> 86 | Debug.Fail("Should not be happening") 87 | formattedKey 88 | | _ -> 89 | formattedKey.Substring(indexOfDot + 2) 90 | 91 | let getDatabaseAndOriginalKeyFromFormattedKeyString (formattedKey: string) = 92 | getDatabaseFromHeader formattedKey, trimDatabaseHeader formattedKey 93 | 94 | module RedisReader = 95 | 96 | let connect (user: string option) (password: string option) (endPoint: string) = redisTask { 97 | let connectionOptions = ConfigurationOptions() 98 | connectionOptions.EndPoints.Add endPoint 99 | 100 | user |> Option.iter ^ fun user -> connectionOptions.User <- user 101 | password |> Option.iter ^ fun password -> connectionOptions.Password <- password 102 | 103 | return! ConnectionMultiplexer.ConnectAsync(connectionOptions) 104 | } 105 | 106 | let getKeys (multiplexer: IConnectionMultiplexer) (database: KeySearchDatabase) (pattern: string) = redisTask { 107 | try 108 | // always 1 endpoint in this version 109 | let server = multiplexer.GetServer(multiplexer.GetEndPoints()[0]) 110 | 111 | let getKeys database = 112 | server.KeysAsync(database, RedisValue pattern) 113 | |> AsyncEnumerable.map (KeyFormatter.getFormattedKeyString database) 114 | 115 | let! keys = 116 | match database with 117 | | Single database -> 118 | getKeys database 119 | | Range(from, to') -> 120 | seq { 121 | for database = from to to' do 122 | getKeys database 123 | } 124 | |> Seq.fold AsyncEnumerable.append AsyncEnumerable.empty 125 | 126 | |> AsyncEnumerable.toResizeArray 127 | 128 | return 129 | keys.ToArray() 130 | |> RedisSet 131 | with 132 | | e -> 133 | return RedisError e 134 | } 135 | 136 | let execCommand (multiplexer: IConnectionMultiplexer) database (command: string) = redisTask { 137 | try 138 | let database = multiplexer.GetDatabase database 139 | let commandAndArgs = command.Split(" ", StringSplitOptions.RemoveEmptyEntries) 140 | let command = commandAndArgs[0] 141 | let args = commandAndArgs[1..] |> Array.map box 142 | 143 | let! result = database.ExecuteAsync(command, args) 144 | return RedisResult.fromStackExchangeRedisResult result 145 | with 146 | | e -> 147 | return RedisError e 148 | } 149 | 150 | let getKeyType (multiplexer: IConnectionMultiplexer) database (key: string) = redisTask { 151 | let key = RedisKey key 152 | let database = multiplexer.GetDatabase database 153 | return! database.KeyTypeAsync(key).AsResult() 154 | } 155 | 156 | let getKeyValue (multiplexer: IConnectionMultiplexer) database (key: string) = redisTask { 157 | match! getKeyType multiplexer database key with 158 | | Ok keyType -> 159 | let key = RedisKey key 160 | let database = multiplexer.GetDatabase database 161 | match keyType with 162 | | RedisType.Hash -> 163 | let! hashFields = database.HashGetAllAsync key 164 | return 165 | hashFields 166 | |> Array.map ^ fun hashField -> 167 | { Field = toString hashField.Name; Value = toString hashField.Value } 168 | |> RedisHash 169 | 170 | | RedisType.Set -> 171 | let! setMembers = database.SetMembersAsync key 172 | return 173 | setMembers 174 | |> Array.map toString 175 | |> RedisSet 176 | 177 | | RedisType.String -> 178 | let! str = database.StringGetAsync key 179 | return 180 | toString str 181 | |> RedisString 182 | 183 | | RedisType.List -> 184 | let! listMembers = database.ListRangeAsync key 185 | return 186 | listMembers 187 | |> Array.mapi ^ fun i value -> 188 | { Index = i; Value = toString value } 189 | |> RedisList 190 | 191 | | RedisType.SortedSet -> 192 | let! setMembers = database.SortedSetScanAsync(key) |> AsyncEnumerable.toResizeArray 193 | return 194 | setMembers 195 | |> Seq.map ^ fun setMember -> 196 | { Score = setMember.Score; Value = toString setMember.Element } 197 | |> Seq.toArray 198 | |> RedisSortedSet 199 | 200 | | RedisType.None -> 201 | return RedisNone 202 | 203 | | RedisType.Stream -> 204 | return RedisError (NotSupportedException("Redis streams are not supported")) 205 | 206 | | RedisType.Unknown -> 207 | return RedisError (Exception("Unknown entity")) 208 | 209 | | _ -> 210 | return RedisError (Exception("Unrecognized enum value")) 211 | | Error exn -> 212 | return RedisError exn 213 | } -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer/Tasks.fs: -------------------------------------------------------------------------------- 1 | namespace PoshRedisViewer 2 | 3 | open En3Tho.FSharp.Extensions 4 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.SemaphoreSlimTask 5 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.SynchronizationContextTask 6 | 7 | [] 8 | type UITask() = 9 | static member val Builder = nullRef with get, set 10 | 11 | [] 12 | type RedisTask() = 13 | static member val Builder = nullRef with get, set 14 | 15 | [] 16 | type Tasks() = 17 | 18 | static member uiTask = UITask.Builder 19 | static member redisTask = RedisTask.Builder -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer/UI.fs: -------------------------------------------------------------------------------- 1 | module PoshRedisViewer.UI 2 | 3 | open FSharp.Reflection 4 | open En3Tho.FSharp.Extensions 5 | open En3Tho.FSharp.ComputationExpressions 6 | open En3Tho.FSharp.ComputationExpressions.SCollectionBuilder 7 | open NStack 8 | open PoshRedisViewer.UIUtil 9 | open Terminal.Gui 10 | 11 | type Views = { 12 | CommandFrameView: FrameView 13 | CommandTextField: TextField 14 | DbPickerCheckBox: CheckBox 15 | DbPickerComboBox: ComboBox 16 | DbPickerFrameView: FrameView 17 | KeyQueryFilterFrameView: FrameView 18 | KeyQueryFilterTextField: TextField 19 | KeyQueryFilterTypeComboBox: ComboBox 20 | KeyQueryFilterTypeFrameView: FrameView 21 | KeyQueryFrameView: FrameView 22 | KeyQueryTextField: TextField 23 | KeysFrameView: FrameView 24 | KeysListView: ListView 25 | ResultFilterFrameView: FrameView 26 | ResultFilterTextField: TextField 27 | ResultsFrameView: FrameView 28 | ResultsListView: ListView 29 | Top: Toplevel 30 | Window: Window 31 | } 32 | 33 | let makeViews() = 34 | let window = Window( 35 | Width = Dim.Fill(), 36 | Height = Dim.Fill() 37 | ) 38 | 39 | let keyQueryFrameView = FrameView(ustr "KeyQuery", 40 | Width = Dim.Percent(45f), 41 | Height = Dim.Sized 3 42 | ) 43 | 44 | let keyQueryTextField = TextField( 45 | Width = Dim.Fill(), 46 | Height = Dim.Fill(), 47 | Text = ustr "*" 48 | ) 49 | 50 | let keyQueryFilterFrameView = FrameView(ustr "Keys Filter", 51 | X = Pos.Right keyQueryFrameView, 52 | Width = Dim.Percent(30f), 53 | Height = Dim.Sized 3 54 | ) 55 | 56 | let keyQueryFilterTextField = TextField( 57 | Width = Dim.Fill(), 58 | Height = Dim.Fill(), 59 | Text = ustr "" 60 | ) 61 | 62 | let keyQueryFilterTypeFrameView = FrameView(ustr "Filter Type", 63 | X = Pos.Right keyQueryFilterFrameView, 64 | Width = Dim.Percent(10f), 65 | Height = Dim.Sized 3 66 | ) 67 | 68 | let keyQueryFilterTypeComboBox = ComboBox(ustr "Contains", 69 | X = Pos.Left keyQueryFilterTypeFrameView + Pos.At 1, 70 | Y = Pos.Top keyQueryFilterTypeFrameView + Pos.At 1, 71 | Width = Dim.Width keyQueryFilterTypeFrameView - Dim.Sized 2, 72 | Height = Dim.Sized (FSharpType.GetUnionCases(typeof).Length + 1), 73 | ReadOnly = true 74 | ) 75 | 76 | keyQueryFilterTypeComboBox.SetSource([| 77 | FilterType.Contains 78 | FilterType.Regex 79 | |]) 80 | 81 | let dbPickerFrameView = FrameView(ustr "DB", 82 | X = Pos.Right keyQueryFilterTypeFrameView, 83 | Width = Dim.Percent(15.f), 84 | Height = Dim.Sized 3 85 | ) 86 | 87 | let dbPickerComboBox = ComboBox(ustr "0", 88 | X = Pos.Left dbPickerFrameView + Pos.At 1, 89 | Y = Pos.Top dbPickerFrameView + Pos.At 1, 90 | Width = Dim.Width dbPickerFrameView - Dim.Sized 15, 91 | Height = Dim.Sized 17, 92 | ReadOnly = true 93 | ) 94 | 95 | dbPickerComboBox.SetSource([| 96 | for i = 0 to 15 do ustr (string i) 97 | |]) 98 | 99 | let dbPickerCheckBox = CheckBox(ustr "Query All", 100 | X = Pos.Right dbPickerComboBox + Pos.At 1, 101 | Y = Pos.Top dbPickerComboBox 102 | ) 103 | 104 | let keysFrameView = FrameView(ustr "Keys", 105 | Y = Pos.Bottom keyQueryFrameView, 106 | Width = Dim.Fill(), 107 | Height = Dim.Percent(50.f) - Dim.Sized 3 108 | ) 109 | 110 | let keysListView = ListView( 111 | Width = Dim.Fill(), 112 | Height = Dim.Fill() 113 | ) 114 | 115 | let resultsFrameView = FrameView(ustr "Results", 116 | Y = Pos.Bottom keysFrameView, 117 | Width = Dim.Fill(), 118 | Height = Dim.Fill() - Dim.Sized 3 119 | ) 120 | 121 | let resultsListView = ListView( 122 | Width = Dim.Fill(), 123 | Height = Dim.Fill() 124 | ) 125 | 126 | let commandFrameView = FrameView(ustr "Command", 127 | Y = Pos.Bottom resultsFrameView, 128 | Width = Dim.Percent(70.f), 129 | Height = Dim.Sized 3 130 | ) 131 | 132 | let commandTextField = TextField( 133 | Width = Dim.Fill(), 134 | Height = Dim.Fill(), 135 | Text = ustring.Empty 136 | ) 137 | 138 | let resultFilterFrameView = FrameView(ustr "Results Filter", 139 | X = Pos.Right commandFrameView, 140 | Y = Pos.Bottom resultsFrameView, 141 | Width = Dim.Percent(30.f), 142 | Height = Dim.Sized 3 143 | ) 144 | 145 | let resultFilterTextField = TextField( 146 | Width = Dim.Fill(), 147 | Height = Dim.Fill(), 148 | Text = ustr "" 149 | ) 150 | 151 | let views: Views = { 152 | Top = Application.Top 153 | Window = window 154 | KeyQueryFrameView = keyQueryFrameView 155 | KeyQueryTextField = keyQueryTextField 156 | KeyQueryFilterFrameView = keyQueryFilterFrameView 157 | KeyQueryFilterTextField = keyQueryFilterTextField 158 | KeyQueryFilterTypeFrameView = keyQueryFilterTypeFrameView 159 | KeyQueryFilterTypeComboBox = keyQueryFilterTypeComboBox 160 | DbPickerFrameView = dbPickerFrameView 161 | DbPickerComboBox = dbPickerComboBox 162 | DbPickerCheckBox = dbPickerCheckBox 163 | KeysFrameView = keysFrameView 164 | KeysListView = keysListView 165 | ResultsFrameView = resultsFrameView 166 | ResultsListView = resultsListView 167 | CommandFrameView = commandFrameView 168 | CommandTextField = commandTextField 169 | ResultFilterFrameView = resultFilterFrameView 170 | ResultFilterTextField = resultFilterTextField 171 | } 172 | 173 | views 174 | 175 | let setupViewsPosition (views: Views) = 176 | views.Top { 177 | views.Window { 178 | views.KeyQueryFrameView { 179 | views.KeyQueryTextField 180 | } 181 | 182 | views.KeyQueryFilterFrameView { 183 | views.KeyQueryFilterTextField 184 | } 185 | 186 | views.KeyQueryFilterTypeFrameView 187 | views.KeyQueryFilterTypeComboBox 188 | 189 | views.DbPickerFrameView 190 | views.DbPickerComboBox 191 | views.DbPickerCheckBox 192 | 193 | views.KeysFrameView { 194 | views.KeysListView 195 | } 196 | 197 | views.ResultsFrameView { 198 | views.ResultsListView 199 | } 200 | 201 | views.CommandFrameView { 202 | views.CommandTextField 203 | } 204 | 205 | views.ResultFilterFrameView { 206 | views.ResultFilterTextField 207 | } 208 | } 209 | } |> ignore 210 | 211 | // what do to with ui? 212 | // something that tzind suggested? 213 | views.Window.Subviews[0].BringSubviewToFront(views.KeyQueryFilterTypeComboBox) 214 | views.Window.Subviews[0].BringSubviewToFront(views.DbPickerComboBox) 215 | views.Window.Subviews[0].BringSubviewToFront(views.DbPickerCheckBox) 216 | 217 | views -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer/UILogic.fs: -------------------------------------------------------------------------------- 1 | module PoshRedisViewer.UILogic 2 | 3 | open System 4 | open En3Tho.FSharp.Extensions 5 | open PoshRedisViewer.Redis 6 | open PoshRedisViewer.UI 7 | open PoshRedisViewer.UIUtil 8 | open Terminal.Gui 9 | 10 | let makeErrorHandler (views: Views) = 11 | fun (exn: Exception) -> 12 | views.ResultsListView.SetSource(exn.ToString().Split(Environment.NewLine)) 13 | true 14 | 15 | let getFilter (views: Views) = 16 | let filterType = 17 | match views.KeyQueryFilterTypeComboBox.SelectedItem with 18 | | 0 | 1 as idx -> 19 | views.KeyQueryFilterTypeComboBox.Source.ToList()[idx] :?> FilterType 20 | | _ -> 21 | FilterType.Contains 22 | 23 | match filterType with 24 | | FilterType.Contains -> 25 | Filter.stringContains 26 | | FilterType.Regex -> 27 | Filter.regex 28 | 29 | let filterBy query (views: Views) keys = 30 | match query with 31 | | String.NullOrWhiteSpace -> 32 | keys 33 | | _ -> 34 | let filter = getFilter views 35 | keys |> Array.filter (filter query) 36 | 37 | let filterKeyQueryResult results views = 38 | let query = Ustr.toString views.KeyQueryFilterTextField.Text 39 | results |> filterBy query views 40 | 41 | let filterCommandResult (views: Views) results = 42 | let query = Ustr.toString views.ResultFilterTextField.Text 43 | results |> filterBy query views 44 | 45 | let updateKeyQueryFieldsWithNewState (uiState: UIState) (views: Views) state = 46 | uiState.KeyQueryResultState <- state 47 | views.KeysFrameView.Title <- uiState.KeyQueryResultState |> KeyQueryResultState.toString |> ustr 48 | views.KeysListView.SetSource state.Keys 49 | 50 | let updateResultsFieldsWithNewState (uiState: UIState) (views: Views) state = 51 | uiState.ResultsState <- state 52 | views.ResultsFrameView.Title <- state |> ResultsState.toString |> ustr 53 | views.ResultsListView.SetSource state.Result 54 | 55 | let getDataBase (views: Views) = 56 | if views.DbPickerCheckBox.Checked then 57 | KeySearchDatabase.Range (0, 15) 58 | else 59 | KeySearchDatabase.Single views.DbPickerComboBox.SelectedItem 60 | 61 | module KeyQueryTextField = 62 | 63 | let addKeyDownEvenProcessing (uiState: UIState) (views: Views) (keyQueryTextField: TextField) = 64 | let filterSourceAndSetKeyQueryResultFromHistory keyQuery keys time = 65 | keyQueryTextField.Text <- ustr keyQuery 66 | let newKeyQueryState = 67 | { uiState.KeyQueryResultState with 68 | Keys = views |> filterKeyQueryResult keys 69 | FromHistory = true 70 | Time = time 71 | } 72 | updateKeyQueryFieldsWithNewState uiState views newKeyQueryState 73 | 74 | keyQueryTextField.add_KeyDown (fun keyDownEvent -> 75 | match keyDownEvent.KeyEvent.Key with 76 | | Key.Enter -> ignore ^ uiTask { 77 | use _ = defer(keyQueryTextField, fun it -> it.Enabled <- true) 78 | 79 | keyQueryTextField.Enabled <- false 80 | views.KeysFrameView.Title <- ustr "Keys (processing)" 81 | let database = getDataBase views 82 | let pattern = keyQueryTextField.Text.ToString() 83 | 84 | let! keys = 85 | pattern 86 | |> RedisReader.getKeys uiState.Multiplexer database 87 | |> Task.map RedisResult.toStringArray 88 | keys |> Array.sortInPlace 89 | 90 | let time = DateTimeOffset.Now 91 | uiState.KeyQueryHistory.Add(pattern, (keys, time)) 92 | 93 | let newKeyQueryResultState = 94 | { uiState.KeyQueryResultState with 95 | Keys = views |> filterKeyQueryResult keys 96 | FromHistory = false 97 | Time = time 98 | } 99 | 100 | updateKeyQueryFieldsWithNewState uiState views newKeyQueryResultState 101 | } 102 | | Key.CursorUp -> 103 | match uiState.KeyQueryHistory.Up() with 104 | | ValueSome { Key = keyQuery; Value = source, time; } -> 105 | filterSourceAndSetKeyQueryResultFromHistory keyQuery source time 106 | | _ -> () 107 | 108 | | Key.CursorDown -> 109 | match uiState.KeyQueryHistory.Down() with 110 | | ValueSome { Key = keyQuery; Value = source, time; } -> 111 | filterSourceAndSetKeyQueryResultFromHistory keyQuery source time 112 | | _ -> () 113 | 114 | | _ -> () 115 | ) 116 | keyQueryTextField 117 | 118 | module KeyQueryFilterTextField = 119 | 120 | let private processEnterKey (uiState: UIState) (views: Views) (keyQueryFilterTextField: TextField) = 121 | match uiState.KeyQueryHistory.TryReadCurrent() with 122 | | ValueSome { Value = keys, time } -> 123 | let filter = Ustr.toString keyQueryFilterTextField.Text 124 | let newKeyQueryResultState = 125 | { uiState.KeyQueryResultState with 126 | Keys = views |> filterKeyQueryResult keys 127 | Filtered = not ^ String.IsNullOrEmpty filter 128 | Time = time 129 | } 130 | updateKeyQueryFieldsWithNewState uiState views newKeyQueryResultState 131 | | _ -> () 132 | 133 | let addKeyDownEventProcessing (uiState: UIState) (views: Views) (keyQueryFilterTextField: TextField) = 134 | keyQueryFilterTextField.add_KeyDown (fun keyDownEvent -> 135 | match keyDownEvent.KeyEvent.Key with 136 | | Key.Enter -> 137 | processEnterKey uiState views keyQueryFilterTextField 138 | keyDownEvent.Handled <- true 139 | | _ -> () 140 | ) 141 | keyQueryFilterTextField 142 | 143 | module KeysListView = 144 | 145 | let private fetchNewKeyInfo (uiState: UIState) (views: Views) key = ignore ^ uiTask { 146 | let database, key = key.ToString() |> KeyFormatter.getDatabaseAndOriginalKeyFromFormattedKeyString 147 | views.ResultsFrameView.Title <- ustr "Results (processing)" 148 | 149 | let! keyValue = key |> RedisReader.getKeyValue uiState.Multiplexer database 150 | let results = keyValue |> RedisResult.toStringArray 151 | 152 | uiState.ResultsFromKeyQuery <- ValueSome results 153 | { uiState.ResultsState with 154 | Result = results |> filterCommandResult views 155 | ResultType = RedisResult.getInformationText keyValue 156 | FromHistory = false 157 | Time = DateTimeOffset.Now 158 | } |> updateResultsFieldsWithNewState uiState views 159 | } 160 | 161 | 162 | let addSelectedItemChangedEventProcessing (uiState: UIState) (views: Views) (keysListView: ListView) = 163 | keysListView.add_SelectedItemChanged (fun selectedItemChangedEvent -> 164 | match selectedItemChangedEvent.Value with 165 | | null -> 166 | () 167 | | value -> 168 | fetchNewKeyInfo uiState views value 169 | ) 170 | keysListView 171 | 172 | module CommandTextField = 173 | 174 | let private filterSourceAndSetCommandResultFromHistory (uiState: UIState) (views: Views) (commandTextField: TextField) 175 | command commandResult time = 176 | commandTextField.Text <- ustr command 177 | let newResultsState = 178 | { uiState.ResultsState with 179 | Result = commandResult |> filterCommandResult views 180 | ResultType = "Command" 181 | FromHistory = true 182 | Time = time 183 | } 184 | 185 | updateResultsFieldsWithNewState uiState views newResultsState 186 | 187 | let private processEnterKey (uiState: UIState) (views: Views) (commandTextField: TextField) = ignore ^ uiTask { 188 | use _ = defer(commandTextField, fun it -> it.Enabled <- true) 189 | 190 | commandTextField.Enabled <- false 191 | views.ResultsFrameView.Title <- ustr "Results (Processing)" 192 | let database = views.DbPickerComboBox.SelectedItem 193 | let command = commandTextField.Text.ToString() 194 | 195 | let! commandResult = 196 | command 197 | |> RedisReader.execCommand uiState.Multiplexer database 198 | |> Task.map RedisResult.toStringArray 199 | 200 | let time = DateTimeOffset.Now 201 | uiState.ResultsHistory.Add(command, (commandResult, time)) 202 | uiState.ResultsFromKeyQuery <- ValueNone 203 | 204 | let filter = Ustr.toString views.ResultFilterTextField.Text 205 | let newResultsState = 206 | { uiState.ResultsState with 207 | Result = commandResult |> filterCommandResult views 208 | ResultType = "Command" 209 | Filtered = not ^ String.IsNullOrEmpty filter 210 | Time = time 211 | } 212 | updateResultsFieldsWithNewState uiState views newResultsState 213 | } 214 | 215 | let private processCursorUpKey (uiState: UIState) (views: Views) (commandTextField: TextField) = 216 | match uiState.ResultsHistory.Up() with 217 | | ValueSome { Key = command; Value = results, time } -> 218 | filterSourceAndSetCommandResultFromHistory uiState views commandTextField command results time 219 | | _ -> () 220 | 221 | let private processCursorDownKey (uiState: UIState) (views: Views) (commandTextField: TextField) = 222 | match uiState.ResultsHistory.Down() with 223 | | ValueSome { Key = command; Value = results, time } -> 224 | filterSourceAndSetCommandResultFromHistory uiState views commandTextField command results time 225 | | _ -> () 226 | 227 | let addKeyDownEventProcessing (uiState: UIState) (views: Views) (commandTextField: TextField) = 228 | commandTextField.add_KeyDown (fun keyDownEvent -> 229 | match keyDownEvent.KeyEvent.Key with 230 | | Key.Enter -> 231 | processEnterKey uiState views commandTextField 232 | keyDownEvent.Handled <- true 233 | 234 | | Key.CursorUp -> 235 | processCursorUpKey uiState views commandTextField 236 | keyDownEvent.Handled <- true 237 | 238 | | Key.CursorDown -> 239 | processCursorDownKey uiState views commandTextField 240 | keyDownEvent.Handled <- true 241 | 242 | | _ -> () 243 | ) 244 | commandTextField 245 | 246 | module ResultFilterTextField = 247 | 248 | let private processEnterKey (uiState: UIState) (views: Views) (resultFilterTextField: TextField) = 249 | match uiState.ResultsFromKeyQuery, uiState.ResultsHistory.TryReadCurrent() with 250 | | ValueSome commandResult, _ 251 | | _, ValueSome { Value = commandResult, _ } -> 252 | let filter = Ustr.toString resultFilterTextField.Text 253 | let newResultState = 254 | { uiState.ResultsState with 255 | Result = commandResult |> filterCommandResult views 256 | Filtered = not ^ String.IsNullOrEmpty filter 257 | } 258 | updateResultsFieldsWithNewState uiState views newResultState 259 | | _ -> () 260 | 261 | let addKeyDownEventProcessing (uiState: UIState) (views: Views) (resultFilterTextField: TextField) = 262 | resultFilterTextField.add_KeyDown (fun keyDownEvent -> 263 | match keyDownEvent.KeyEvent.Key with 264 | | Key.Enter -> 265 | processEnterKey uiState views resultFilterTextField 266 | keyDownEvent.Handled <- true 267 | | _ -> () 268 | ) 269 | resultFilterTextField 270 | 271 | let setupViewsLogic multiplexer (views: Views) = 272 | 273 | let uiState = { 274 | Multiplexer = multiplexer 275 | KeyQueryResultState = { KeyQueryResultState.Keys = [||]; Filtered = false; FromHistory = false; Time = DateTimeOffset() } 276 | ResultsState = { ResultsState.Result = [||]; ResultType = ""; Filtered = false; FromHistory = false; Time = DateTimeOffset() } 277 | ResultsFromKeyQuery = ValueSome [||] 278 | KeyQueryHistory = ResultHistoryCache(100) 279 | ResultsHistory = ResultHistoryCache(100) 280 | } 281 | 282 | views.KeyQueryTextField 283 | |> View.preventCursorUpDownKeyPressedEvents 284 | |> TextField.addCopyPasteSupportWithMiniClipboard 285 | |> KeyQueryTextField.addKeyDownEvenProcessing uiState views 286 | |> ignore 287 | 288 | views.KeyQueryFilterTextField 289 | |> View.preventCursorUpDownKeyPressedEvents 290 | |> TextField.addCopyPasteSupportWithMiniClipboard 291 | |> KeyQueryFilterTextField.addKeyDownEventProcessing uiState views 292 | |> ignore 293 | 294 | views.DbPickerComboBox 295 | |> View.preventCursorUpDownKeyPressedEvents 296 | |> ignore 297 | 298 | views.KeysListView 299 | |> ListView.addValueCopyOnRightClick KeyFormatter.trimDatabaseHeader 300 | |> ListView.addValueCopyOnCopyHotKey KeyFormatter.trimDatabaseHeader 301 | |> KeysListView.addSelectedItemChangedEventProcessing uiState views 302 | |> ignore 303 | 304 | views.ResultsListView 305 | |> ListView.addValueCopyOnRightClick id 306 | |> ListView.addValueCopyOnCopyHotKey id 307 | |> ListView.addDetailedViewOnEnterKey 308 | |> ignore 309 | 310 | views.CommandTextField 311 | |> View.preventCursorUpDownKeyPressedEvents 312 | |> TextField.addCopyPasteSupportWithMiniClipboard 313 | |> CommandTextField.addKeyDownEventProcessing uiState views 314 | |> ignore 315 | 316 | views.ResultFilterTextField 317 | |> View.preventCursorUpDownKeyPressedEvents 318 | |> TextField.addCopyPasteSupportWithMiniClipboard 319 | |> ResultFilterTextField.addKeyDownEventProcessing uiState views 320 | |> ignore 321 | 322 | views -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewer/UIUtil.fs: -------------------------------------------------------------------------------- 1 | module PoshRedisViewer.UIUtil 2 | 3 | open System 4 | open System.ComponentModel 5 | open System.Runtime.CompilerServices 6 | open System.Text.RegularExpressions 7 | open En3Tho.FSharp.Extensions 8 | open En3Tho.FSharp.ComputationExpressions 9 | open En3Tho.FSharp.ComputationExpressions.SStringBuilderBuilder 10 | open NStack 11 | open PoshRedisViewer.Redis 12 | 13 | open Terminal.Gui 14 | 15 | type HistorySlot<'a, 'b> = { 16 | Key: 'a 17 | Value: 'b 18 | } 19 | 20 | type KeyQueryResultState = { 21 | Keys: string[] 22 | FromHistory: bool 23 | Filtered: bool 24 | Time: DateTimeOffset 25 | } 26 | 27 | module KeyQueryResultState = 28 | let toString (result: KeyQueryResultState) = 29 | let flags = 30 | seq { 31 | toString result.Keys.Length 32 | if result.FromHistory then "From History" 33 | if result.Filtered then "Filtered" 34 | result.Time.ToString() 35 | } |> String.concat ", " 36 | $"Keys ({flags})" 37 | 38 | type ResultsState = { 39 | ResultType: string 40 | Result: string[] 41 | FromHistory: bool 42 | Filtered: bool 43 | Time: DateTimeOffset 44 | } 45 | 46 | module ResultsState = 47 | let toString (result: ResultsState) = 48 | let flags = 49 | seq { 50 | result.ResultType 51 | if result.FromHistory then "From History" 52 | if result.Filtered then "Filtered" 53 | result.Time.ToString() 54 | } |> String.concat ", " 55 | 56 | $"Results ({flags})" 57 | 58 | [] 59 | type ViewExtensions() = 60 | [] 61 | static member inline Run(value: #View, [] runExpr: CollectionCode) = runExpr(); value 62 | 63 | type ResultHistoryCache<'a, 'b when 'a: equality>(capacity: int) = 64 | let syncRoot = obj() 65 | let items = ResizeArray>(capacity) 66 | let mutable index = 0 67 | 68 | member _.Up() = 69 | lock syncRoot ^ fun() -> 70 | match items.Count, index - 1 with 71 | | 0, _ -> 72 | ValueNone 73 | | currentCount, newIndex -> 74 | if uint newIndex >= uint currentCount then 75 | ValueNone 76 | else 77 | index <- newIndex 78 | ValueSome items[newIndex] 79 | 80 | member _.Down() = 81 | lock syncRoot ^ fun() -> 82 | match items.Count, index + 1 with 83 | | 0, _ -> 84 | ValueNone 85 | | currentCount, newIndex -> 86 | if uint newIndex >= uint currentCount then 87 | ValueNone 88 | else 89 | index <- newIndex 90 | ValueSome items[newIndex] 91 | 92 | member _.Add(key, value) = 93 | lock syncRoot ^ fun() -> 94 | if items.Count = 0 then 95 | () 96 | else 97 | match items.FindIndex(fun slot -> slot.Key = key) with 98 | | -1 -> () 99 | | index -> 100 | items.RemoveAt index 101 | 102 | items.Add { Key = key; Value = value } 103 | if items.Count > capacity then 104 | items.RemoveAt 0 105 | index <- items.Count - 1 106 | 107 | member _.TryReadCurrent() = 108 | lock syncRoot ^ fun() -> 109 | let index = index 110 | if uint index >= uint items.Count then 111 | ValueNone 112 | else 113 | ValueSome items[index] 114 | 115 | type UIState = { 116 | Multiplexer: StackExchange.Redis.IConnectionMultiplexer 117 | mutable KeyQueryResultState: KeyQueryResultState 118 | mutable ResultsState: ResultsState 119 | mutable ResultsFromKeyQuery: ValueOption 120 | KeyQueryHistory: ResultHistoryCache 121 | ResultsHistory: ResultHistoryCache 122 | } 123 | 124 | module rec RedisResult = 125 | 126 | let toStringArray (value: RedisResult) = 127 | match value with 128 | | RedisString str -> 129 | [| str |] 130 | | RedisList strings -> 131 | strings 132 | |> Array.map ^ fun member' -> $"Index: {member'.Index} | Value: {member'.Value}" 133 | | RedisError e -> 134 | e.ToString().Split(Environment.NewLine) 135 | | RedisHash members -> 136 | members 137 | |> Array.map ^ fun member' -> $"Field: {member'.Field} | Value: {member'.Value}" 138 | | RedisNone -> 139 | [| "None" |] 140 | | RedisSet strings -> 141 | strings 142 | | RedisSortedSet members -> 143 | members 144 | |> Array.map ^ fun member' -> $"Score: {member'.Score} | Value: {member'.Value}" 145 | | RedisStream -> 146 | [| "RedisStream is not supported" |] 147 | | RedisMultiResult values -> 148 | values 149 | |> Array.map toStringArray 150 | |> Array.concat 151 | 152 | let getInformationText (value: RedisResult) = 153 | match value with 154 | | RedisString _ -> 155 | "RedisString" 156 | | RedisList strings -> 157 | $"RedisList ({strings.Length})" 158 | | RedisError _ -> 159 | "RedisError" 160 | | RedisHash members -> 161 | $"RedisHash ({members.Length})" 162 | | RedisNone -> 163 | "RedisNone" 164 | | RedisSet strings -> 165 | $"RedisSet ({strings.Length})" 166 | | RedisSortedSet members -> 167 | $"RedisSortedSet ({members.Length})" 168 | | RedisStream -> 169 | "RedisStream" 170 | | RedisMultiResult values -> 171 | $"RedisMultiResult ({values.Length})" 172 | 173 | let ustr str = icast str 174 | module Ustr = 175 | 176 | let toString (utext: ustring) = 177 | match utext with 178 | | null -> "" 179 | | _ -> utext.ToString() 180 | 181 | let fromString (text: string) = 182 | match text with 183 | | null | "" -> ustring.Empty 184 | | _ -> ustr text 185 | 186 | module Key = 187 | let private copyKey = Key.CtrlMask ||| Key.C 188 | let private pasteKey = Key.CtrlMask ||| Key.V 189 | 190 | let private is flag (key: Key) = key |> Enum.hasFlag flag |> Option.ofBool 191 | 192 | let (|CopyCommand|_|) key = key |> is copyKey 193 | let (|PasteCommand|_|) key = key |> is pasteKey 194 | 195 | [] 196 | type FilterType = 197 | | Contains 198 | | Regex 199 | 200 | module Filter = 201 | let stringContains (pattern: string) (value: string) = 202 | value.Contains(pattern, StringComparison.OrdinalIgnoreCase) 203 | 204 | let regex (pattern: string) (value: string) = 205 | let regex = Regex(pattern, RegexOptions.Compiled) 206 | regex.IsMatch(value) 207 | 208 | module View = 209 | let preventKeyPressedEvents (events: Key[]) (view: #View) = 210 | view.add_KeyPress(fun keyPressEvent -> 211 | match keyPressEvent.KeyEvent.Key with 212 | | key when events |> Array.contains key -> 213 | keyPressEvent.Handled <- true 214 | | _ -> () 215 | ) 216 | view 217 | 218 | let preventCursorUpDownKeyPressedEvents (view: #View) = 219 | view |> preventKeyPressedEvents [| Key.CursorUp; Key.CursorDown |] 220 | 221 | type MiniClipboard(clipboard: IClipboard) = 222 | let mutable current = "" 223 | 224 | interface IClipboard with 225 | member this.GetClipboardData() = 226 | let original = try' { clipboard.GetClipboardData() } 227 | original |> String.defaultValue current 228 | 229 | member this.SetClipboardData(text) = 230 | current <- text 231 | try' { clipboard.SetClipboardData(current) } 232 | 233 | member this.TryGetClipboardData(result) = 234 | if not (clipboard.TryGetClipboardData(&result)) then 235 | result <- current 236 | true 237 | 238 | member this.TrySetClipboardData(text) = 239 | current <- text 240 | clipboard.TrySetClipboardData(text) |> ignore 241 | true 242 | 243 | member this.IsSupported = true 244 | 245 | module Clipboard = 246 | 247 | let mutable MiniClipboard = nullRef 248 | 249 | let saveToClipboard text = 250 | let text = if Object.ReferenceEquals(text, null) then "" else text.ToString() 251 | MiniClipboard.TrySetClipboardData text |> ignore 252 | 253 | let getFromClipboard() = 254 | let mutable text = nullRef 255 | MiniClipboard.TryGetClipboardData(&text) |> ignore 256 | text |> String.defaultValue "" 257 | 258 | module ListView = 259 | 260 | type ListView with 261 | member this.TrySelectedItem() = 262 | match this.Source, this.SelectedItem with 263 | | (NotNull & source), (GtEq 0 & selectedItem) when source.Count >= 0 -> 264 | let value = source.ToList()[selectedItem] 265 | value |> ValueOption.ofObj 266 | | _ -> 267 | ValueNone 268 | 269 | let private copySelectedItemTextToClipboard (textMapper: string -> string) (listView: ListView) = 270 | match listView.TrySelectedItem() with 271 | | ValueSome selectedItem -> 272 | selectedItem 273 | |> toString 274 | |> textMapper 275 | |> Clipboard.saveToClipboard 276 | | _ -> () 277 | 278 | let addValueCopyOnRightClick textMapper (listView: ListView) = 279 | listView.add_MouseClick(fun mouseClickEvent -> 280 | if listView.HasFocus then 281 | match mouseClickEvent.MouseEvent.Flags with 282 | | Enum.HasFlag MouseFlags.Button3Released -> 283 | copySelectedItemTextToClipboard textMapper listView 284 | mouseClickEvent.Handled <- true 285 | | _ -> () 286 | ) 287 | listView 288 | 289 | let addValueCopyOnCopyHotKey textMapper (listView: ListView) = 290 | listView.add_KeyDown(fun keyDownEvent -> 291 | match keyDownEvent.KeyEvent.Key with 292 | | Key.CopyCommand -> 293 | copySelectedItemTextToClipboard textMapper listView 294 | keyDownEvent.Handled <- true 295 | | _ -> () 296 | ) 297 | listView 298 | 299 | let addDetailedViewOnEnterKey (listView: ListView) = 300 | listView.add_KeyDown (fun keyDownEvent -> 301 | match keyDownEvent.KeyEvent.Key, listView.TrySelectedItem() with 302 | | Key.Enter, ValueSome selectedItem -> 303 | MessageBox.Query("Value", selectedItem.ToString(), "Ok") 304 | |> ignore 305 | keyDownEvent.Handled <- true 306 | | _ -> () 307 | ) 308 | listView 309 | 310 | module TextField = 311 | 312 | let addCopyPasteSupportWithMiniClipboard (textField: TextField) = 313 | textField.add_KeyDown(fun keyDownEvent -> 314 | match keyDownEvent.KeyEvent.Key with 315 | | Key.CopyCommand -> 316 | textField.SelectedText |> Clipboard.saveToClipboard 317 | keyDownEvent.Handled <- true 318 | 319 | | Key.PasteCommand -> 320 | let newText, newCursorPosition = 321 | let clipboardText = Clipboard.getFromClipboard() 322 | 323 | match textField.SelectedLength, textField.CursorPosition with 324 | | 0, 0 -> 325 | clipboardText |> Ustr.fromString, 326 | clipboardText.Length 327 | 328 | | 0, cursor -> 329 | let text = textField.Text |> Ustr.toString 330 | let left = text.Substring(0, cursor) 331 | let right = text.Substring(cursor) 332 | 333 | left + clipboardText + right |> Ustr.fromString, 334 | textField.CursorPosition + clipboardText.Length 335 | 336 | | _, cursor -> 337 | let text = textField.Text 338 | text.Replace(textField.SelectedText, clipboardText |> Ustr.fromString, maxReplacements = 1), 339 | if textField.SelectedStart < cursor then 340 | textField.SelectedStart + clipboardText.Length 341 | else 342 | cursor + clipboardText.Length 343 | 344 | textField.Text <- newText 345 | textField.CursorPosition <- newCursorPosition 346 | keyDownEvent.Handled <- true 347 | | _ -> () 348 | ) 349 | textField -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewerHost/PoshRedisViewerHost.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewerHost/Program.fs: -------------------------------------------------------------------------------- 1 | open System 2 | open System.Threading.Tasks 3 | open En3Tho.FSharp.Extensions 4 | open PoshRedisViewer 5 | open PoshRedisViewer.Redis 6 | 7 | [] 8 | let main argv = 9 | try 10 | "localhost:6379" 11 | |> RedisReader.connect None None 12 | |> Task.RunSynchronously 13 | |> App.run 14 | |> App.shutdown 15 | finally 16 | if not Console.IsInputRedirected then 17 | Console.Write("\u001b[?1h\u001b[?1003l") // fixes an issue with certain terminals, same as ocgv 18 | 0 -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewerModule/GetRedisViewerCommand.fs: -------------------------------------------------------------------------------- 1 | namespace PoshRedisViewerModule 2 | 3 | open System 4 | open System.Management.Automation 5 | open System.Threading.Tasks 6 | open PoshRedisViewer 7 | open PoshRedisViewer.Redis 8 | open En3Tho.FSharp.Extensions 9 | 10 | [] 11 | type GetRedisViewerCommand() = 12 | inherit Cmdlet() 13 | 14 | [] 15 | [] 16 | member val ConnectionString = "" with get, set 17 | 18 | [] 19 | member val User = "" with get, set 20 | 21 | [] 22 | member val Password = "" with get, set 23 | 24 | override this.ProcessRecord() = 25 | let connectionString = this.ConnectionString 26 | let user = this.User |> Option.ofString 27 | let password = this.Password |> Option.ofString 28 | 29 | let dispose() = 30 | if not Console.IsInputRedirected then 31 | Console.Write("\u001b[?1h\u001b[?1003l") // fixes an issue with certain terminals, same as ocgv 32 | 33 | use _ = defer dispose 34 | 35 | connectionString 36 | |> RedisReader.connect user password 37 | |> Task.RunSynchronously 38 | |> App.run 39 | |> App.shutdown -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewerModule/PoshRedisViewerModule.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | PowershellModule 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /PoshRedisViewer/PoshRedisViewerModule/PoshRedisViewerModule.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PoshRedisViewerModule' 3 | # 4 | # Generated by: Rzrl 5 | # 6 | # Generated on: 25.09.2021 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PoshRedisViewerModule.dll' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.0.11' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '0c79ae41-ea88-4f75-94f4-17a31572e263' 22 | 23 | # Author of this module 24 | Author = 'Igor Bagdamyan' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Igor Bagdamyan' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) Igor Bagdamyan. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'A simple Redis viewer capable of reading values, issuing commands, filtering fetched data in memory, keeping history' 34 | 35 | # Minimum version of the PowerShell engine required by this module 36 | PowerShellVersion = '7.1.3' 37 | 38 | # Name of the PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # ClrVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = @() 73 | 74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 75 | CmdletsToExport = @('Get-RedisViewer') 76 | 77 | # Variables to export from this module 78 | VariablesToExport = '*' 79 | 80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | # Tags = @() 99 | 100 | # A URL to the license for this module. 101 | LicenseUri = 'https://github.com/En3Tho/PoshRedisViewer/blob/main/LICENSE' 102 | 103 | # A URL to the main website for this project. 104 | ProjectUri = 'https://github.com/En3Tho/PoshRedisViewer' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | # Prerelease string of this module 113 | # Prerelease = '' 114 | 115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 116 | # RequireLicenseAcceptance = $false 117 | 118 | # External dependent modules of this module 119 | # ExternalModuleDependencies = @() 120 | 121 | } # End of PSData hashtable 122 | 123 | } # End of PrivateData hashtable 124 | 125 | # HelpInfo URI of this module 126 | # HelpInfoURI = '' 127 | 128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 129 | # DefaultCommandPrefix = '' 130 | 131 | } 132 | 133 | -------------------------------------------------------------------------------- /PoshRedisViewer/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0", 4 | "rollForward": "latestMajor", 5 | "allowPrerelease": false 6 | } 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PoshRedisViewer 2 | A compact redis browser for powershell powered by https://github.com/StackExchange/StackExchange.Redis and https://github.com/migueldeicaza/gui.cs 3 | 4 | https://www.powershellgallery.com/packages/PoshRedisViewerModule 5 | 6 | ![img.png](img.png) 7 | 8 | It allows you to: 9 | 1. Look for Redis Keys via Scan/Keys (depending on Redis version) 10 | 2. View the type and contents of specified key 11 | 3. Filter keys without requerying them from Redis 12 | 4. Filter results to look for useful information 13 | 5. Execute custom commands 14 | 6. Select DB index 15 | 16 | Copy and Paste is allowed: 17 | - Windows: Ctrl+C | Ctrl+V 18 | - Linux: Ctrl+Y | Ctrl+V 19 | - Keys and Results list views support copying by right-clicking on them 20 | - In case when OS clipboard (clip/xclip) is not available, pasting from internal mini-clipboard is available with Ctrl+B combination 21 | 22 | Query, command and filter execution require "Enter" key press. 23 | 24 | Also, keys and commands support history mode using CursorUp and CursorDown keys. 25 | 26 | Powershell module exports "Get-RedisViewer" command: 27 | - [0, Mandatory] ConnectionString 28 | - [Optional] User 29 | - [Optional] Password 30 | 31 | Example: 32 | ``` 33 | Install-Module -Name PoshRedisViewerModule 34 | Import-Module -Name PoshRedisViewerModule 35 | ``` 36 | then 37 | ``` 38 | Get-RedisViewer "localhost:6379" 39 | ``` 40 | or 41 | ``` 42 | Get-RedisViewer "localhost:6379" -User me -Password myPassword 43 | ``` 44 | 45 | ## Development 46 | 47 | 1. Install power shell 48 | 2. Clone the GitHub repository 49 | 50 | ### Testing application 51 | 52 | The easiest way to test the module is to run the PoshRedisViewerHost project 53 | 54 | ``` 55 | cd .\PoshRedisViewer\PoshRedisViewerHost\ 56 | dotnet run 57 | ``` 58 | 59 | ### Testing as a Powershell Module 60 | 61 | To test running as a powershell module 62 | 63 | 1. Build the repository (if building on linux use `-r linux-x64`) 64 | ``` 65 | cd PoshRedisViewer 66 | dotnet publish -o .artifacts -r win-x64 67 | ``` 68 | 2. Copy the psd1 file into the build directory 69 | ``` 70 | cp .\PoshRedisViewerModule\PoshRedisViewerModule.psd1 .\.artifacts\ 71 | ``` 72 | 3. Start a new powershell instance (prevents stale modules sitting around etc) 73 | ``` 74 | pwsh –noprofile 75 | ``` 76 | 4. Import the module 77 | ``` 78 | Import-Module -Name ".\.artifacts\PoshRedisViewerModule.psd1" 79 | ``` 80 | 81 | ## Testing With Redis Docker Image 82 | 83 | 1. Start a new Redis via docker: 84 | 85 | ``` 86 | docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest 87 | ``` 88 | 89 | 2. Connect using PoshRedisViewer 90 | 91 | ``` 92 | Get-RedisViewer "localhost:6379" 93 | ``` 94 | 95 | 3. Create a Redis key entry by clicking in the 'Command' window and enter 96 | ``` 97 | set fish haddock 98 | ``` 99 | 100 | 4. Query the Redis to see the key by clicking in 'KeyQuery' and pressing enter 101 | 102 | ![Screenshot showing query results fetched from Redis. Key 'fish' has value 'haddock'.](./fish-haddock.png) 103 | 104 | 105 | TODO: 106 | - Tabs/add to favorites 107 | - Results filter history 108 | - Quick commands to change/set values -------------------------------------------------------------------------------- /Releases/0.0.5/En3Tho.FSharp.ComputationExpressions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/En3Tho.FSharp.ComputationExpressions.dll -------------------------------------------------------------------------------- /Releases/0.0.5/En3Tho.FSharp.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/En3Tho.FSharp.Extensions.dll -------------------------------------------------------------------------------- /Releases/0.0.5/FSharp.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/FSharp.Core.dll -------------------------------------------------------------------------------- /Releases/0.0.5/NStack.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/NStack.dll -------------------------------------------------------------------------------- /Releases/0.0.5/Pipelines.Sockets.Unofficial.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/Pipelines.Sockets.Unofficial.dll -------------------------------------------------------------------------------- /Releases/0.0.5/Ply.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/Ply.dll -------------------------------------------------------------------------------- /Releases/0.0.5/PoshRedisViewer.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/PoshRedisViewer.dll -------------------------------------------------------------------------------- /Releases/0.0.5/PoshRedisViewerModule.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/PoshRedisViewerModule.dll -------------------------------------------------------------------------------- /Releases/0.0.5/PoshRedisViewerModule.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PoshRedisViewerModule' 3 | # 4 | # Generated by: Rzrl 5 | # 6 | # Generated on: 25.09.2021 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PoshRedisViewerModule.dll' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.0.5' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '0c79ae41-ea88-4f75-94f4-17a31572e263' 22 | 23 | # Author of this module 24 | Author = 'Igor Bagdmamyan' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Igor Bagdmamyan' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) Igor Bagdmamyan. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'A simple Redis viewer capable of readonly operations' 34 | 35 | # Minimum version of the PowerShell engine required by this module 36 | PowerShellVersion = '7.1.3' 37 | 38 | # Name of the PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # ClrVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = @() 73 | 74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 75 | CmdletsToExport = @('Get-RedisViewer') 76 | 77 | # Variables to export from this module 78 | VariablesToExport = '*' 79 | 80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | # Tags = @() 99 | 100 | # A URL to the license for this module. 101 | LicenseUri = 'https://github.com/En3Tho/PoshRedisViewer/blob/main/LICENSE' 102 | 103 | # A URL to the main website for this project. 104 | ProjectUri = 'https://github.com/En3Tho/PoshRedisViewer' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | # Prerelease string of this module 113 | # Prerelease = '' 114 | 115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 116 | # RequireLicenseAcceptance = $false 117 | 118 | # External dependent modules of this module 119 | # ExternalModuleDependencies = @() 120 | 121 | } # End of PSData hashtable 122 | 123 | } # End of PrivateData hashtable 124 | 125 | # HelpInfo URI of this module 126 | # HelpInfoURI = '' 127 | 128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 129 | # DefaultCommandPrefix = '' 130 | 131 | } 132 | 133 | -------------------------------------------------------------------------------- /Releases/0.0.5/StackExchange.Redis.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/StackExchange.Redis.dll -------------------------------------------------------------------------------- /Releases/0.0.5/System.IO.Pipelines.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/System.IO.Pipelines.dll -------------------------------------------------------------------------------- /Releases/0.0.5/Terminal.Gui.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.5/Terminal.Gui.dll -------------------------------------------------------------------------------- /Releases/0.0.6/En3Tho.FSharp.ComputationExpressions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/En3Tho.FSharp.ComputationExpressions.dll -------------------------------------------------------------------------------- /Releases/0.0.6/En3Tho.FSharp.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/En3Tho.FSharp.Extensions.dll -------------------------------------------------------------------------------- /Releases/0.0.6/FSharp.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/FSharp.Core.dll -------------------------------------------------------------------------------- /Releases/0.0.6/NStack.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/NStack.dll -------------------------------------------------------------------------------- /Releases/0.0.6/Pipelines.Sockets.Unofficial.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/Pipelines.Sockets.Unofficial.dll -------------------------------------------------------------------------------- /Releases/0.0.6/PoshRedisViewer.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/PoshRedisViewer.dll -------------------------------------------------------------------------------- /Releases/0.0.6/PoshRedisViewerModule.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/PoshRedisViewerModule.dll -------------------------------------------------------------------------------- /Releases/0.0.6/PoshRedisViewerModule.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PoshRedisViewerModule' 3 | # 4 | # Generated by: Rzrl 5 | # 6 | # Generated on: 25.09.2021 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PoshRedisViewerModule.dll' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.0.6' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '0c79ae41-ea88-4f75-94f4-17a31572e263' 22 | 23 | # Author of this module 24 | Author = 'Igor Bagdmamyan' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Igor Bagdmamyan' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) Igor Bagdmamyan. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'A simple Redis viewer capable of readonly operations' 34 | 35 | # Minimum version of the PowerShell engine required by this module 36 | PowerShellVersion = '7.1.3' 37 | 38 | # Name of the PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # ClrVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = @() 73 | 74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 75 | CmdletsToExport = @('Get-RedisViewer') 76 | 77 | # Variables to export from this module 78 | VariablesToExport = '*' 79 | 80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | # Tags = @() 99 | 100 | # A URL to the license for this module. 101 | LicenseUri = 'https://github.com/En3Tho/PoshRedisViewer/blob/main/LICENSE' 102 | 103 | # A URL to the main website for this project. 104 | ProjectUri = 'https://github.com/En3Tho/PoshRedisViewer' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | # Prerelease string of this module 113 | # Prerelease = '' 114 | 115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 116 | # RequireLicenseAcceptance = $false 117 | 118 | # External dependent modules of this module 119 | # ExternalModuleDependencies = @() 120 | 121 | } # End of PSData hashtable 122 | 123 | } # End of PrivateData hashtable 124 | 125 | # HelpInfo URI of this module 126 | # HelpInfoURI = '' 127 | 128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 129 | # DefaultCommandPrefix = '' 130 | 131 | } 132 | 133 | -------------------------------------------------------------------------------- /Releases/0.0.6/StackExchange.Redis.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/StackExchange.Redis.dll -------------------------------------------------------------------------------- /Releases/0.0.6/System.IO.Pipelines.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/System.IO.Pipelines.dll -------------------------------------------------------------------------------- /Releases/0.0.6/Terminal.Gui.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.6/Terminal.Gui.dll -------------------------------------------------------------------------------- /Releases/0.0.7/En3Tho.FSharp.ComputationExpressions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/En3Tho.FSharp.ComputationExpressions.dll -------------------------------------------------------------------------------- /Releases/0.0.7/En3Tho.FSharp.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/En3Tho.FSharp.Extensions.dll -------------------------------------------------------------------------------- /Releases/0.0.7/FSharp.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/FSharp.Core.dll -------------------------------------------------------------------------------- /Releases/0.0.7/NStack.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/NStack.dll -------------------------------------------------------------------------------- /Releases/0.0.7/Pipelines.Sockets.Unofficial.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/Pipelines.Sockets.Unofficial.dll -------------------------------------------------------------------------------- /Releases/0.0.7/PoshRedisViewer.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/PoshRedisViewer.dll -------------------------------------------------------------------------------- /Releases/0.0.7/PoshRedisViewerModule.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/PoshRedisViewerModule.dll -------------------------------------------------------------------------------- /Releases/0.0.7/PoshRedisViewerModule.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PoshRedisViewerModule' 3 | # 4 | # Generated by: Rzrl 5 | # 6 | # Generated on: 25.09.2021 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PoshRedisViewerModule.dll' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.0.7' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '0c79ae41-ea88-4f75-94f4-17a31572e263' 22 | 23 | # Author of this module 24 | Author = 'Igor Bagdmamyan' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Igor Bagdmamyan' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) Igor Bagdmamyan. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'A simple Redis viewer capable of readonly operations' 34 | 35 | # Minimum version of the PowerShell engine required by this module 36 | PowerShellVersion = '7.1.3' 37 | 38 | # Name of the PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # ClrVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = @() 73 | 74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 75 | CmdletsToExport = @('Get-RedisViewer') 76 | 77 | # Variables to export from this module 78 | VariablesToExport = '*' 79 | 80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | # Tags = @() 99 | 100 | # A URL to the license for this module. 101 | LicenseUri = 'https://github.com/En3Tho/PoshRedisViewer/blob/main/LICENSE' 102 | 103 | # A URL to the main website for this project. 104 | ProjectUri = 'https://github.com/En3Tho/PoshRedisViewer' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | # Prerelease string of this module 113 | # Prerelease = '' 114 | 115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 116 | # RequireLicenseAcceptance = $false 117 | 118 | # External dependent modules of this module 119 | # ExternalModuleDependencies = @() 120 | 121 | } # End of PSData hashtable 122 | 123 | } # End of PrivateData hashtable 124 | 125 | # HelpInfo URI of this module 126 | # HelpInfoURI = '' 127 | 128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 129 | # DefaultCommandPrefix = '' 130 | 131 | } 132 | 133 | -------------------------------------------------------------------------------- /Releases/0.0.7/StackExchange.Redis.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/StackExchange.Redis.dll -------------------------------------------------------------------------------- /Releases/0.0.7/System.IO.Pipelines.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/System.IO.Pipelines.dll -------------------------------------------------------------------------------- /Releases/0.0.7/Terminal.Gui.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.7/Terminal.Gui.dll -------------------------------------------------------------------------------- /Releases/0.0.8/En3Tho.FSharp.ComputationExpressions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/En3Tho.FSharp.ComputationExpressions.dll -------------------------------------------------------------------------------- /Releases/0.0.8/En3Tho.FSharp.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/En3Tho.FSharp.Extensions.dll -------------------------------------------------------------------------------- /Releases/0.0.8/FSharp.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/FSharp.Core.dll -------------------------------------------------------------------------------- /Releases/0.0.8/NStack.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/NStack.dll -------------------------------------------------------------------------------- /Releases/0.0.8/Pipelines.Sockets.Unofficial.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/Pipelines.Sockets.Unofficial.dll -------------------------------------------------------------------------------- /Releases/0.0.8/PoshRedisViewer.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/PoshRedisViewer.dll -------------------------------------------------------------------------------- /Releases/0.0.8/PoshRedisViewerModule.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/PoshRedisViewerModule.dll -------------------------------------------------------------------------------- /Releases/0.0.8/PoshRedisViewerModule.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PoshRedisViewerModule' 3 | # 4 | # Generated by: Rzrl 5 | # 6 | # Generated on: 25.09.2021 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PoshRedisViewerModule.dll' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.0.7' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '0c79ae41-ea88-4f75-94f4-17a31572e263' 22 | 23 | # Author of this module 24 | Author = 'Igor Bagdmamyan' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Igor Bagdmamyan' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) Igor Bagdmamyan. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'A simple Redis viewer capable of readonly operations' 34 | 35 | # Minimum version of the PowerShell engine required by this module 36 | PowerShellVersion = '7.1.3' 37 | 38 | # Name of the PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # ClrVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = @() 73 | 74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 75 | CmdletsToExport = @('Get-RedisViewer') 76 | 77 | # Variables to export from this module 78 | VariablesToExport = '*' 79 | 80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | # Tags = @() 99 | 100 | # A URL to the license for this module. 101 | LicenseUri = 'https://github.com/En3Tho/PoshRedisViewer/blob/main/LICENSE' 102 | 103 | # A URL to the main website for this project. 104 | ProjectUri = 'https://github.com/En3Tho/PoshRedisViewer' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | # Prerelease string of this module 113 | # Prerelease = '' 114 | 115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 116 | # RequireLicenseAcceptance = $false 117 | 118 | # External dependent modules of this module 119 | # ExternalModuleDependencies = @() 120 | 121 | } # End of PSData hashtable 122 | 123 | } # End of PrivateData hashtable 124 | 125 | # HelpInfo URI of this module 126 | # HelpInfoURI = '' 127 | 128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 129 | # DefaultCommandPrefix = '' 130 | 131 | } 132 | 133 | -------------------------------------------------------------------------------- /Releases/0.0.8/StackExchange.Redis.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/StackExchange.Redis.dll -------------------------------------------------------------------------------- /Releases/0.0.8/System.IO.Pipelines.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/System.IO.Pipelines.dll -------------------------------------------------------------------------------- /Releases/0.0.8/Terminal.Gui.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/Releases/0.0.8/Terminal.Gui.dll -------------------------------------------------------------------------------- /fish-haddock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/fish-haddock.png -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/En3Tho/PoshRedisViewer/10f1ff7e873787501f4d3040bfa8d3de1a897c26/img.png --------------------------------------------------------------------------------