Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Argu.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@
<Project Path="src/Argu.Extensions.Configuration/Argu.Extensions.Configuration.fsproj" />
<Project Path="benchmarks/Argu.Benchmarks/Argu.Benchmarks.fsproj" />
<Project Path="tests/Argu.Tests/Argu.Tests.fsproj" />
<Project Path="src/Argu.ConfigurationManager/Argu.ConfigurationManager.fsproj" />
</Solution>
22 changes: 22 additions & 0 deletions src/Argu.ConfigurationManager/Argu.ConfigurationManager.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="Library.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="DotNet.ReproducibleBuilds" PrivateAssets="All" />
<PackageReference Include="FSharp.Core" />
<PackageReference Include="System.Configuration.ConfigurationManager" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Argu\Argu.fsproj" />
</ItemGroup>

</Project>
49 changes: 49 additions & 0 deletions src/Argu.ConfigurationManager/Library.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
namespace Argu

open System.Configuration
open System.IO

/// AppSettings XML configuration reader
type AppSettingsConfigurationReader () =
interface IConfigurationReader with
member _.Name = "AppSettings configuration reader"
member _.GetValue(key : string) = ConfigurationManager.AppSettings[key]

/// AppSettings XML configuration reader
type AppSettingsConfigurationFileReader private (xmlPath : string, kv : KeyValueConfigurationCollection) =
member _.Path = xmlPath
interface IConfigurationReader with
member _.Name = $"App.config configuration reader: %s{xmlPath}"
member _.GetValue(key : string) =
match kv[key] with
| null -> null
| entry -> entry.Value

/// Create used supplied XML file path
static member Create(path : string) =
if not <| File.Exists path then raise <| FileNotFoundException(path)
let fileMap = ExeConfigurationFileMap(ExeConfigFilename = path)
let config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None)
AppSettingsConfigurationFileReader(path, config.AppSettings.Settings)

[<AutoOpen>]
module ConfigurationReaderExtensions =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont think there's a benefit to this layering - type augmentation in namespace should be good?

open System.Reflection
open System

/// Configuration reader implementations
type ConfigurationReader with

/// Create a configuration reader instance using the application's resident AppSettings configuration
static member FromAppSettings() : IConfigurationReader = AppSettingsConfigurationReader()

/// Create a configuration reader instance using a local xml App.Config file
static member FromAppSettingsFile(path : string) : IConfigurationReader = AppSettingsConfigurationFileReader.Create path

/// Create a configuration reader instance using the location of an assembly file
static member FromAppSettings(assembly : Assembly) : IConfigurationReader =
let path = assembly.Location
if String.IsNullOrEmpty path then
invalidArg assembly.FullName $"Assembly location for '{assembly.Location}' is null or empty."

AppSettingsConfigurationFileReader.Create(path + ".config") :> IConfigurationReader
3 changes: 1 addition & 2 deletions src/Argu/Argu.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
<!-- Removal triggers issues in dotnet publish, e.g. for Lambda projects -->
<!-- Also avoids Rider search finding stuff in FSharp.Core.xml -->
<PackageReference Include="FSharp.Core" ExcludeAssets="contentfiles" />

<PackageReference Include="System.Configuration.ConfigurationManager"/>

</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion src/Argu/ArgumentParser.fs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ and [<Sealed; NoEquality; NoComparison; AutoSerializable(false)>]
let configurationReader =
match configurationReader with
| Some c -> c
| None -> ConfigurationReader.FromAppSettings()
| None -> ConfigurationReader.NullReader

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better to simply make reader mandatory otherwise people need to read the docs to discover the semantic change which is a killer for something like this where the people that picked Argu are long gone, the app people don't care, and an LLM or infra specialist is doing dependency updates

(and as a followup also remove argv defaulting)
See also #317 comments in this direction


try
let appSettingsResults = parseKeyValueConfig configurationReader argInfo
Expand Down
38 changes: 0 additions & 38 deletions src/Argu/ConfigReaders.fs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
namespace Argu

open System
open System.Configuration
open System.Collections.Generic
open System.IO
open System.Reflection

/// Abstract key/value configuration reader
type IConfigurationReader =
Expand Down Expand Up @@ -54,29 +51,6 @@ type FunctionConfigurationReader (configFunc : string -> string option, ?name :
member _.Name = name
member _.GetValue(key : string) = configFunc key |> Option.toObj

/// AppSettings XML configuration reader
type AppSettingsConfigurationReader () =
interface IConfigurationReader with
member _.Name = "AppSettings configuration reader"
member _.GetValue(key : string) = ConfigurationManager.AppSettings[key]

/// AppSettings XML configuration reader
type AppSettingsConfigurationFileReader private (xmlPath : string, kv : KeyValueConfigurationCollection) =
member _.Path = xmlPath
interface IConfigurationReader with
member _.Name = $"App.config configuration reader: %s{xmlPath}"
member _.GetValue(key : string) =
match kv[key] with
| null -> null
| entry -> entry.Value

/// Create used supplied XML file path
static member Create(path : string) =
if not <| File.Exists path then raise <| FileNotFoundException(path)
let fileMap = ExeConfigurationFileMap(ExeConfigFilename = path)
let config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None)
AppSettingsConfigurationFileReader(path, config.AppSettings.Settings)

/// Configuration reader implementations
[<AbstractClass; Sealed>]
type ConfigurationReader =
Expand Down Expand Up @@ -108,15 +82,3 @@ type ConfigurationReader =
let read (key : string) = inner.GetValue(prefix + key) |> Option.ofObj
FunctionConfigurationReader(read, name = $"Environment Variables (prefix=%s{prefix})")

/// Create a configuration reader instance using the application's resident AppSettings configuration
static member FromAppSettings() : IConfigurationReader = AppSettingsConfigurationReader()

/// Create a configuration reader instance using a local xml App.Config file
static member FromAppSettingsFile(path : string) : IConfigurationReader = AppSettingsConfigurationFileReader.Create path

/// Create a configuration reader instance using the location of an assembly file
static member FromAppSettings(assembly : Assembly) : IConfigurationReader =
let path = assembly.Location
if String.IsNullOrEmpty path then
invalidArg assembly.FullName $"Assembly location for '{assembly.Location}' is null or empty."
AppSettingsConfigurationFileReader.Create(path + ".config") :> IConfigurationReader
3 changes: 2 additions & 1 deletion tests/Argu.Tests/Argu.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
<Compile Include="SeparatorAttributeTests.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Argu\Argu.fsproj"/>
<ProjectReference Include="..\..\src\Argu.ConfigurationManager\Argu.ConfigurationManager.fsproj" />
<ProjectReference Include="..\..\src\Argu\Argu.fsproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FSharp.Core"/>
Expand Down
Loading