tonerdo / dotnet-env

A .NET library to load environment variables from .env files
MIT License
427 stars 50 forks source link

Creating a IConfigurationBuilder method extension for environment variables #64

Closed ptsneves closed 2 years ago

ptsneves commented 2 years ago

Hello, I think it would be useful to have a IConfigurationBuilder method extension for dev work. Would you be open to accept a pull request for such functionality?

rogusdev commented 2 years ago

Sure, that sounds great :)

rogusdev commented 2 years ago

You can create the pr whenever, I will close this issue now

ptsneves commented 2 years ago

Hey @rogusdev I privately created the extension but it brings Microsoft.Extensions.Configuration as a dependency. Only after I opened the issue did I notice that the dependencies of dotnet-env are very lightweight.

Sorry for wasting your time and thank you for your work on dotnet-env.

If you would be open to add the dependency I will submit the PR.

rogusdev commented 2 years ago

Yeah, I'd rather not have a new dependency for that. Tho, if your extension method is short, you could post it on this thread to help others who have the same desire :)

ptsneves commented 2 years ago

Short if not for all the boiler plate. It was inspired by aspnet code so it needs to comply with the Apache license as in the header:

//
// Licensed under the Apache License, Version 2.0.
// See https://github.com/aspnet/Configuration/blob/master/LICENSE.txt for license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using DotNetEnv;
using Microsoft.Extensions.Configuration;

namespace ProgramExtensions {
    public static class EnvFileExtension {
        public static IConfigurationBuilder LoadEnvironmentVariablesFromFile(this IConfigurationBuilder builder,
            string path, string prefix, LoadOptions options) {
            return builder.Add(new EnvFileConfigurationSource(path, prefix, options));
        }
    }

    public class EnvFileConfigurationSource : IConfigurationSource {
        private readonly LoadOptions _load_options;
        private readonly string _path;
        private readonly string _prefix;

        public EnvFileConfigurationSource(string path, string prefix, LoadOptions load_options) {
            _load_options = load_options;
            _path = path;
            _prefix = prefix;
        }

        public IConfigurationProvider Build(IConfigurationBuilder builder)
        {
            return new EnvFileConfigurationProvider(_path, _prefix, _load_options);
        }
    }

    public class EnvFileConfigurationProvider : ConfigurationProvider{
        private readonly LoadOptions _options;
        private readonly string _path;
        private readonly string _prefix;

        public EnvFileConfigurationProvider(string path, string prefix, LoadOptions options) {
            _options = options;
            _path = path;
            _prefix = prefix;
        }

        public override void Load() {
            var env = Env.Load(_path, _options).ToDictionary();

            var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            var filtered_variables = env
                .SelectMany(NormalizeKey)
                .Where(entry => ((string)entry.Key).StartsWith(_prefix, StringComparison.OrdinalIgnoreCase));

            foreach (var envVariable in filtered_variables)
            {
                var key = ((string)envVariable.Key)[_prefix.Length..];
                data[key] = (string)envVariable.Value;
            }

            Data = data;
        }

        private static IEnumerable<DictionaryEntry> NormalizeKey(KeyValuePair<string, string> e) {
            var (key, value) = e;
            yield return new DictionaryEntry() {
                Key = NormalizeKey(key),
                Value = value
            };
        }

        private static string NormalizeKey(string key)
        {
            return key.Replace("__", ConfigurationPath.KeyDelimiter);
        }
    }
}