DERO-HE STARGATE Testnet Release27
This commit is contained in:
parent
ac9d12577c
commit
2ef26faa40
@ -123,9 +123,9 @@ func (chain *Blockchain) Get_Difficulty_At_Tips(tips []crypto.Hash) *big.Int {
|
||||
step := new(big.Int)
|
||||
|
||||
if globals.IsMainnet() {
|
||||
MinimumDifficulty = new(big.Int).SetUint64(config.MAINNET_MINIMUM_DIFFICULTY) // this must be controllable parameter
|
||||
MinimumDifficulty = new(big.Int).SetUint64(config.Settings.MAINNET_MINIMUM_DIFFICULTY) // this must be controllable parameter
|
||||
} else {
|
||||
MinimumDifficulty = new(big.Int).SetUint64(config.TESTNET_MINIMUM_DIFFICULTY) // this must be controllable parameter
|
||||
MinimumDifficulty = new(big.Int).SetUint64(config.Settings.TESTNET_MINIMUM_DIFFICULTY) // this must be controllable parameter
|
||||
}
|
||||
GenesisDifficulty := new(big.Int).SetUint64(1)
|
||||
|
||||
|
@ -327,12 +327,12 @@ func (pool *Mempool) Mempool_Print() {
|
||||
})
|
||||
|
||||
loggerpool.Info(fmt.Sprintf("Total TX in mempool = %d\n", len(klist)))
|
||||
loggerpool.Info(fmt.Sprintf("%20s %14s %7s %7s %6s %32s\n", "Added", "Size", "Height", "TXID"))
|
||||
loggerpool.Info(fmt.Sprintf("%20s %7s %6s %32s\n", "Added", "Size", "Height", "TXID"))
|
||||
|
||||
for i := range klist {
|
||||
k := klist[i]
|
||||
v := vlist[i]
|
||||
loggerpool.Info(fmt.Sprintf("%20s %14s %7d %7d %6d %32s\n", time.Unix(int64(v.Added), 0).UTC().Format(time.RFC3339),
|
||||
loggerpool.Info(fmt.Sprintf("%20s %7d %6d %32s\n", time.Unix(int64(v.Added), 0).UTC().Format(time.RFC3339),
|
||||
len(v.Tx.Serialize()), v.Height, k))
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func Test_mempool(t *testing.T) {
|
||||
|
||||
// this tx is from internal testnet
|
||||
// tx_id 499002f3fb7fea8a71dac93dea65c0ff74be05b0858078a27a48d78b71eacf87
|
||||
tx_hex := "010000030101000000000000000000000000000000000000000000000000000000000000000000002aa937d8879cebc74425f4fee7bcc81b424dd6f2130d0548a0c4afa7f9e40b1c41ee8f9487971ee3ecf35cef40d26b6382b49b6315370f96696cda3394a550c4bd5d301a3b91737c354573ffa0d9e921a523af274b2ccc5d7078efd5c489c8deba43a0205aafd9f89b80cd432825f2f0bcbfa61f82bc30c2a83a7976e6743aad48fbdb8c7799cbf3131d156d2ac7b780c80202000d136ae832342f1ecf99a935e1dcdc255c7f33297b204383e7539f31278fd097001e261e85c34489c313aaa05ce4abb3772d31e5d192321dc2cce7fe45b21f4d114244b8fbf5a6cc1f0119b69ed785e7c8739c306b4a35278159bbb6977a28ea71ea0ffc5a6398ec935e000b8b5806e6e7a0bd8b57e24edd21cfafa79a54bf7d3bc74643294b88920b3b900113dad7529adb51a5b634140f01c8389c57c92af984d16cf4400a463a448fd7e7007ed6b651c46aab06f6f9677d50ee78cf98a3e121b325a0cab4bf67910f9a92d0153222d7f87cedc63a5b773a5c5482479e554d56d906eaf5d5f9b1c37d9f52170029bd3c397bcb89dca495d7ee8cab0e5e4cd8b204dff933e8673cacece063830d010676be97476c5028c633ca3160a7367352750304ee2be99bf6311c198b1207a20006364d038c0d479f5264c085c8f210ea88f8a7f08535240c3541c641aadae496002449b5477c26ff74be1159c36d71d9dbca3ab2c2a0804590637fb79e0d89e5d801053afa58bcdbf17aa0ce544e3ca12e9b5c204aaea6a0b5698fef75669c1d91690114acfe9439eff58fe532aa243187adadf7118ba78f9772429b3041a07cb203df0125ffa09b619faa1b890d0a5323a4f162b4d0d1a6864f9a0261820bfc901dea4501098ad5f3c3bb0629de269f3e01633b4e0ee6a8f74e648aa40f57dd2496ed7230010f100ede75074bafc160fc1327545a44fd8d34627b136fed6de0bfa72df0e1330116108dc5545f42b9f5b8e302d1546930d9840c18fc92e425bacd0856e842f60f011b7f689aa43b95bbfd02ee20aab2cac2077c9639b9e4f84b8b221b37cdaf13a2010b979e41274c4338395dfc447c0d4a548caf414ee425aab1748475cca9faef4e012051ff90139adccfbf0459e3a5c0638dfb1654ee728cba59c6803ab1daaddda9011ef927e921f956134892499c7513bc2e077986295c050633a1566d449fd0954a00071f56b24c65aaa4549be3c859ac8ec75fe302d89e8755166da3f7d0d08ae285011983fdc2aa2a0611187b0b86119508e3f7642dfe7380f5bdbe6ad27f0dc4ccf201127e3c3923518fec22a4ead80845bbe6008d9b790bd50ec6ef83b087d0490995011982ad7c49288a3115c638a04e14a0c74a75008359b40aff422e9972e276929800237a7bfd1fe105865abbc36e926de565530da393da1ddc0b1005bb0c31504402002569e7e95e54711903c29f63cb8c6b58d47ea3299c455c8738b543d552e8dffe012ea47467abedda0d168d06bcf2f45ab8fa090b90d6e4662d4052df0488084403057542fc7603a7d717dd8267dc036330e3083d2a1562bf5b1ecfbce99804c05100000000000000000000000000000000000000000000000000000000000000000e377bd9c8a2b6faf14c766e51fc9415ed979d604e3d5dcc800b3c5a9c6bec1f239a65bdeb2028926a6fa13fdf2caf0a09ba17e1420b74bef294aa4c9731d2c71f46ae2c57612f525f14cf49e86be5a984aab4d8c25ec3818e19612ba07722d500082c5b600665b3a718fad7516b976749ac3692a6ccf60225c04d1da272a95780012c932f36c1cca1753377ad16b2dbecabb58a2efbf8e88a066d80efba20addf8208d52649fc24ba86ecd87da10efd8ab480c48cadf4ee205e60d10f6e1f1527dd01171915999416a85d08a06e4a8ed18797fcd62910e6624222e120bdd60a00db22fb4d9f0a7aee7570f72b99c673195c67b7dc5e53aa5a32ffc04b409aa79cb52faa6cb8374cd242bd90fd2da9a891fc18022112ecb943b8b0f6c69a4b2e33ad0b16abbd22b38f11f1e76bfd6c1950864753ef84ce0ba869b82f909fbe2751ef0e02b170d107ec7cf6c25d9fdbdd86f5e4790e532254f89ad6cd477bf30816e207ce092de3148c4fd818812abfcadc03a325991fb7dd48b5a54266137709715a11556344ae9bbb2afd1e7ea7fa417f1308d20fdef8dd2168f89101d0643ce62217ee68724101b6e8644ad0860940f95b1674a1befa7ec45235d2c93043087e9001211d72b9f744364770a276f99a183dc184f90ad09d034119f5e4754249046bb201156984d6a418664bda88f98075679d6bb8cc4c644ff727278931797db12a2c65012698726eabb2580faecafa12befe8855a7074da2b4f2d2cd74fd481e818f2287001d445415f6abc3b4a9ff7043906d8f83d5c61ad853b12a72c152a21b9627d8bf00004d5a3deaf642e40e1401ead28babb7ce0263afcc3b3f1e7876a31d6fbea5e90125bff3189cf6ab0e68caa851e60fc92483d3bc2a5ecd511594e7f80f565301c101247b8928fb1866a98b99add332a7e6065cf27ed8a7d48d7775727299308f0ab9002cb7663076e2883fe31b3397f6f5460491835f1a23c173f858fb93cbc312cf360117c5b8e4a25c2e3228b6f4d06525f96e818772292b095b7d344eb3f9f18735bd000889c0fcc3c2f25fe74c915b178db653b0d7725efdc6e2e80a2770738cc3bff9002a578a39364fa16dc82fd7f15872da799f2ddc6422491b9e5c6447ea87adb01a00187461816522889fc2bcb1dc1b0b5d2537df3ae2bb58cbef2b58cb678ba6b9f30121f1cf89f1e9840aace8469b06edf81e9930ddfb8aef4a2c3d46e8551cc40ab400"
|
||||
tx_hex := "01000003519b9e874a9dd7fcbfb7802268123dcf970c336891101a3a0705855b72b9eb08c80100000000000000000000000000000000000000000000000000000000000000000000f394d06566a81b024fd8624b4c8592f8b9344e58f227261eb08d821bd190b4ac9c7df0660038ddcf881602a14fe5ad8eebff83d3ce3541a1f232fe61acf71109e417160936863e8c0597c798b73ef08e76b8eeebcaf20260ba91fcdef5f626361f824dc6197b02c599d4511c624a933226d8fccc125611ec80d8ad5acea4baf2f1c77e4c5525d9859b40f43daaf3d4e4450203d80419dcb026b9adbd7d6912284ee9ce20f3721076947f16e1faefd9b8c35116f4cf0044ead0a12fda3f3bcf2ed91627b922c02c10d112df9e782d3750cda151f6ba2ebe6495065141f32800894566011198c4055458005a31d3e285037446ba14bac54420bcab87cbebeaccab1f158b011e90132b7310e6c866e348cab68d21bad1d9f811b4d667a1e40bdef1a19259d3000b972ae06da958658b12ca4a45c2c6ea50d305be86f7e979fc962c687eb012f401a2c4bf0beb6c713343b94d65ee6d937660480daf670ab5b0ed421a9103e8a25119c836dd799600e67e4ec66ef680833398a99b16956aed2ee5bd9e6c8e68009a01145636e5e448d6c8ab902db394aa9c5aeee92dd150e1150d5b8f6674f330c279010912df27232ae126525a130ed8a9b4dee62eb57ce1d8a42ae9bcef867b10ce94012f94aa78846fcdda504243f83f3122aee5dc38dd32fa2a90224b3a40b4cf79c301129028709e39591c55e6e477a36cc26dc2efd7183fb59f51635dbd114dfed9d5010d66f31b781fa181c2114600ee1f03d55439355265700fb9d35ac684a19db398001e12558cc17e59fb85fb825481dee6fef3612c043bb38ca4833183da5381364600077a99d4d2df2f06067359f178a40f98a3e0943309c5ce3ce5398b9909174254012aa49cfc78ebec51c00e7824db0d8d41ef43fbb0396824d2c31b7db64124cc61000a8b51bd6c94a502f7aea3e5001f00d76b165ad580e48f8aa72813da8240286e00055f3480aa7cdb68a20d4b1e567d42389b44cf4e345da9e5a5655a80418695bc012c8f1e1ef8af646b4fb08afaeb3febde17c4c0d04b30dfd22d96a32ced9c9439010c40484e1547d307ad56b79bb7450e4eaaed7ed79bd5392dffd7449043f8783a0015104264f8d80356176ca6fdf4777e4ebc8edcbab50f8b6c366ac75ebd6bb5c701078d55053d2d41f21f51d5e617f282ba9dfa2576f0231048dd73998ceaa699fd0105821b61addd6935c7d80e65c706f0f2aa2d73ad22c5548c9c8b92cd82689dc40115f576c413a37014b9ee5377b121c69ad6e0f00f2b4f6c99d1f68b00a5d923370119b29fe6e9cf35f1344e24d52ee1fdb6521de3176f3d2b2a2b9327ae6e6cfbb10017198369a6ff180253700361c40e7f378c8e7ead6e04f01dbf5b4c47e7e1e4ff001abaf84628764eccbc5d0f66dc0a99505bb0e598362c45312470526afad11caf00242c6a902c564b3b363eb4ae08f643f18571779428358bfbdaf9856b0ebf229f0100000000000000000000000000000000000000000000000000000000000000001b7a78b6f573d4ac5d3a8d7d10dafac0dbc92604474417b29d40448d9fbc821d0fe3f73da41b27752d5db0658fcc0bff7da21112417ac719e491fdd43c8e5d0b07bcca22c27367a276e6a4e0cfb1bfbc772842898356ad4723101d4ada5137f409bb3b290d7519da3af15feb09e183e2be809690099551f1cb984deb1295b7611dd0cfd9000482314a388512d278fc22b2f6e3fc6e95023b4bd228e22626b7440119fa2c6f45a33b008ef5b23a5522ebbf0bf5f26d8263613a7f9b5ad9a192357600107a023453f7f79f413756dfba0096ae5d13158bbbb147089a020e819ef6640728acf01c811b967d1a14d767ac20088c5ca1613aa5d94ebe7fe2840b6431d8d002137dae183071d12cd9fb14c4b4738ef750ff514ce26257d1245eaedfea625516bfcb075a33a8910dc4e40742bfaa70d95591711e9db33d0db0be06cc819817072c22a9c81f4369f68bb1ef71c7f7458671d647caef18b30cca91da4f1b201d082a03306b4a07d1f0e1a8cc8844038fc524201a6c3a346a971203fd45458d292987d44049eecdac25c5622465a8d683a43621612d2fb283724b17a7bfa3bad2266a6fb1cf669eef21c90e24e85d6ab48e8b237355e964407dde00d65fb442dd210ce0378349f533c0e8ab65293de5319e246a044ed22b1e6db28add1508dd6f0da22954eea8a431b211a8b54147e32fec6593f9fb731389f62f94d5047e0f3301216576c71a79629bc3879325280e216d59cf7e6ff2d6a0babe418f8f2ed603ce010e16785f8c855043221ed5a603d4ed4e8e90059669543738ceb2a04dc996079c01264e9beaf4bb82e1b8f47bb0c239167064001e306eeef2b5495ec42754806b65012357f738543b13594ff081a070dbc0575645ac5ce8e0f89d3afdeb3d49e9ddae012a17d5095b9cef08f6eee2ce733dbf2cfcdb8c23e39410ed973e68cf8505266d00280c52b45752dd12ba84499618898111a8f9b19f2b76c06917e57eef221f3387011cc3360b478d144118a82a3c4391814f34aac2fb0f2d043394304bed179689e501292ca5631820f5f465242b4cc517f06c153c176a43f77daca463b7e2d3e2d7ba001aa0b2c90c3172cd2556137d78748f47924432449210d8b2f5e27adb6210bc77012c92a12526a5b0b1ef60994eb4d08e9bb5a3ea330d417838a52ad8636d37fa9e00131153c9affeefd6b98ecb6a80f5e6ea97417376030325182c064e12dd353c32011f4e25c49eea09adaa46d56642e2ea0afced2d75036acd3839435863f1ec3bf1002e19fe8456a7236c8fb77a15f87164debaad865e85c3468152a5deaffde3da6a01"
|
||||
|
||||
var tx, dup_tx transaction.Transaction
|
||||
|
||||
|
@ -166,12 +166,15 @@ func (chain *Blockchain) Create_new_miner_block(miner_address rpc.Address) (cbl
|
||||
}
|
||||
|
||||
height := chain.Calculate_Height_At_Tips(bl.Tips) // we are 1 higher than previous highest tip
|
||||
|
||||
history := map[crypto.Hash]bool{}
|
||||
|
||||
var history_array []crypto.Hash
|
||||
for i := range bl.Tips {
|
||||
history_array = append(history_array, chain.get_ordered_past(bl.Tips[i], 26)...)
|
||||
h := height - 20
|
||||
if h < 0 {
|
||||
h = 0
|
||||
}
|
||||
history_array = append(history_array, chain.get_ordered_past(bl.Tips[i], h)...)
|
||||
}
|
||||
for _, h := range history_array {
|
||||
history[h] = true
|
||||
|
@ -17,6 +17,7 @@
|
||||
package config
|
||||
|
||||
import "github.com/satori/go.uuid"
|
||||
import "github.com/caarlos0/env/v6"
|
||||
import "github.com/deroproject/derohe/cryptography/crypto"
|
||||
|
||||
// all global configuration variables are picked from here
|
||||
@ -52,12 +53,20 @@ const MAX_RINGSIZE = 128 // <= 128, ringsize will be accepted
|
||||
// Minimum FEE calculation constants are here
|
||||
const FEE_PER_KB = uint64(100) // .00100 dero per kb
|
||||
|
||||
const MAINNET_BOOTSTRAP_DIFFICULTY = uint64(80000000) // atlantis mainnet botstrapped at 80 MH/s
|
||||
const MAINNET_MINIMUM_DIFFICULTY = uint64(800000000) // 80 MH/s
|
||||
|
||||
// testnet bootstraps at 1 MH
|
||||
const TESTNET_BOOTSTRAP_DIFFICULTY = uint64(10000) // testnet bootstrap at 50KH/s
|
||||
const TESTNET_MINIMUM_DIFFICULTY = uint64(10000) // 10KH/s
|
||||
type SettingsStruct struct {
|
||||
MAINNET_BOOTSTRAP_DIFFICULTY uint64 `env:"MAINNET_BOOTSTRAP_DIFFICULTY" envDefault:"80000000"`
|
||||
MAINNET_MINIMUM_DIFFICULTY uint64 `env:"MAINNET_MINIMUM_DIFFICULTY" envDefault:"80000000"`
|
||||
|
||||
TESTNET_BOOTSTRAP_DIFFICULTY uint64 `env:"TESTNET_BOOTSTRAP_DIFFICULTY" envDefault:"10000"`
|
||||
TESTNET_MINIMUM_DIFFICULTY uint64 `env:"TESTNET_MINIMUM_DIFFICULTY" envDefault:"10000"`
|
||||
}
|
||||
|
||||
var Settings SettingsStruct
|
||||
|
||||
var _ = env.Parse(&Settings)
|
||||
|
||||
|
||||
|
||||
// this single parameter controls lots of various parameters
|
||||
// within the consensus, it should never go below 7
|
||||
|
@ -20,4 +20,4 @@ import "github.com/blang/semver/v4"
|
||||
|
||||
// right now it has to be manually changed
|
||||
// do we need to include git commitsha??
|
||||
var Version = semver.MustParse("3.4.80-1.DEROHE.STARGATE+20112021")
|
||||
var Version = semver.MustParse("3.4.82-1.DEROHE.STARGATE+20112021")
|
||||
|
@ -146,13 +146,13 @@ func Connection_Pending_Clear() {
|
||||
if time.Now().Sub(v.update_received).Round(time.Second).Seconds() > 20 {
|
||||
v.exit()
|
||||
Connection_Delete(v)
|
||||
v.logger.Info("Purging connection due since idle")
|
||||
v.logger.V(1).Info("Purging connection due since idle")
|
||||
}
|
||||
|
||||
if IsAddressInBanList(Address(v)) {
|
||||
v.exit()
|
||||
Connection_Delete(v)
|
||||
v.logger.Info("Purging connection due to ban list")
|
||||
v.logger.V(1).Info("Purging connection due to ban list")
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
@ -112,7 +112,7 @@ func Test_TX_Valid(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "4 ring test",
|
||||
txhex: "010000030101000000000000000000000000000000000000000000000000000000000000000000002aa937d8879cebc74425f4fee7bcc81b424dd6f2130d0548a0c4afa7f9e40b1c41ee8f9487971ee3ecf35cef40d26b6382b49b6315370f96696cda3394a550c4bd5d301a3b91737c354573ffa0d9e921a523af274b2ccc5d7078efd5c489c8deba43a0205aafd9f89b80cd432825f2f0bcbfa61f82bc30c2a83a7976e6743aad48fbdb8c7799cbf3131d156d2ac7b780c80202000d136ae832342f1ecf99a935e1dcdc255c7f33297b204383e7539f31278fd097001e261e85c34489c313aaa05ce4abb3772d31e5d192321dc2cce7fe45b21f4d114244b8fbf5a6cc1f0119b69ed785e7c8739c306b4a35278159bbb6977a28ea71ea0ffc5a6398ec935e000b8b5806e6e7a0bd8b57e24edd21cfafa79a54bf7d3bc74643294b88920b3b900113dad7529adb51a5b634140f01c8389c57c92af984d16cf4400a463a448fd7e7007ed6b651c46aab06f6f9677d50ee78cf98a3e121b325a0cab4bf67910f9a92d0153222d7f87cedc63a5b773a5c5482479e554d56d906eaf5d5f9b1c37d9f52170029bd3c397bcb89dca495d7ee8cab0e5e4cd8b204dff933e8673cacece063830d010676be97476c5028c633ca3160a7367352750304ee2be99bf6311c198b1207a20006364d038c0d479f5264c085c8f210ea88f8a7f08535240c3541c641aadae496002449b5477c26ff74be1159c36d71d9dbca3ab2c2a0804590637fb79e0d89e5d801053afa58bcdbf17aa0ce544e3ca12e9b5c204aaea6a0b5698fef75669c1d91690114acfe9439eff58fe532aa243187adadf7118ba78f9772429b3041a07cb203df0125ffa09b619faa1b890d0a5323a4f162b4d0d1a6864f9a0261820bfc901dea4501098ad5f3c3bb0629de269f3e01633b4e0ee6a8f74e648aa40f57dd2496ed7230010f100ede75074bafc160fc1327545a44fd8d34627b136fed6de0bfa72df0e1330116108dc5545f42b9f5b8e302d1546930d9840c18fc92e425bacd0856e842f60f011b7f689aa43b95bbfd02ee20aab2cac2077c9639b9e4f84b8b221b37cdaf13a2010b979e41274c4338395dfc447c0d4a548caf414ee425aab1748475cca9faef4e012051ff90139adccfbf0459e3a5c0638dfb1654ee728cba59c6803ab1daaddda9011ef927e921f956134892499c7513bc2e077986295c050633a1566d449fd0954a00071f56b24c65aaa4549be3c859ac8ec75fe302d89e8755166da3f7d0d08ae285011983fdc2aa2a0611187b0b86119508e3f7642dfe7380f5bdbe6ad27f0dc4ccf201127e3c3923518fec22a4ead80845bbe6008d9b790bd50ec6ef83b087d0490995011982ad7c49288a3115c638a04e14a0c74a75008359b40aff422e9972e276929800237a7bfd1fe105865abbc36e926de565530da393da1ddc0b1005bb0c31504402002569e7e95e54711903c29f63cb8c6b58d47ea3299c455c8738b543d552e8dffe012ea47467abedda0d168d06bcf2f45ab8fa090b90d6e4662d4052df0488084403057542fc7603a7d717dd8267dc036330e3083d2a1562bf5b1ecfbce99804c05100000000000000000000000000000000000000000000000000000000000000000e377bd9c8a2b6faf14c766e51fc9415ed979d604e3d5dcc800b3c5a9c6bec1f239a65bdeb2028926a6fa13fdf2caf0a09ba17e1420b74bef294aa4c9731d2c71f46ae2c57612f525f14cf49e86be5a984aab4d8c25ec3818e19612ba07722d500082c5b600665b3a718fad7516b976749ac3692a6ccf60225c04d1da272a95780012c932f36c1cca1753377ad16b2dbecabb58a2efbf8e88a066d80efba20addf8208d52649fc24ba86ecd87da10efd8ab480c48cadf4ee205e60d10f6e1f1527dd01171915999416a85d08a06e4a8ed18797fcd62910e6624222e120bdd60a00db22fb4d9f0a7aee7570f72b99c673195c67b7dc5e53aa5a32ffc04b409aa79cb52faa6cb8374cd242bd90fd2da9a891fc18022112ecb943b8b0f6c69a4b2e33ad0b16abbd22b38f11f1e76bfd6c1950864753ef84ce0ba869b82f909fbe2751ef0e02b170d107ec7cf6c25d9fdbdd86f5e4790e532254f89ad6cd477bf30816e207ce092de3148c4fd818812abfcadc03a325991fb7dd48b5a54266137709715a11556344ae9bbb2afd1e7ea7fa417f1308d20fdef8dd2168f89101d0643ce62217ee68724101b6e8644ad0860940f95b1674a1befa7ec45235d2c93043087e9001211d72b9f744364770a276f99a183dc184f90ad09d034119f5e4754249046bb201156984d6a418664bda88f98075679d6bb8cc4c644ff727278931797db12a2c65012698726eabb2580faecafa12befe8855a7074da2b4f2d2cd74fd481e818f2287001d445415f6abc3b4a9ff7043906d8f83d5c61ad853b12a72c152a21b9627d8bf00004d5a3deaf642e40e1401ead28babb7ce0263afcc3b3f1e7876a31d6fbea5e90125bff3189cf6ab0e68caa851e60fc92483d3bc2a5ecd511594e7f80f565301c101247b8928fb1866a98b99add332a7e6065cf27ed8a7d48d7775727299308f0ab9002cb7663076e2883fe31b3397f6f5460491835f1a23c173f858fb93cbc312cf360117c5b8e4a25c2e3228b6f4d06525f96e818772292b095b7d344eb3f9f18735bd000889c0fcc3c2f25fe74c915b178db653b0d7725efdc6e2e80a2770738cc3bff9002a578a39364fa16dc82fd7f15872da799f2ddc6422491b9e5c6447ea87adb01a00187461816522889fc2bcb1dc1b0b5d2537df3ae2bb58cbef2b58cb678ba6b9f30121f1cf89f1e9840aace8469b06edf81e9930ddfb8aef4a2c3d46e8551cc40ab400",
|
||||
txhex: "01000003519b9e874a9dd7fcbfb7802268123dcf970c336891101a3a0705855b72b9eb08c80100000000000000000000000000000000000000000000000000000000000000000000f394d06566a81b024fd8624b4c8592f8b9344e58f227261eb08d821bd190b4ac9c7df0660038ddcf881602a14fe5ad8eebff83d3ce3541a1f232fe61acf71109e417160936863e8c0597c798b73ef08e76b8eeebcaf20260ba91fcdef5f626361f824dc6197b02c599d4511c624a933226d8fccc125611ec80d8ad5acea4baf2f1c77e4c5525d9859b40f43daaf3d4e4450203d80419dcb026b9adbd7d6912284ee9ce20f3721076947f16e1faefd9b8c35116f4cf0044ead0a12fda3f3bcf2ed91627b922c02c10d112df9e782d3750cda151f6ba2ebe6495065141f32800894566011198c4055458005a31d3e285037446ba14bac54420bcab87cbebeaccab1f158b011e90132b7310e6c866e348cab68d21bad1d9f811b4d667a1e40bdef1a19259d3000b972ae06da958658b12ca4a45c2c6ea50d305be86f7e979fc962c687eb012f401a2c4bf0beb6c713343b94d65ee6d937660480daf670ab5b0ed421a9103e8a25119c836dd799600e67e4ec66ef680833398a99b16956aed2ee5bd9e6c8e68009a01145636e5e448d6c8ab902db394aa9c5aeee92dd150e1150d5b8f6674f330c279010912df27232ae126525a130ed8a9b4dee62eb57ce1d8a42ae9bcef867b10ce94012f94aa78846fcdda504243f83f3122aee5dc38dd32fa2a90224b3a40b4cf79c301129028709e39591c55e6e477a36cc26dc2efd7183fb59f51635dbd114dfed9d5010d66f31b781fa181c2114600ee1f03d55439355265700fb9d35ac684a19db398001e12558cc17e59fb85fb825481dee6fef3612c043bb38ca4833183da5381364600077a99d4d2df2f06067359f178a40f98a3e0943309c5ce3ce5398b9909174254012aa49cfc78ebec51c00e7824db0d8d41ef43fbb0396824d2c31b7db64124cc61000a8b51bd6c94a502f7aea3e5001f00d76b165ad580e48f8aa72813da8240286e00055f3480aa7cdb68a20d4b1e567d42389b44cf4e345da9e5a5655a80418695bc012c8f1e1ef8af646b4fb08afaeb3febde17c4c0d04b30dfd22d96a32ced9c9439010c40484e1547d307ad56b79bb7450e4eaaed7ed79bd5392dffd7449043f8783a0015104264f8d80356176ca6fdf4777e4ebc8edcbab50f8b6c366ac75ebd6bb5c701078d55053d2d41f21f51d5e617f282ba9dfa2576f0231048dd73998ceaa699fd0105821b61addd6935c7d80e65c706f0f2aa2d73ad22c5548c9c8b92cd82689dc40115f576c413a37014b9ee5377b121c69ad6e0f00f2b4f6c99d1f68b00a5d923370119b29fe6e9cf35f1344e24d52ee1fdb6521de3176f3d2b2a2b9327ae6e6cfbb10017198369a6ff180253700361c40e7f378c8e7ead6e04f01dbf5b4c47e7e1e4ff001abaf84628764eccbc5d0f66dc0a99505bb0e598362c45312470526afad11caf00242c6a902c564b3b363eb4ae08f643f18571779428358bfbdaf9856b0ebf229f0100000000000000000000000000000000000000000000000000000000000000001b7a78b6f573d4ac5d3a8d7d10dafac0dbc92604474417b29d40448d9fbc821d0fe3f73da41b27752d5db0658fcc0bff7da21112417ac719e491fdd43c8e5d0b07bcca22c27367a276e6a4e0cfb1bfbc772842898356ad4723101d4ada5137f409bb3b290d7519da3af15feb09e183e2be809690099551f1cb984deb1295b7611dd0cfd9000482314a388512d278fc22b2f6e3fc6e95023b4bd228e22626b7440119fa2c6f45a33b008ef5b23a5522ebbf0bf5f26d8263613a7f9b5ad9a192357600107a023453f7f79f413756dfba0096ae5d13158bbbb147089a020e819ef6640728acf01c811b967d1a14d767ac20088c5ca1613aa5d94ebe7fe2840b6431d8d002137dae183071d12cd9fb14c4b4738ef750ff514ce26257d1245eaedfea625516bfcb075a33a8910dc4e40742bfaa70d95591711e9db33d0db0be06cc819817072c22a9c81f4369f68bb1ef71c7f7458671d647caef18b30cca91da4f1b201d082a03306b4a07d1f0e1a8cc8844038fc524201a6c3a346a971203fd45458d292987d44049eecdac25c5622465a8d683a43621612d2fb283724b17a7bfa3bad2266a6fb1cf669eef21c90e24e85d6ab48e8b237355e964407dde00d65fb442dd210ce0378349f533c0e8ab65293de5319e246a044ed22b1e6db28add1508dd6f0da22954eea8a431b211a8b54147e32fec6593f9fb731389f62f94d5047e0f3301216576c71a79629bc3879325280e216d59cf7e6ff2d6a0babe418f8f2ed603ce010e16785f8c855043221ed5a603d4ed4e8e90059669543738ceb2a04dc996079c01264e9beaf4bb82e1b8f47bb0c239167064001e306eeef2b5495ec42754806b65012357f738543b13594ff081a070dbc0575645ac5ce8e0f89d3afdeb3d49e9ddae012a17d5095b9cef08f6eee2ce733dbf2cfcdb8c23e39410ed973e68cf8505266d00280c52b45752dd12ba84499618898111a8f9b19f2b76c06917e57eef221f3387011cc3360b478d144118a82a3c4391814f34aac2fb0f2d043394304bed179689e501292ca5631820f5f465242b4cc517f06c153c176a43f77daca463b7e2d3e2d7ba001aa0b2c90c3172cd2556137d78748f47924432449210d8b2f5e27adb6210bc77012c92a12526a5b0b1ef60994eb4d08e9bb5a3ea330d417838a52ad8636d37fa9e00131153c9affeefd6b98ecb6a80f5e6ea97417376030325182c064e12dd353c32011f4e25c49eea09adaa46d56642e2ea0afced2d75036acd3839435863f1ec3bf1002e19fe8456a7236c8fb77a15f87164debaad865e85c3468152a5deaffde3da6a01",
|
||||
},
|
||||
}
|
||||
|
||||
|
1
vendor/github.com/caarlos0/env/v6/.github/FUNDING.yml
generated
vendored
Normal file
1
vendor/github.com/caarlos0/env/v6/.github/FUNDING.yml
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
github: [caarlos0]
|
18
vendor/github.com/caarlos0/env/v6/.github/dependabot.yml
generated
vendored
Normal file
18
vendor/github.com/caarlos0/env/v6/.github/dependabot.yml
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "08:00"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "automerge"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "08:00"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "automerge"
|
61
vendor/github.com/caarlos0/env/v6/.github/workflows/build.yml
generated
vendored
Normal file
61
vendor/github.com/caarlos0/env/v6/.github/workflows/build.yml
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [~1.17]
|
||||
os: [ ubuntu-latest, macos-latest, windows-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
-
|
||||
name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
-
|
||||
name: CI
|
||||
run: make setup ci
|
||||
-
|
||||
name: Upload coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ./coverage.txt
|
||||
-
|
||||
name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
if: success() && startsWith(github.ref, 'refs/tags/') && matrix.os == 'ubuntu-latest'
|
||||
with:
|
||||
version: latest
|
||||
distribution: goreleaser-pro
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||
TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }}
|
||||
TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }}
|
||||
TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
|
||||
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
|
22
vendor/github.com/caarlos0/env/v6/.github/workflows/lint.yml
generated
vendored
Normal file
22
vendor/github.com/caarlos0/env/v6/.github/workflows/lint.yml
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ~1.16
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
skip-go-installation: true
|
4
vendor/github.com/caarlos0/env/v6/.gitignore
generated
vendored
Normal file
4
vendor/github.com/caarlos0/env/v6/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
coverage.txt
|
||||
bin
|
||||
card.png
|
||||
dist
|
8
vendor/github.com/caarlos0/env/v6/.golangci.yml
generated
vendored
Normal file
8
vendor/github.com/caarlos0/env/v6/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
linters:
|
||||
enable:
|
||||
- thelper
|
||||
- gofumpt
|
||||
- tparallel
|
||||
- unconvert
|
||||
- unparam
|
||||
- wastedassign
|
3
vendor/github.com/caarlos0/env/v6/.goreleaser.yml
generated
vendored
Normal file
3
vendor/github.com/caarlos0/env/v6/.goreleaser.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
includes:
|
||||
- from_url:
|
||||
url: https://raw.githubusercontent.com/caarlos0/.goreleaserfiles/main/lib.yml
|
21
vendor/github.com/caarlos0/env/v6/LICENSE.md
generated
vendored
Normal file
21
vendor/github.com/caarlos0/env/v6/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2019 Carlos Alexandro Becker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
37
vendor/github.com/caarlos0/env/v6/Makefile
generated
vendored
Normal file
37
vendor/github.com/caarlos0/env/v6/Makefile
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
SOURCE_FILES?=./...
|
||||
TEST_PATTERN?=.
|
||||
|
||||
export GO111MODULE := on
|
||||
|
||||
setup:
|
||||
go mod tidy
|
||||
.PHONY: setup
|
||||
|
||||
build:
|
||||
go build
|
||||
.PHONY: build
|
||||
|
||||
test:
|
||||
go test -v -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m
|
||||
.PHONY: test
|
||||
|
||||
cover: test
|
||||
go tool cover -html=coverage.txt
|
||||
.PHONY: cover
|
||||
|
||||
fmt:
|
||||
find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
|
||||
.PHONY: fmt
|
||||
|
||||
lint:
|
||||
./bin/golangci-lint run ./...
|
||||
.PHONY: lint
|
||||
|
||||
ci: build test
|
||||
.PHONY: ci
|
||||
|
||||
card:
|
||||
wget -O card.png -c "https://og.caarlos0.dev/**env**: parse envs to structs.png?theme=light&md=1&fontSize=100px&images=https://github.com/caarlos0.png"
|
||||
.PHONY: card
|
||||
|
||||
.DEFAULT_GOAL := ci
|
459
vendor/github.com/caarlos0/env/v6/README.md
generated
vendored
Normal file
459
vendor/github.com/caarlos0/env/v6/README.md
generated
vendored
Normal file
@ -0,0 +1,459 @@
|
||||
# env
|
||||
|
||||
[![Build Status](https://img.shields.io/github/workflow/status/caarlos0/env/build?style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build)
|
||||
[![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env)
|
||||
[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v6)
|
||||
|
||||
Simple lib to parse envs to structs in Go.
|
||||
|
||||
## Example
|
||||
|
||||
Get the module with:
|
||||
|
||||
```sh
|
||||
go get github.com/caarlos0/env/v6
|
||||
```
|
||||
|
||||
The usage looks like this:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Home string `env:"HOME"`
|
||||
Port int `env:"PORT" envDefault:"3000"`
|
||||
Password string `env:"PASSWORD,unset"`
|
||||
IsProduction bool `env:"PRODUCTION"`
|
||||
Hosts []string `env:"HOSTS" envSeparator:":"`
|
||||
Duration time.Duration `env:"DURATION"`
|
||||
TempFolder string `env:"TEMP_FOLDER" envDefault:"${HOME}/tmp" envExpand:"true"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", cfg)
|
||||
}
|
||||
```
|
||||
|
||||
You can run it like this:
|
||||
|
||||
```sh
|
||||
$ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run main.go
|
||||
{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s}
|
||||
```
|
||||
|
||||
⚠️⚠️⚠️ **Attention:** _unexported fields_ will be **ignored**.
|
||||
|
||||
## Supported types and defaults
|
||||
|
||||
Out of the box all built-in types are supported, plus a few others that
|
||||
are commonly used.
|
||||
|
||||
Complete list:
|
||||
|
||||
- `string`
|
||||
- `bool`
|
||||
- `int`
|
||||
- `int8`
|
||||
- `int16`
|
||||
- `int32`
|
||||
- `int64`
|
||||
- `uint`
|
||||
- `uint8`
|
||||
- `uint16`
|
||||
- `uint32`
|
||||
- `uint64`
|
||||
- `float32`
|
||||
- `float64`
|
||||
- `string`
|
||||
- `time.Duration`
|
||||
- `encoding.TextUnmarshaler`
|
||||
- `url.URL`
|
||||
|
||||
Pointers, slices and slices of pointers of those types are also supported.
|
||||
|
||||
You can also use/define a [custom parser func](#custom-parser-funcs) for any
|
||||
other type you want.
|
||||
|
||||
If you set the `envDefault` tag for something, this value will be used in the
|
||||
case of absence of it in the environment.
|
||||
|
||||
By default, slice types will split the environment value on `,`; you can change
|
||||
this behavior by setting the `envSeparator` tag.
|
||||
|
||||
If you set the `envExpand` tag, environment variables (either in `${var}` or
|
||||
`$var` format) in the string will be replaced according with the actual value
|
||||
of the variable.
|
||||
|
||||
## Custom Parser Funcs
|
||||
|
||||
If you have a type that is not supported out of the box by the lib, you are able
|
||||
to use (or define) and pass custom parsers (and their associated `reflect.Type`)
|
||||
to the `env.ParseWithFuncs()` function.
|
||||
|
||||
In addition to accepting a struct pointer (same as `Parse()`), this function
|
||||
also accepts a `map[reflect.Type]env.ParserFunc`.
|
||||
|
||||
`env` also ships with some pre-built custom parser funcs for common types. You
|
||||
can check them out [here](parsers/).
|
||||
|
||||
If you add a custom parser for, say `Foo`, it will also be used to parse
|
||||
`*Foo` and `[]Foo` types.
|
||||
|
||||
This directory contains pre-built, custom parsers that can be used with `env.ParseWithFuncs`
|
||||
to facilitate the parsing of envs that are not basic types.
|
||||
|
||||
Check the example in the [go doc](http://godoc.org/github.com/caarlos0/env)
|
||||
for more info.
|
||||
|
||||
### A note about `TextUnmarshaler` and `time.Time`
|
||||
|
||||
Env supports by default anything that implements the `TextUnmarshaler` interface.
|
||||
That includes things like `time.Time` for example.
|
||||
The upside is that depending on the format you need, you don't need to change anything.
|
||||
The downside is that if you do need time in another format, you'll need to create your own type.
|
||||
|
||||
Its fairly straightforward:
|
||||
|
||||
```go
|
||||
type MyTime time.Time
|
||||
|
||||
func (t *MyTime) UnmarshalText(text []byte) error {
|
||||
tt, err := time.Parse("2006-01-02", string(text))
|
||||
*t = MyTime(tt)
|
||||
return err
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
SomeTime MyTime `env:"SOME_TIME"`
|
||||
}
|
||||
```
|
||||
|
||||
And then you can parse `Config` with `env.Parse`.
|
||||
|
||||
## Required fields
|
||||
|
||||
The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added to ensure that some environment variable is set.
|
||||
In the example above, an error is returned if the `config` struct is changed to:
|
||||
|
||||
```go
|
||||
type config struct {
|
||||
SecretKey string `env:"SECRET_KEY,required"`
|
||||
}
|
||||
```
|
||||
|
||||
## Not Empty fields
|
||||
|
||||
While `required` demands the environment variable to be check, it doesn't check its value.
|
||||
If you want to make sure the environment is set and not empty, you need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`).
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
type config struct {
|
||||
SecretKey string `env:"SECRET_KEY,notEmpty"`
|
||||
}
|
||||
```
|
||||
|
||||
## Unset environment variable after reading it
|
||||
|
||||
The `env` tag option `unset` (e.g., `env:"tagKey,unset"`) can be added
|
||||
to ensure that some environment variable is unset after reading it.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
type config struct {
|
||||
SecretKey string `env:"SECRET_KEY,unset"`
|
||||
}
|
||||
```
|
||||
|
||||
## From file
|
||||
|
||||
The `env` tag option `file` (e.g., `env:"tagKey,file"`) can be added
|
||||
to in order to indicate that the value of the variable shall be loaded from a file. The path of that file is given
|
||||
by the environment variable associated with it
|
||||
Example below
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Secret string `env:"SECRET,file"`
|
||||
Password string `env:"PASSWORD,file" envDefault:"/tmp/password"`
|
||||
Certificate string `env:"CERTIFICATE,file" envDefault:"${CERTIFICATE_FILE}" envExpand:"true"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := config{}
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", cfg)
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
$ echo qwerty > /tmp/secret
|
||||
$ echo dvorak > /tmp/password
|
||||
$ echo coleman > /tmp/certificate
|
||||
|
||||
$ SECRET=/tmp/secret \
|
||||
CERTIFICATE_FILE=/tmp/certificate \
|
||||
go run main.go
|
||||
{Secret:qwerty Password:dvorak Certificate:coleman}
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### Environment
|
||||
|
||||
By setting the `Options.Environment` map you can tell `Parse` to add those `keys` and `values`
|
||||
as env vars before parsing is done. These envs are stored in the map and never actually set by `os.Setenv`.
|
||||
This option effectively makes `env` ignore the OS environment variables: only the ones provided in the option are used.
|
||||
|
||||
This can make your testing scenarios a bit more clean and easy to handle.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Password string `env:"PASSWORD"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{Environment: map[string]string{
|
||||
"PASSWORD": "MY_PASSWORD",
|
||||
}}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print the loaded data.
|
||||
fmt.Printf("%+v\n", cfg.envData)
|
||||
}
|
||||
```
|
||||
|
||||
### Changing default tag name
|
||||
|
||||
You can change what tag name to use for setting the env vars by setting the `Options.TagName`
|
||||
variable.
|
||||
|
||||
For example
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Password string `json:"PASSWORD"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{TagName: "json"}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print the loaded data.
|
||||
fmt.Printf("%+v\n", cfg.envData)
|
||||
}
|
||||
```
|
||||
|
||||
### Prefixes
|
||||
|
||||
You can prefix sub-structs env tags, as well as a whole `env.Parse` call.
|
||||
|
||||
Here's an example flexing it a bit:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Home string `env:"HOME"`
|
||||
}
|
||||
|
||||
type ComplexConfig struct {
|
||||
Foo Config `envPrefix:"FOO_"`
|
||||
Clean Config
|
||||
Bar Config `envPrefix:"BAR_"`
|
||||
Blah string `env:"BLAH"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := ComplexConfig{}
|
||||
if err := Parse(&cfg, Options{
|
||||
Prefix: "T_",
|
||||
Environment: map[string]string{
|
||||
"T_FOO_HOME": "/foo",
|
||||
"T_BAR_HOME": "/bar",
|
||||
"T_BLAH": "blahhh",
|
||||
"T_HOME": "/clean",
|
||||
},
|
||||
}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print the loaded data.
|
||||
fmt.Printf("%+v\n", cfg.envData)
|
||||
}
|
||||
```
|
||||
|
||||
### On set hooks
|
||||
|
||||
You might want to listen to value sets and, for example, log something or do some other kind of logic.
|
||||
You can do this by passing a `OnSet` option:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Username string `env:"USERNAME" envDefault:"admin"`
|
||||
Password string `env:"PASSWORD"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{
|
||||
OnSet: func(tag string, value interface{}, isDefault bool) {
|
||||
fmt.Printf("Set %s to %v (default? %v)\n", tag, value, isDefault)
|
||||
},
|
||||
}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print the loaded data.
|
||||
fmt.Printf("%+v\n", cfg.envData)
|
||||
}
|
||||
```
|
||||
|
||||
## Making all fields to required
|
||||
|
||||
You can make all fields that don't have a default value be required by setting the `RequiredIfNoDef: true` in the `Options`.
|
||||
|
||||
For example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Username string `env:"USERNAME" envDefault:"admin"`
|
||||
Password string `env:"PASSWORD"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{RequiredIfNoDef: true}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print the loaded data.
|
||||
fmt.Printf("%+v\n", cfg.envData)
|
||||
}
|
||||
```
|
||||
|
||||
## Defaults from code
|
||||
|
||||
You may define default value also in code, by initialising the config data before it's filled by `env.Parse`.
|
||||
Default values defined as struct tags will overwrite existing values during Parse.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v6"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Username string `env:"USERNAME" envDefault:"admin"`
|
||||
Password string `env:"PASSWORD"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
var cfg = Config{
|
||||
Username: "test",
|
||||
Password: "123456",
|
||||
}
|
||||
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
fmt.Println("failed:", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%+v", cfg) // {Username:admin Password:123456}
|
||||
}
|
||||
```
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[![Stargazers over time](https://starchart.cc/caarlos0/env.svg)](https://starchart.cc/caarlos0/env)
|
477
vendor/github.com/caarlos0/env/v6/env.go
generated
vendored
Normal file
477
vendor/github.com/caarlos0/env/v6/env.go
generated
vendored
Normal file
@ -0,0 +1,477 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// nolint: gochecknoglobals
|
||||
var (
|
||||
// ErrNotAStructPtr is returned if you pass something that is not a pointer to a
|
||||
// Struct to Parse.
|
||||
ErrNotAStructPtr = errors.New("env: expected a pointer to a Struct")
|
||||
|
||||
defaultBuiltInParsers = map[reflect.Kind]ParserFunc{
|
||||
reflect.Bool: func(v string) (interface{}, error) {
|
||||
return strconv.ParseBool(v)
|
||||
},
|
||||
reflect.String: func(v string) (interface{}, error) {
|
||||
return v, nil
|
||||
},
|
||||
reflect.Int: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseInt(v, 10, 32)
|
||||
return int(i), err
|
||||
},
|
||||
reflect.Int16: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseInt(v, 10, 16)
|
||||
return int16(i), err
|
||||
},
|
||||
reflect.Int32: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseInt(v, 10, 32)
|
||||
return int32(i), err
|
||||
},
|
||||
reflect.Int64: func(v string) (interface{}, error) {
|
||||
return strconv.ParseInt(v, 10, 64)
|
||||
},
|
||||
reflect.Int8: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseInt(v, 10, 8)
|
||||
return int8(i), err
|
||||
},
|
||||
reflect.Uint: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseUint(v, 10, 32)
|
||||
return uint(i), err
|
||||
},
|
||||
reflect.Uint16: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseUint(v, 10, 16)
|
||||
return uint16(i), err
|
||||
},
|
||||
reflect.Uint32: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseUint(v, 10, 32)
|
||||
return uint32(i), err
|
||||
},
|
||||
reflect.Uint64: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseUint(v, 10, 64)
|
||||
return i, err
|
||||
},
|
||||
reflect.Uint8: func(v string) (interface{}, error) {
|
||||
i, err := strconv.ParseUint(v, 10, 8)
|
||||
return uint8(i), err
|
||||
},
|
||||
reflect.Float64: func(v string) (interface{}, error) {
|
||||
return strconv.ParseFloat(v, 64)
|
||||
},
|
||||
reflect.Float32: func(v string) (interface{}, error) {
|
||||
f, err := strconv.ParseFloat(v, 32)
|
||||
return float32(f), err
|
||||
},
|
||||
}
|
||||
|
||||
defaultTypeParsers = map[reflect.Type]ParserFunc{
|
||||
reflect.TypeOf(url.URL{}): func(v string) (interface{}, error) {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse URL: %v", err)
|
||||
}
|
||||
return *u, nil
|
||||
},
|
||||
reflect.TypeOf(time.Nanosecond): func(v string) (interface{}, error) {
|
||||
s, err := time.ParseDuration(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse duration: %v", err)
|
||||
}
|
||||
return s, err
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// ParserFunc defines the signature of a function that can be used within `CustomParsers`.
|
||||
type ParserFunc func(v string) (interface{}, error)
|
||||
|
||||
// OnSetFn is a hook that can be run when a value is set.
|
||||
type OnSetFn func(tag string, value interface{}, isDefault bool)
|
||||
|
||||
// Options for the parser.
|
||||
type Options struct {
|
||||
// Environment keys and values that will be accessible for the service.
|
||||
Environment map[string]string
|
||||
|
||||
// TagName specifies another tagname to use rather than the default env.
|
||||
TagName string
|
||||
|
||||
// RequiredIfNoDef automatically sets all env as required if they do not declare 'envDefault'
|
||||
RequiredIfNoDef bool
|
||||
|
||||
// OnSet allows to run a function when a value is set
|
||||
OnSet OnSetFn
|
||||
|
||||
// Prefix define a prefix for each key
|
||||
Prefix string
|
||||
|
||||
// Sets to true if we have already configured once.
|
||||
configured bool
|
||||
}
|
||||
|
||||
// configure will do the basic configurations and defaults.
|
||||
func configure(opts []Options) []Options {
|
||||
// If we have already configured the first item
|
||||
// of options will have been configured set to true.
|
||||
if len(opts) > 0 && opts[0].configured {
|
||||
return opts
|
||||
}
|
||||
|
||||
// Created options with defaults.
|
||||
opt := Options{
|
||||
TagName: "env",
|
||||
Environment: toMap(os.Environ()),
|
||||
configured: true,
|
||||
}
|
||||
|
||||
// Loop over all opts structs and set
|
||||
// to opt if value is not default/empty.
|
||||
for _, item := range opts {
|
||||
if item.Environment != nil {
|
||||
opt.Environment = item.Environment
|
||||
}
|
||||
if item.TagName != "" {
|
||||
opt.TagName = item.TagName
|
||||
}
|
||||
if item.OnSet != nil {
|
||||
opt.OnSet = item.OnSet
|
||||
}
|
||||
if item.Prefix != "" {
|
||||
opt.Prefix = item.Prefix
|
||||
}
|
||||
opt.RequiredIfNoDef = item.RequiredIfNoDef
|
||||
}
|
||||
|
||||
return []Options{opt}
|
||||
}
|
||||
|
||||
func getOnSetFn(opts []Options) OnSetFn {
|
||||
return opts[0].OnSet
|
||||
}
|
||||
|
||||
// getTagName returns the tag name.
|
||||
func getTagName(opts []Options) string {
|
||||
return opts[0].TagName
|
||||
}
|
||||
|
||||
// getEnvironment returns the environment map.
|
||||
func getEnvironment(opts []Options) map[string]string {
|
||||
return opts[0].Environment
|
||||
}
|
||||
|
||||
// Parse parses a struct containing `env` tags and loads its values from
|
||||
// environment variables.
|
||||
func Parse(v interface{}, opts ...Options) error {
|
||||
return ParseWithFuncs(v, map[reflect.Type]ParserFunc{}, opts...)
|
||||
}
|
||||
|
||||
// ParseWithFuncs is the same as `Parse` except it also allows the user to pass
|
||||
// in custom parsers.
|
||||
func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ...Options) error {
|
||||
opts = configure(opts)
|
||||
|
||||
ptrRef := reflect.ValueOf(v)
|
||||
if ptrRef.Kind() != reflect.Ptr {
|
||||
return ErrNotAStructPtr
|
||||
}
|
||||
ref := ptrRef.Elem()
|
||||
if ref.Kind() != reflect.Struct {
|
||||
return ErrNotAStructPtr
|
||||
}
|
||||
parsers := defaultTypeParsers
|
||||
for k, v := range funcMap {
|
||||
parsers[k] = v
|
||||
}
|
||||
|
||||
return doParse(ref, parsers, opts)
|
||||
}
|
||||
|
||||
func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Options) error {
|
||||
refType := ref.Type()
|
||||
|
||||
for i := 0; i < refType.NumField(); i++ {
|
||||
refField := ref.Field(i)
|
||||
if !refField.CanSet() {
|
||||
continue
|
||||
}
|
||||
if reflect.Ptr == refField.Kind() && !refField.IsNil() {
|
||||
err := ParseWithFuncs(refField.Interface(), funcMap, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if reflect.Struct == refField.Kind() && refField.CanAddr() && refField.Type().Name() == "" {
|
||||
err := Parse(refField.Addr().Interface(), opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
refTypeField := refType.Field(i)
|
||||
value, err := get(refTypeField, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value == "" {
|
||||
if reflect.Struct == refField.Kind() {
|
||||
subOpts := make([]Options, len(opts))
|
||||
copy(subOpts, opts)
|
||||
if prefix := refType.Field(i).Tag.Get("envPrefix"); prefix != "" {
|
||||
subOpts[0].Prefix += prefix
|
||||
}
|
||||
if err := doParse(refField, funcMap, subOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := set(refField, refTypeField, value, funcMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func get(field reflect.StructField, opts []Options) (val string, err error) {
|
||||
var exists bool
|
||||
var isDefault bool
|
||||
var loadFile bool
|
||||
var unset bool
|
||||
var notEmpty bool
|
||||
|
||||
required := opts[0].RequiredIfNoDef
|
||||
prefix := opts[0].Prefix
|
||||
key, tags := parseKeyForOption(field.Tag.Get(getTagName(opts)))
|
||||
key = prefix + key
|
||||
for _, tag := range tags {
|
||||
switch tag {
|
||||
case "":
|
||||
continue
|
||||
case "file":
|
||||
loadFile = true
|
||||
case "required":
|
||||
required = true
|
||||
case "unset":
|
||||
unset = true
|
||||
case "notEmpty":
|
||||
notEmpty = true
|
||||
default:
|
||||
return "", fmt.Errorf("env: tag option %q not supported", tag)
|
||||
}
|
||||
}
|
||||
expand := strings.EqualFold(field.Tag.Get("envExpand"), "true")
|
||||
defaultValue, defExists := field.Tag.Lookup("envDefault")
|
||||
val, exists, isDefault = getOr(key, defaultValue, defExists, getEnvironment(opts))
|
||||
|
||||
if expand {
|
||||
val = os.ExpandEnv(val)
|
||||
}
|
||||
|
||||
if unset {
|
||||
defer os.Unsetenv(key)
|
||||
}
|
||||
|
||||
if required && !exists && len(key) > 0 {
|
||||
return "", fmt.Errorf(`env: required environment variable %q is not set`, key)
|
||||
}
|
||||
|
||||
if notEmpty && val == "" {
|
||||
return "", fmt.Errorf("env: environment variable %q should not be empty", key)
|
||||
}
|
||||
|
||||
if loadFile && val != "" {
|
||||
filename := val
|
||||
val, err = getFromFile(filename)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(`env: could not load content of file "%s" from variable %s: %v`, filename, key, err)
|
||||
}
|
||||
}
|
||||
|
||||
if onSetFn := getOnSetFn(opts); onSetFn != nil {
|
||||
onSetFn(key, val, isDefault)
|
||||
}
|
||||
return val, err
|
||||
}
|
||||
|
||||
// split the env tag's key into the expected key and desired option, if any.
|
||||
func parseKeyForOption(key string) (string, []string) {
|
||||
opts := strings.Split(key, ",")
|
||||
return opts[0], opts[1:]
|
||||
}
|
||||
|
||||
func getFromFile(filename string) (value string, err error) {
|
||||
b, err := os.ReadFile(filename)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func getOr(key, defaultValue string, defExists bool, envs map[string]string) (string, bool, bool) {
|
||||
value, exists := envs[key]
|
||||
switch {
|
||||
case (!exists || key == "") && defExists:
|
||||
return defaultValue, true, true
|
||||
case !exists:
|
||||
return "", false, false
|
||||
}
|
||||
|
||||
return value, true, false
|
||||
}
|
||||
|
||||
func set(field reflect.Value, sf reflect.StructField, value string, funcMap map[reflect.Type]ParserFunc) error {
|
||||
if tm := asTextUnmarshaler(field); tm != nil {
|
||||
if err := tm.UnmarshalText([]byte(value)); err != nil {
|
||||
return newParseError(sf, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
typee := sf.Type
|
||||
fieldee := field
|
||||
if typee.Kind() == reflect.Ptr {
|
||||
typee = typee.Elem()
|
||||
fieldee = field.Elem()
|
||||
}
|
||||
|
||||
parserFunc, ok := funcMap[typee]
|
||||
if ok {
|
||||
val, err := parserFunc(value)
|
||||
if err != nil {
|
||||
return newParseError(sf, err)
|
||||
}
|
||||
|
||||
fieldee.Set(reflect.ValueOf(val))
|
||||
return nil
|
||||
}
|
||||
|
||||
parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
|
||||
if ok {
|
||||
val, err := parserFunc(value)
|
||||
if err != nil {
|
||||
return newParseError(sf, err)
|
||||
}
|
||||
|
||||
fieldee.Set(reflect.ValueOf(val).Convert(typee))
|
||||
return nil
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Slice {
|
||||
return handleSlice(field, value, sf, funcMap)
|
||||
}
|
||||
|
||||
return newNoParserError(sf)
|
||||
}
|
||||
|
||||
func handleSlice(field reflect.Value, value string, sf reflect.StructField, funcMap map[reflect.Type]ParserFunc) error {
|
||||
separator := sf.Tag.Get("envSeparator")
|
||||
if separator == "" {
|
||||
separator = ","
|
||||
}
|
||||
parts := strings.Split(value, separator)
|
||||
|
||||
typee := sf.Type.Elem()
|
||||
if typee.Kind() == reflect.Ptr {
|
||||
typee = typee.Elem()
|
||||
}
|
||||
|
||||
if _, ok := reflect.New(typee).Interface().(encoding.TextUnmarshaler); ok {
|
||||
return parseTextUnmarshalers(field, parts, sf)
|
||||
}
|
||||
|
||||
parserFunc, ok := funcMap[typee]
|
||||
if !ok {
|
||||
parserFunc, ok = defaultBuiltInParsers[typee.Kind()]
|
||||
if !ok {
|
||||
return newNoParserError(sf)
|
||||
}
|
||||
}
|
||||
|
||||
result := reflect.MakeSlice(sf.Type, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
r, err := parserFunc(part)
|
||||
if err != nil {
|
||||
return newParseError(sf, err)
|
||||
}
|
||||
v := reflect.ValueOf(r).Convert(typee)
|
||||
if sf.Type.Elem().Kind() == reflect.Ptr {
|
||||
v = reflect.New(typee)
|
||||
v.Elem().Set(reflect.ValueOf(r).Convert(typee))
|
||||
}
|
||||
result = reflect.Append(result, v)
|
||||
}
|
||||
field.Set(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func asTextUnmarshaler(field reflect.Value) encoding.TextUnmarshaler {
|
||||
if reflect.Ptr == field.Kind() {
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
}
|
||||
} else if field.CanAddr() {
|
||||
field = field.Addr()
|
||||
}
|
||||
|
||||
tm, ok := field.Interface().(encoding.TextUnmarshaler)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return tm
|
||||
}
|
||||
|
||||
func parseTextUnmarshalers(field reflect.Value, data []string, sf reflect.StructField) error {
|
||||
s := len(data)
|
||||
elemType := field.Type().Elem()
|
||||
slice := reflect.MakeSlice(reflect.SliceOf(elemType), s, s)
|
||||
for i, v := range data {
|
||||
sv := slice.Index(i)
|
||||
kind := sv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
sv = reflect.New(elemType.Elem())
|
||||
} else {
|
||||
sv = sv.Addr()
|
||||
}
|
||||
tm := sv.Interface().(encoding.TextUnmarshaler)
|
||||
if err := tm.UnmarshalText([]byte(v)); err != nil {
|
||||
return newParseError(sf, err)
|
||||
}
|
||||
if kind == reflect.Ptr {
|
||||
slice.Index(i).Set(sv)
|
||||
}
|
||||
}
|
||||
|
||||
field.Set(slice)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newParseError(sf reflect.StructField, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return parseError{
|
||||
sf: sf,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
type parseError struct {
|
||||
sf reflect.StructField
|
||||
err error
|
||||
}
|
||||
|
||||
func (e parseError) Error() string {
|
||||
return fmt.Sprintf(`env: parse error on field "%s" of type "%s": %v`, e.sf.Name, e.sf.Type, e.err)
|
||||
}
|
||||
|
||||
func newNoParserError(sf reflect.StructField) error {
|
||||
return fmt.Errorf(`env: no parser found for field "%s" of type "%s"`, sf.Name, sf.Type)
|
||||
}
|
1477
vendor/github.com/caarlos0/env/v6/env_test.go
generated
vendored
Normal file
1477
vendor/github.com/caarlos0/env/v6/env_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
vendor/github.com/caarlos0/env/v6/env_unix.go
generated
vendored
Normal file
15
vendor/github.com/caarlos0/env/v6/env_unix.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package env
|
||||
|
||||
import "strings"
|
||||
|
||||
func toMap(env []string) map[string]string {
|
||||
r := map[string]string{}
|
||||
for _, e := range env {
|
||||
p := strings.SplitN(e, "=", 2)
|
||||
r[p[0]] = p[1]
|
||||
}
|
||||
return r
|
||||
}
|
25
vendor/github.com/caarlos0/env/v6/env_windows.go
generated
vendored
Normal file
25
vendor/github.com/caarlos0/env/v6/env_windows.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package env
|
||||
|
||||
import "strings"
|
||||
|
||||
func toMap(env []string) map[string]string {
|
||||
r := map[string]string{}
|
||||
for _, e := range env {
|
||||
p := strings.SplitN(e, "=", 2)
|
||||
|
||||
// On Windows, environment variables can start with '='. If so, Split at next character.
|
||||
// See env_windows.go in the Go source: https://github.com/golang/go/blob/master/src/syscall/env_windows.go#L58
|
||||
prefixEqualSign := false
|
||||
if len(e) > 0 && e[0] == '=' {
|
||||
e = e[1:]
|
||||
prefixEqualSign = true
|
||||
}
|
||||
p = strings.SplitN(e, "=", 2)
|
||||
if prefixEqualSign {
|
||||
p[0] = "=" + p[0]
|
||||
}
|
||||
|
||||
r[p[0]] = p[1]
|
||||
}
|
||||
return r
|
||||
}
|
20
vendor/github.com/caarlos0/env/v6/env_windows_test.go
generated
vendored
Normal file
20
vendor/github.com/caarlos0/env/v6/env_windows_test.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
// On Windows, environment variables can start with '='. This test verifies this behavior without relying on a Windows environment.
|
||||
// See env_windows.go in the Go source: https://github.com/golang/go/blob/master/src/syscall/env_windows.go#L58
|
||||
func TestToMapWindows(t *testing.T) {
|
||||
is := is.New(t)
|
||||
envVars := []string{"=::=::\\", "=C:=C:\\test", "VAR=REGULARVAR"}
|
||||
result := toMap(envVars)
|
||||
is.Equal(map[string]string{
|
||||
"=::": "::\\",
|
||||
"=C:": "C:\\test",
|
||||
"VAR": "REGULARVAR",
|
||||
}, result)
|
||||
}
|
5
vendor/github.com/caarlos0/env/v6/go.mod
generated
vendored
Normal file
5
vendor/github.com/caarlos0/env/v6/go.mod
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
module github.com/caarlos0/env/v6
|
||||
|
||||
require github.com/matryer/is v1.4.0
|
||||
|
||||
go 1.17
|
2
vendor/github.com/caarlos0/env/v6/go.sum
generated
vendored
Normal file
2
vendor/github.com/caarlos0/env/v6/go.sum
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
Loading…
Reference in New Issue
Block a user