Skip to content

Commit

Permalink
Add cmdlet toy examples
Browse files Browse the repository at this point in the history
  • Loading branch information
rjmholt committed Oct 29, 2021
1 parent a4ac00d commit 5b495f5
Show file tree
Hide file tree
Showing 10 changed files with 876 additions and 0 deletions.
Binary file added cmdlet/.vs/cmdlet/DesignTimeBuild/.dtbcache.v2
Binary file not shown.
Binary file added cmdlet/.vs/cmdlet/v16/.suo
Binary file not shown.
42 changes: 42 additions & 0 deletions cmdlet/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"WARNING01": "*********************************************************************************",
"WARNING02": "The C# extension was unable to automatically decode projects in the current",
"WARNING03": "workspace to create a runnable launch.json file. A template launch.json file has",
"WARNING04": "been created as a placeholder.",
"WARNING05": "",
"WARNING06": "If OmniSharp is currently unable to load your project, you can attempt to resolve",
"WARNING07": "this by restoring any missing project dependencies (example: run 'dotnet restore')",
"WARNING08": "and by fixing any reported errors from building the projects in your workspace.",
"WARNING09": "If this allows OmniSharp to now load your project then --",
"WARNING10": " * Delete this file",
"WARNING11": " * Open the Visual Studio Code command palette (View->Command Palette)",
"WARNING12": " * run the command: '.NET: Generate Assets for Build and Debug'.",
"WARNING13": "",
"WARNING14": "If your project requires a more complex launch configuration, you may wish to delete",
"WARNING15": "this configuration and pick a different template using the 'Add Configuration...'",
"WARNING16": "button at the bottom of this file.",
"WARNING17": "*********************************************************************************",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/<insert-target-framework-here>/<insert-project-name-here>.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
280 changes: 280 additions & 0 deletions cmdlet/Async.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Management.Automation;
using System.Threading;
using System.Threading.Tasks;

namespace cmdlet
{
public abstract class AsyncCmdlet : PSCmdlet
{
private readonly ConcurrentQueue<ICmdletTask> _cmdletTaskQueue;

private readonly List<Task> _processingTasks;

private Task _beginProcessingTask;

protected AsyncCmdlet()
{
_processingTasks = new List<Task>();
_cmdletTaskQueue = new ConcurrentQueue<ICmdletTask>();
}

protected abstract void HandleException(Exception e);

protected virtual Task BeginProcessingAsync() => Task.CompletedTask;

protected virtual Task ProcessRecordAsync() => Task.CompletedTask;

protected virtual Task EndProcessingAsync() => Task.CompletedTask;

protected override void BeginProcessing()
{
// Kick off BeginProcessingAsync()
_beginProcessingTask = BeginProcessingAsync();

// Drain queue to this point
RunQueuedTasks();
}

protected override void ProcessRecord()
{
// If not yet processed, complete BeginProcessingAsync() queue, mark it as processed
if (_beginProcessingTask is not null)
{
try
{
_beginProcessingTask.GetAwaiter().GetResult();
}
catch (Exception e)
{
HandleException(e);
}

RunQueuedTasks();
_beginProcessingTask = null;
}

// Kick off ProcessRecordAsync()
_processingTasks.Add(ProcessRecordAsync());

// Drain queue to this point
RunQueuedTasks();
}

protected override void EndProcessing()
{
// Complete ProcessRecord() queue
foreach (Task processTask in _processingTasks)
{
try
{
processTask.GetAwaiter().GetResult();
}
catch (Exception e)
{
HandleException(e);
}
}

RunQueuedTasks();

// Run EndProcessingAsync()
try
{
EndProcessingAsync().GetAwaiter().GetResult();
}
catch (Exception e)
{
HandleException(e);
}
}

private void RunQueuedTasks()
{
var tasks = new List<ICmdletTask>();

while (_cmdletTaskQueue.TryDequeue(out ICmdletTask task))
{
tasks.Add(task);
}

foreach (ICmdletTask task in tasks)
{
task.DoInvoke(this);
}
}
}

internal interface ICmdletTask
{
void DoInvoke(Cmdlet cmdlet);
}

internal abstract class CmdletTask<T> : ICmdletTask
{
private readonly TaskCompletionSource<T> _taskCompletionSource;

protected CmdletTask()
{
_taskCompletionSource = new TaskCompletionSource<T>();
}

public Task<T> ResultTask => _taskCompletionSource.Task;

public abstract T Invoke(Cmdlet cmdlet);

public void DoInvoke(Cmdlet cmdlet)
{
try
{
T result = Invoke(cmdlet);
_taskCompletionSource.SetResult(result);
}
catch (Exception e)
{
_taskCompletionSource.SetException(e);
}
}
}

internal abstract class CmdletTask : ICmdletTask
{
private readonly TaskCompletionSource<object> _taskCompletionSource;

protected CmdletTask()
{
_taskCompletionSource = new TaskCompletionSource<object>();
}

public Task Result => _taskCompletionSource.Task;

public abstract void Invoke(Cmdlet cmdlet);

public void DoInvoke(Cmdlet cmdlet)
{
try
{
Invoke(cmdlet);
_taskCompletionSource.SetResult(null);
}
catch (Exception e)
{
_taskCompletionSource.SetException(e);
}
}
}

internal abstract class StringMessage : CmdletTask
{
protected StringMessage(string message)
{
Message = message;
}

protected string Message { get; }
}

internal class VerboseMessage : StringMessage
{
public VerboseMessage(string message)
: base(message)
{
}

public override void Invoke(Cmdlet cmdlet)
{
cmdlet.WriteVerbose(Message);
}
}

internal class DebugMessage : StringMessage
{
public DebugMessage(string message)
: base(message)
{
}

public override void Invoke(Cmdlet cmdlet)
{
cmdlet.WriteDebug(Message);
}
}

internal class WarningMessage : StringMessage
{
public WarningMessage(string message)
: base(message)
{
}

public override void Invoke(Cmdlet cmdlet)
{
cmdlet.WriteWarning(Message);
}
}

internal class ErrorMessage : CmdletTask
{
private readonly ErrorRecord _errorRecord;

public ErrorMessage(ErrorRecord errorRecord)
{
_errorRecord = errorRecord;
}

public override void Invoke(Cmdlet cmdlet)
{
cmdlet.WriteError(_errorRecord);
}
}

internal class InformationMessage : CmdletTask
{
private readonly InformationRecord _informationRecord;

public InformationMessage(InformationRecord informationRecord)
{
_informationRecord = informationRecord;
}

public override void Invoke(Cmdlet cmdlet)
{
cmdlet.WriteInformation(_informationRecord);
}
}

internal class ObjectMessage : CmdletTask
{
private readonly object _object;

private readonly bool _enumerate;

public ObjectMessage(object obj, bool enumerate)
{
_object = obj;
_enumerate = enumerate;
}

public override void Invoke(Cmdlet cmdlet)
{
cmdlet.WriteObject(_object, _enumerate);
}
}

internal class ProgressMessage : CmdletTask
{
private readonly ProgressRecord _progressRecord;

public ProgressMessage(ProgressRecord progressRecord)
{
_progressRecord = progressRecord;
}

public override void Invoke(Cmdlet cmdlet)
{
cmdlet.WriteProgress(_progressRecord);
}
}
}
62 changes: 62 additions & 0 deletions cmdlet/AsyncExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Management.Automation;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace cmdlet
{
[Cmdlet(VerbsDiagnostic.Test, "AsyncExample")]
public class AsyncExampleCmdlet : Cmdlet
{
private readonly HttpClient _httpClient;

private readonly List<Task> _tasks;

private readonly ConcurrentQueue<HttpStatusCode> _results;

public AsyncExampleCmdlet()
{
_httpClient = new HttpClient();
_tasks = new List<Task>();
_results = new ConcurrentQueue<HttpStatusCode>();
}

[Parameter(Mandatory = true, ValueFromPipeline = true)]
public Uri[] Uri { get; set; }

protected override void ProcessRecord()
{
if (Uri is not null)
{
foreach (Uri uri in Uri)
{
_tasks.Add(GetUriAsync(uri));
}
}

while (_results.TryDequeue(out HttpStatusCode result))
{
WriteObject(result);
}
}

protected override void EndProcessing()
{
Task.WaitAll(_tasks.ToArray());

while (_results.TryDequeue(out HttpStatusCode result))
{
WriteObject(result);
}
}

private async Task GetUriAsync(Uri uri)
{
HttpResponseMessage response = await _httpClient.GetAsync(uri);
_results.Enqueue(response.StatusCode);
}
}
}
Loading

0 comments on commit 5b495f5

Please sign in to comment.