зеркало из https://github.com/github/VisualStudio.git
Add separate lib to hold mef interfaces with reactive dependencies
This commit is contained in:
Родитель
b3fa798524
Коммит
d97c92633f
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub.Authentication
|
||||
{
|
||||
public interface ITwoFactorChallengeHandler
|
||||
{
|
||||
IObservable<TwoFactorChallengeResult> HandleTwoFactorException(TwoFactorRequiredException exception);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub
|
||||
{
|
||||
/// <summary>
|
||||
/// Per host cache data.
|
||||
/// </summary>
|
||||
public interface IHostCache : IDisposable
|
||||
{
|
||||
IObservable<User> GetUser();
|
||||
IObservable<Unit> InsertUser(User user);
|
||||
IObservable<IEnumerable<Organization>> GetAllOrganizations();
|
||||
IObservable<Unit> InsertOrganization(Organization organization);
|
||||
IObservable<Unit> InvalidateOrganization(Organization organization);
|
||||
IObservable<Unit> InvalidateOrganization(IAccount organization);
|
||||
IObservable<Unit> InvalidateAll();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GitHub.Exports.Reactive</RootNamespace>
|
||||
<AssemblyName>GitHub.Exports.Reactive</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Octokit">
|
||||
<HintPath>..\..\packages\Octokit.0.6.2\lib\net45\Octokit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="ReactiveUI">
|
||||
<HintPath>..\..\packages\reactiveui-core.6.3.1\lib\Net45\ReactiveUI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Splat">
|
||||
<HintPath>..\..\packages\Splat.1.6.0\lib\Net45\Splat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Reactive.Core">
|
||||
<HintPath>..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Interfaces">
|
||||
<HintPath>..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Linq">
|
||||
<HintPath>..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.PlatformServices">
|
||||
<HintPath>..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Windows.Threading">
|
||||
<HintPath>..\..\packages\Rx-XAML.2.2.5\lib\net45\System.Reactive.Windows.Threading.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\script\SolutionInfo.cs">
|
||||
<Link>Properties\SolutionInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Authentication\ITwoFactorChallengeHandler.cs" />
|
||||
<Compile Include="Caches\IHostCache.cs" />
|
||||
<Compile Include="Models\IAccount.cs" />
|
||||
<Compile Include="Models\IRepositoryHost.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\IApiClient.cs" />
|
||||
<Compile Include="Services\IAvatarProvider.cs" />
|
||||
<Compile Include="UI\ICreateRepoViewModel.cs" />
|
||||
<Compile Include="Validation\ReactivePropertyValidator.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GitHub.Exports\GitHub.Exports.csproj">
|
||||
<Project>{9aea02db-02b5-409c-b0ca-115d05331a6b}</Project>
|
||||
<Name>GitHub.Exports</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.Extensions\GitHub.Extensions.csproj">
|
||||
<Project>{6afe2e2d-6db0-4430-a2ea-f5f5388d2f78}</Project>
|
||||
<Name>GitHub.Extensions</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,38 @@
|
|||
using GitHub.Models;
|
||||
using Octokit;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub
|
||||
{
|
||||
public interface IAccount
|
||||
{
|
||||
string Email { get; }
|
||||
int Id { get; }
|
||||
bool IsEnterprise { get; }
|
||||
bool IsGitHub { get; }
|
||||
bool IsLocal { get; }
|
||||
bool IsOnFreePlan { get; }
|
||||
bool HasMaximumPrivateRepositories { get; }
|
||||
bool IsUser { get; }
|
||||
/// <summary>
|
||||
/// True if the user is an admin on the host (GitHub or Enterprise).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Do not confuse this with "IsStaff". This is true if the user is an admin
|
||||
/// on the site. IsStaff is true if that site is github.com.
|
||||
/// </remarks>
|
||||
bool IsSiteAdmin { get; }
|
||||
/// <summary>
|
||||
/// Returns true if the user is a member of the GitHub staff.
|
||||
/// </summary>
|
||||
bool IsGitHubStaff { get; }
|
||||
IRepositoryHost Host { get; }
|
||||
string Login { get; }
|
||||
string Name { get; }
|
||||
int OwnedPrivateRepos { get; }
|
||||
long PrivateReposInPlan { get; }
|
||||
|
||||
void Update(User ghUser);
|
||||
void Update(Organization org);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using GitHub.Authentication;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
public interface IRepositoryHost
|
||||
{
|
||||
HostAddress Address { get; }
|
||||
IApiClient ApiClient { get; }
|
||||
IHostCache Cache { get; }
|
||||
bool IsGitHub { get; }
|
||||
bool IsLoggedIn { get; }
|
||||
bool IsLoggingIn { get; }
|
||||
bool IsEnterprise { get; }
|
||||
bool IsLocal { get; }
|
||||
ReactiveList<IAccount> Organizations { get; }
|
||||
ReactiveList<IAccount> Accounts { get; }
|
||||
string Title { get; }
|
||||
IAccount User { get; }
|
||||
|
||||
IObservable<AuthenticationResult> LogIn(string usernameOrEmail, string password);
|
||||
IObservable<AuthenticationResult> LogInFromCache();
|
||||
IObservable<Unit> LogOut();
|
||||
IObservable<Unit> Refresh();
|
||||
IObservable<Unit> Refresh(Func<IRepositoryHost, IObservable<Unit>> refreshTrackedRepositoriesFunc);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("GitHub.Exports.Reactive")]
|
||||
[assembly: AssemblyDescription("GitHub interfaces for mef exports with reactive dependencies")]
|
||||
[assembly: Guid("e4ed0537-d1d9-44b6-9212-3096d7c3f7a1")]
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using GitHub.Authentication;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub
|
||||
{
|
||||
public interface IApiClient
|
||||
{
|
||||
HostAddress HostAddress { get; }
|
||||
IObservable<SshKey> AddSshKey(SshKey newKey);
|
||||
IObservable<Repository> CreateRepository(Repository repo, string login, bool isUser);
|
||||
IObservable<SshKey> GetSshKeys();
|
||||
IObservable<User> GetUser();
|
||||
IObservable<User> GetAllUsersForAllOrganizations();
|
||||
IObservable<Organization> GetOrganization(string login);
|
||||
IObservable<Organization> GetOrganizations();
|
||||
IObservable<User> GetMembersOfOrganization(string organizationName);
|
||||
IObservable<Repository> GetRepository(string owner, string name);
|
||||
IObservable<IEnumerable<Repository>> GetUserRepositories(int currentUserId);
|
||||
IObservable<Repository> GetCurrentUserRepositoriesStreamed();
|
||||
IObservable<Repository> GetOrganizationRepositoriesStreamed(string login);
|
||||
IObservable<Authorization> GetOrCreateApplicationAuthenticationCode(
|
||||
Func<TwoFactorRequiredException, IObservable<TwoFactorChallengeResult>> twoFactorChallengeHander = null,
|
||||
bool useOldScopes = false);
|
||||
IObservable<Authorization> GetOrCreateApplicationAuthenticationCode(
|
||||
string authenticationCode,
|
||||
bool useOldScopes = false);
|
||||
IObservable<IReadOnlyList<EmailAddress>> GetEmails();
|
||||
ITwoFactorChallengeHandler TwoFactorChallengeHandler { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
public interface IHostAvatarProvider
|
||||
{
|
||||
IAvatarProvider Get(string gitHubBaseUrl);
|
||||
}
|
||||
|
||||
public interface IAvatarProvider
|
||||
{
|
||||
BitmapImage DefaultUserBitmapImage { get; }
|
||||
BitmapImage DefaultOrgBitmapImage { get; }
|
||||
IObservable<BitmapSource> GetAvatar(Account apiAccount);
|
||||
IObservable<Unit> InvalidateAvatar(Account apiAccount);
|
||||
IObservable<BitmapSource> GetAvatar(string email);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using GitHub.Validation;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace GitHub.UI
|
||||
{
|
||||
public interface ICreateRepoViewModel
|
||||
{
|
||||
string RepositoryName { get; }
|
||||
string SafeRepositoryName { get; }
|
||||
bool ShowRepositoryNameWarning { get; }
|
||||
string RepositoryNameWarningText { get; }
|
||||
ReactivePropertyValidator<string> RepositoryNameValidator { get; }
|
||||
|
||||
string Description { get; set; }
|
||||
|
||||
ReactiveList<IAccount> Accounts { get; }
|
||||
IAccount SelectedAccount { get; }
|
||||
|
||||
bool KeepPrivate { get; set; }
|
||||
bool CanKeepPrivate { get; }
|
||||
bool ShowUpgradeToMicroPlanWarning { get; }
|
||||
bool ShowUpgradePlanWarning { get; }
|
||||
|
||||
bool IsPublishing { get; }
|
||||
ReactiveCommand<Unit> CreateRepository { get; }
|
||||
ReactiveCommand<Object> UpgradeAccountPlan { get; }
|
||||
ReactiveCommand<Object> Reset { get; }
|
||||
|
||||
ICommand OkCmd { get; }
|
||||
ICommand CancelCmd { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Extensions;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.Validation
|
||||
{
|
||||
public class ReactivePropertyValidationResult
|
||||
{
|
||||
public bool IsValid { get; private set; }
|
||||
public ValidationStatus Status { get; private set; }
|
||||
public string Message { get; private set; }
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "It is immutable")]
|
||||
public static readonly ReactivePropertyValidationResult Success = new ReactivePropertyValidationResult(ValidationStatus.Valid);
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "It is immutable")]
|
||||
public static readonly ReactivePropertyValidationResult Unvalidated = new ReactivePropertyValidationResult();
|
||||
|
||||
public ReactivePropertyValidationResult()
|
||||
: this(ValidationStatus.Unvalidated, "")
|
||||
{
|
||||
}
|
||||
|
||||
public ReactivePropertyValidationResult(ValidationStatus validationStatus)
|
||||
: this(validationStatus, "")
|
||||
{
|
||||
}
|
||||
|
||||
public ReactivePropertyValidationResult(ValidationStatus validationStatus, string message)
|
||||
{
|
||||
Status = validationStatus;
|
||||
IsValid = validationStatus != ValidationStatus.Invalid;
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ValidationStatus
|
||||
{
|
||||
Unvalidated = 0,
|
||||
Invalid = 1,
|
||||
Valid = 2,
|
||||
}
|
||||
|
||||
public abstract class ReactivePropertyValidator : ReactiveObject
|
||||
{
|
||||
public static ReactivePropertyValidator<TProp> For<TObj, TProp>(TObj This, Expression<Func<TObj, TProp>> property)
|
||||
{
|
||||
return new ReactivePropertyValidator<TObj, TProp>(This, property);
|
||||
}
|
||||
|
||||
public abstract ReactivePropertyValidationResult ValidationResult { get; protected set; }
|
||||
|
||||
public abstract bool IsValidating { get; }
|
||||
|
||||
protected ReactivePropertyValidator()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract Task ExecuteAsync();
|
||||
|
||||
public abstract Task ResetAsync();
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
|
||||
public class ReactivePropertyValidator<TProp> : ReactivePropertyValidator
|
||||
{
|
||||
readonly ReactiveCommand<ReactivePropertyValidationResult> validateCommand;
|
||||
ReactivePropertyValidationResult validationResult;
|
||||
|
||||
public override ReactivePropertyValidationResult ValidationResult
|
||||
{
|
||||
get { return validationResult; }
|
||||
protected set { this.RaiseAndSetIfChanged(ref validationResult, value); }
|
||||
}
|
||||
|
||||
public override Task ExecuteAsync()
|
||||
{
|
||||
return validateCommand.ExecuteAsyncTask(new ValidationParameter());
|
||||
}
|
||||
|
||||
public override Task ResetAsync()
|
||||
{
|
||||
return validateCommand.ExecuteAsyncTask(new ValidationParameter { RequiresReset = true });
|
||||
}
|
||||
|
||||
readonly List<Func<TProp, IObservable<ReactivePropertyValidationResult>>> validators =
|
||||
new List<Func<TProp, IObservable<ReactivePropertyValidationResult>>>();
|
||||
|
||||
readonly ObservableAsPropertyHelper<bool> isValidating;
|
||||
public override bool IsValidating
|
||||
{
|
||||
get { return isValidating.Value; }
|
||||
}
|
||||
|
||||
public ReactivePropertyValidator(IObservable<TProp> signal)
|
||||
{
|
||||
validateCommand = ReactiveCommand.CreateAsyncObservable(param =>
|
||||
{
|
||||
var validationParams = (ValidationParameter)param;
|
||||
|
||||
if (validationParams.RequiresReset)
|
||||
{
|
||||
return Observable.Return(ReactivePropertyValidationResult.Unvalidated);
|
||||
}
|
||||
|
||||
TProp value = validationParams.PropertyValue;
|
||||
|
||||
var currentValidators = validators.ToList();
|
||||
|
||||
// HEAR YE, HEAR YE
|
||||
|
||||
// This .ToList() is here to ignore changes to the validator collection,
|
||||
// and thus avoid fantastically vague exceptions about
|
||||
// "Collection was modified, enumeration operation may not execute"
|
||||
// bubbling up to tear the application down
|
||||
|
||||
// Thus, the collection will be correct when the command executes,
|
||||
// which should be fine until we need to do more complex validation
|
||||
|
||||
if (!currentValidators.Any())
|
||||
return Observable.Return(ReactivePropertyValidationResult.Unvalidated);
|
||||
|
||||
return currentValidators.ToObservable()
|
||||
.SelectMany(v => v(value))
|
||||
.FirstOrDefaultAsync(x => x.Status == ValidationStatus.Invalid)
|
||||
.Select(x => x == null ? ReactivePropertyValidationResult.Success : x);
|
||||
});
|
||||
|
||||
isValidating = validateCommand.IsExecuting.ToProperty(this, x => x.IsValidating);
|
||||
|
||||
validateCommand.Subscribe(x => ValidationResult = x);
|
||||
signal.Subscribe(x => validateCommand.Execute(new ValidationParameter { PropertyValue = x, RequiresReset = false }));
|
||||
}
|
||||
|
||||
public ReactivePropertyValidator<TProp> IfTrue(Func<TProp, bool> predicate, string errorMessage)
|
||||
{
|
||||
return Add(predicate, errorMessage);
|
||||
}
|
||||
|
||||
public ReactivePropertyValidator<TProp> IfFalse(Func<TProp, bool> predicate, string errorMessage)
|
||||
{
|
||||
return Add(x => !predicate(x), errorMessage);
|
||||
}
|
||||
|
||||
ReactivePropertyValidator<TProp> Add(Func<TProp, bool> predicate, string errorMessage)
|
||||
{
|
||||
return Add(x => predicate(x) ? errorMessage : null);
|
||||
}
|
||||
|
||||
public ReactivePropertyValidator<TProp> Add(Func<TProp, string> predicateWithMessage)
|
||||
{
|
||||
validators.Add(value => Observable.Defer(() => Observable.Return(Validate(value, predicateWithMessage))));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReactivePropertyValidator<TProp> IfTrueAsync(Func<TProp, IObservable<bool>> predicate, string errorMessage)
|
||||
{
|
||||
AddAsync(x => predicate(x).Select(result => result ? errorMessage : null));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReactivePropertyValidator<TProp> IfFalseAsync(Func<TProp, IObservable<bool>> predicate, string errorMessage)
|
||||
{
|
||||
AddAsync(x => predicate(x).Select(result => result ? null : errorMessage));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReactivePropertyValidator<TProp> AddAsync(Func<TProp, IObservable<string>> predicateWithMessage)
|
||||
{
|
||||
validators.Add(value => Observable.Defer(() =>
|
||||
{
|
||||
return predicateWithMessage(value)
|
||||
.Select(result => String.IsNullOrEmpty(result)
|
||||
? ReactivePropertyValidationResult.Success
|
||||
: new ReactivePropertyValidationResult(ValidationStatus.Invalid, result));
|
||||
|
||||
}));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
static ReactivePropertyValidationResult Validate(TProp value, Func<TProp, string> predicateWithMessage)
|
||||
{
|
||||
var result = predicateWithMessage(value);
|
||||
|
||||
if (String.IsNullOrEmpty(result))
|
||||
return ReactivePropertyValidationResult.Success;
|
||||
|
||||
return new ReactivePropertyValidationResult(ValidationStatus.Invalid, result);
|
||||
}
|
||||
|
||||
class ValidationParameter
|
||||
{
|
||||
public TProp PropertyValue { get; set; }
|
||||
public bool RequiresReset { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class ReactivePropertyValidator<TObj, TProp> : ReactivePropertyValidator<TProp>
|
||||
{
|
||||
protected ReactivePropertyValidator()
|
||||
: base(Observable.Empty<TProp>())
|
||||
{
|
||||
}
|
||||
|
||||
public ReactivePropertyValidator(TObj This, Expression<Func<TObj, TProp>> property)
|
||||
: base(This.WhenAny(property, x => x.Value)) { }
|
||||
}
|
||||
|
||||
public static class ReactivePropertyValidatorExtensions
|
||||
{
|
||||
public static ReactivePropertyValidator<string> IfMatch(this ReactivePropertyValidator<string> This, string pattern, string errorMessage)
|
||||
{
|
||||
var regex = new Regex(pattern);
|
||||
|
||||
return This.IfTrue(regex.IsMatch, errorMessage);
|
||||
}
|
||||
|
||||
public static ReactivePropertyValidator<string> IfNotMatch(this ReactivePropertyValidator<string> This, string pattern, string errorMessage)
|
||||
{
|
||||
var regex = new Regex(pattern);
|
||||
|
||||
return This.IfFalse(regex.IsMatch, errorMessage);
|
||||
}
|
||||
|
||||
public static ReactivePropertyValidator<string> IfNullOrEmpty(this ReactivePropertyValidator<string> This, string errorMessage)
|
||||
{
|
||||
return This.IfTrue(String.IsNullOrEmpty, errorMessage);
|
||||
}
|
||||
|
||||
public static ReactivePropertyValidator<string> IfNotUri(this ReactivePropertyValidator<string> This, string errorMessage)
|
||||
{
|
||||
return This.IfFalse(s =>
|
||||
{
|
||||
Uri uri;
|
||||
return Uri.TryCreate(s, UriKind.Absolute, out uri);
|
||||
}, errorMessage);
|
||||
}
|
||||
|
||||
public static ReactivePropertyValidator<string> IfSameAsHost(this ReactivePropertyValidator<string> This, Uri compareToHost, string errorMessage)
|
||||
{
|
||||
return This.IfTrue(s =>
|
||||
{
|
||||
Uri uri;
|
||||
var isUri = Uri.TryCreate(s, UriKind.Absolute, out uri);
|
||||
return isUri && uri.IsSameHost(compareToHost);
|
||||
|
||||
}, errorMessage);
|
||||
}
|
||||
|
||||
public static ReactivePropertyValidator<string> IfContainsInvalidPathChars(this ReactivePropertyValidator<string> This, string errorMessage)
|
||||
{
|
||||
return This.IfTrue(str =>
|
||||
{
|
||||
// easiest check to make
|
||||
if (str.ContainsAny(Path.GetInvalidPathChars()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string driveLetter;
|
||||
|
||||
try
|
||||
{
|
||||
// if for whatever reason you don't have an absolute path
|
||||
// hopefully you've remembered to use `IfPathNotRooted`
|
||||
// in your validator
|
||||
driveLetter = Path.GetPathRoot(str);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Path.GetPathRoot does some fun things
|
||||
// around legal combinations of characters that we miss
|
||||
// by simply checking against an array of legal characters
|
||||
return true;
|
||||
}
|
||||
|
||||
if (driveLetter == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// lastly, check each directory name doesn't contain
|
||||
// any invalid filename characters
|
||||
var foldersInPath = str.Substring(driveLetter.Length);
|
||||
return foldersInPath.Split(new[] { '\\', '/' }, StringSplitOptions.None)
|
||||
.Any(x => x.ContainsAny(Path.GetInvalidFileNameChars()));
|
||||
}, errorMessage);
|
||||
}
|
||||
|
||||
public static ReactivePropertyValidator<string> IfPathNotRooted(this ReactivePropertyValidator<string> This, string errorMessage)
|
||||
{
|
||||
return This.IfFalse(Path.IsPathRooted, errorMessage);
|
||||
}
|
||||
|
||||
public static ReactivePropertyValidator<string> IfUncPath(this ReactivePropertyValidator<string> This, string errorMessage)
|
||||
{
|
||||
return This.IfTrue(str => str.StartsWith(@"\\", StringComparison.Ordinal), errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Octokit" version="0.6.2" targetFramework="net45" />
|
||||
<package id="reactiveui" version="6.3.1" targetFramework="net45" />
|
||||
<package id="reactiveui-core" version="6.3.1" targetFramework="net45" />
|
||||
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Main" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-XAML" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Splat" version="1.6.0" targetFramework="net45" />
|
||||
</packages>
|
Загрузка…
Ссылка в новой задаче