/// <summary>
        /// Combine two <see cref="Errorable{T}"/> values and return a result.
        /// </summary>
        /// <remarks>Any errors already present are preserved.</remarks>
        /// <typeparam name="T">Type of value possibly present in <paramref name="first"/>.</typeparam>
        /// <typeparam name="A">Type of value possibly present in <paramref name="second"/>.</typeparam>
        /// <typeparam name="R">Type of return value.</typeparam>
        /// <param name="first">First errorable value.</param>
        /// <param name="second">Second errorable value.</param>
        /// <param name="combinerFunc">Function to combine both values when available.</param>
        /// <returns>An errorable containing either the result of combining both available values,
        /// or a combined set of errors.</returns>
        public static Errorable <R> Combine <T, A, R>(
            this Errorable <T> first,
            Errorable <A> second,
            Func <T, A, R> combinerFunc)
        {
            if (first == null)
            {
                throw new ArgumentNullException(nameof(first));
            }

            if (second == null)
            {
                throw new ArgumentNullException(nameof(second));
            }

            if (combinerFunc == null)
            {
                throw new ArgumentNullException(nameof(combinerFunc));
            }

            if (first.HasValue && second.HasValue)
            {
                return(Errorable.Success(
                           combinerFunc(first.Value, second.Value)));
            }

            var allErrors = first.Errors.Union(second.Errors);

            return(Errorable.Failure <R>(allErrors));
        }
示例#2
0
        /// <summary>
        /// Find a certificate based on the provided thumbprint
        /// </summary>
        /// <param name="purpose">A use for which the certificate is needed (for human consumption).</param>
        /// <param name="thumbprint">Thumbprint of the certificate we need.</param>
        /// <returns>Certificate, if found; null otherwise.</returns>
        public Errorable <X509Certificate2> FindByThumbprint(string purpose, CertificateThumbprint thumbprint)
        {
            var query =
                from name in _storeNames
                from location in _storeLocations
                select FindByThumbprint(thumbprint, name, location);

            var candidates = query.Where(cert => cert != null).ToList();

            var certWithPrivateKey = candidates.Find(cert => cert.HasPrivateKey);

            if (certWithPrivateKey != null)
            {
                // We might have multiple copies of the same certificate available in different stores.
                // If so, prefer any copies that have their private key over those that do not
                // Certificates with private keys can be used to both encrypt/decrypt and to
                // sign/verify - copies without can only be used to encrypt and verify.
                return(Errorable.Success(certWithPrivateKey));
            }

            var certificate = candidates.FirstOrDefault();

            if (certificate != null)
            {
                return(Errorable.Success(certificate));
            }

            return(Errorable.Failure <X509Certificate2>($"Did not find {purpose} certificate {thumbprint}"));
        }
        /// <summary>
        /// Try to parse a string into a DateTimeOffset
        /// </summary>
        /// <param name="value">The string value to parse.</param>
        /// <param name="name">Name to use for the value if there is an error.</param>
        /// <returns>A succesfully parsed <see cref="DateTimeOffset"/> or errors detailing what
        /// went wrong.</returns>
        public Errorable <DateTimeOffset> TryParse(string value, string name)
        {
            if (DateTimeOffset.TryParseExact(
                    value, ExpectedFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var result))
            {
                // Correctly parsed in the expected format
                return(Errorable.Success(result));
            }

            if (DateTimeOffset.TryParse(
                    value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result))
            {
                // Correctly parsed with detected format
                return(Errorable.Success(result));
            }

            if (DateTimeOffset.TryParse(value, out result))
            {
                // Correctly parsed with detected format
                return(Errorable.Success(result));
            }

            return(Errorable.Failure <DateTimeOffset>(
                       $"Unable to parse {name} timestamp '{value}' (expected format is '{ExpectedFormat}')"));
        }