@@ -3,17 +3,20 @@ module Nap.Core.Environment
33
44open System
55open System.IO
6+
67/// Parse a .napenv file (simple key = value format, TOML-like)
78let parseEnvFile ( content : string ) : Map < string , string > =
8- content.Split([| '\n' ; '\r' |], StringSplitOptions.RemoveEmptyEntries)
9+ content.Split([| '\n' ; '\r' |], StringSplitOptions.RemoveEmptyEntries)
910 |> Array.choose ( fun line ->
1011 let line = line.Trim()
11- if line = " " || line.StartsWith " #" then None
12+
13+ if line = " " || line.StartsWith " #" then
14+ None
1215 elif line.Contains " =" then
13- let parts = line.Split([| '=' |], 2 )
14- Some ( parts.[ 0 ]. Trim(), parts.[ 1 ]. Trim() .Trim( '"' ))
15- else None
16- )
16+ let parts = line.Split([| '=' |], 2 )
17+ Some( parts.[ 0 ]. Trim(), parts.[ 1 ]. Trim() .Trim( '"' ))
18+ else
19+ None )
1720 |> Map.ofArray
1821
1922let private mergeInto ( source : Map < string , string >) ( target : Map < string , string >) =
@@ -26,21 +29,29 @@ let private mergeInto (source: Map<string, string>) (target: Map<string, string>
2629/// 3 . Named environment file (.napenv.{name})
2730/// 4 . Base .napenv
2831/// 5 . [ vars] block in the .nap file
29- let loadEnvironment ( dir : string ) ( envName : string option ) ( cliVars : Map < string , string >) ( fileVars : Map < string , string >) : Map < string , string > =
32+ let loadEnvironment
33+ ( dir : string )
34+ ( envName : string option )
35+ ( cliVars : Map < string , string >)
36+ ( fileVars : Map < string , string >)
37+ : Map < string , string > =
3038 let readIfExists path =
3139 if File.Exists path then
3240 File.ReadAllText path |> parseEnvFile
33- else Map.empty
41+ else
42+ Map.empty
3443
3544 let baseEnv = readIfExists ( Path.Combine( dir, " .napenv" ))
3645 Logger.debug $" Loaded .napenv: {baseEnv.Count} vars"
46+
3747 let namedEnv =
3848 match envName with
3949 | Some name ->
4050 let env = readIfExists ( Path.Combine( dir, $" .napenv.{name}" ))
4151 Logger.debug $" Loaded .napenv.{name}: {env.Count} vars"
4252 env
4353 | None -> Map.empty
54+
4455 let localEnv = readIfExists ( Path.Combine( dir, " .napenv.local" ))
4556 Logger.debug $" Loaded .napenv.local: {localEnv.Count} vars"
4657
@@ -54,69 +65,79 @@ let loadEnvironment (dir: string) (envName: string option) (cliVars: Map<string,
5465let resolveVars ( vars : Map < string , string >) ( input : string ) : string =
5566 let sb = System.Text.StringBuilder()
5667 let mutable i = 0
68+
5769 while i < input.Length do
5870 if i + 3 < input.Length && input.[ i] = '{' && input.[ i + 1 ] = '{' then
5971 let start = i + 2
6072 let mutable j = start
61- while j < input.Length && input.[ j] <> '}' && Char.IsLetterOrDigit( input.[ j]) || input.[ j] = '_' do
73+
74+ while j < input.Length && input.[ j] <> '}' && Char.IsLetterOrDigit( input.[ j])
75+ || input.[ j] = '_' do
6276 j <- j + 1
77+
6378 if j + 1 < input.Length && input.[ j] = '}' && input.[ j + 1 ] = '}' && j > start then
6479 let key = input.Substring( start, j - start)
80+
6581 match Map.tryFind key vars with
6682 | Some v -> sb.Append( v) |> ignore
6783 | None -> sb.Append( input, i, j + 2 - i) |> ignore
84+
6885 i <- j + 2
6986 else
7087 sb.Append( input.[ i]) |> ignore
7188 i <- i + 1
7289 else
7390 sb.Append( input.[ i]) |> ignore
7491 i <- i + 1
92+
7593 sb.ToString()
7694
7795/// Detect available environment names by scanning a directory for .napenv.{name} files.
7896/// Excludes .napenv (base) and .napenv.local (secrets). Returns sorted unique names.
7997let detectEnvironmentNames ( dir : string ) : string list =
80- if not ( Directory.Exists dir) then []
98+ if not ( Directory.Exists dir) then
99+ []
81100 else
82101 let prefix = " .napenv."
83102 let localSuffix = " .local"
103+
84104 Directory.GetFiles( dir, " .napenv.*" )
85105 |> Array.choose ( fun path ->
86106 let fileName = Path.GetFileName( path)
87- if fileName = " .napenv" then None
88- elif fileName.EndsWith( localSuffix) then None
107+
108+ if fileName = " .napenv" then
109+ None
110+ elif fileName.EndsWith( localSuffix) then
111+ None
89112 elif fileName.StartsWith( prefix) then
90- Some ( fileName.Substring( prefix.Length))
91- else None
92- )
113+ Some( fileName.Substring( prefix.Length))
114+ else
115+ None )
93116 |> Array.distinct
94117 |> Array.sort
95118 |> Array.toList
96119
97120/// Resolve all variables in a NapFile's request
98121let resolveNapFile ( vars : Map < string , string >) ( napFile : NapFile ) : NapFile =
99122 let resolve = resolveVars vars
123+
100124 { napFile with
101- Request = {
102- napFile.Request with
125+ Request =
126+ { napFile.Request with
103127 Url = resolve napFile.Request.Url
104128 Headers = napFile.Request.Headers |> Map.map ( fun _ v -> resolve v)
105129 Body =
106130 napFile.Request.Body
107- |> Option.map ( fun b -> { b with Content = resolve b.Content })
108- }
131+ |> Option.map ( fun b -> { b with Content = resolve b.Content }) }
109132 Assertions =
110- napFile.Assertions |> List.map ( fun a ->
133+ napFile.Assertions
134+ |> List.map ( fun a ->
111135 { a with
112136 Op =
113137 match a.Op with
114- | Equals v -> Equals ( resolve v)
115- | Contains v -> Contains ( resolve v)
116- | Matches v -> Matches ( resolve v)
117- | LessThan v -> LessThan ( resolve v)
118- | GreaterThan v -> GreaterThan ( resolve v)
119- | Exists -> Exists
120- }
121- )
122- }
138+ | Equals v -> Equals( resolve v)
139+ | Contains v -> Contains( resolve v)
140+ | Matches v -> Matches( resolve v)
141+ | LessThan v -> LessThan( resolve v)
142+ | GreaterThan v -> GreaterThan( resolve v)
143+ | Exists -> Exists }) }
0 commit comments