LINUX.ORG.RU

Какой выдумать или создать формат для конфигов?

 , , ,


1

3

Хочу для своего упражнения использовать yaml. Вроде голанг его умеет читать и писать. Что говорит на эту тему духовенство? Уместен ли такой формат или же лучше взять что-то другое?

json не хочу по той причине, что в него нельзя вставлять комментарии, xml слишком многословен.

==========================================

Решение: взят обычный json, который читает - пишет структуру. Для комментария в структуре предусмотрено специальное поле Comment. Это не так удобно, зато гомоиконно.

Если в конфиге задано поле, отсутствующее в структуре, например, если название поля написано с опечаткой, то будет ошибка чтения конфига. Это важно, поскольку толерантность многих программ к опечаткам в конфигах приводит к тому, что можно часами сидеть и искать ответ на вопрос «ну почему же это дерьмо не работает»?

Код такой:

package main

import (
	"strings"; "fmt"; "os"
	"encoding/json"
	"io/ioutil"
	// "github.com/flynn/json5"
)

type SecretConfigDataStruct struct {
	Comment       string
	RecieverEMail string
	SMTPServer    string
	SMTPUser      string
	SMTPPassword  string
	SenderEMail   string }

var SecretConfigData SecretConfigDataStruct

func (sds *SecretConfigDataStruct) SaveToFile(filename string) (err error) {
	var text []byte
	text, err = json.MarshalIndent(sds,""," ")
	if err != nil { return	}	
	err = ioutil.WriteFile(filename, text, 0600)
	return }

const ConfigFileName = "secret-data.config.json"

// for development
func saveSecretConfigDataExample() {
	sds := SecretConfigDataStruct{
		Comment:       "Example config file. Copy this one to the secret-data.config.json and edit",
		SenderEMail:   "den@example.net",
		RecieverEMail: "world@example.net",
		SMTPServer:    "smtp.example.net",
		SMTPUser:      "Кирилл",
		SMTPPassword:  "bla-bla-bla"}
	err := sds.SaveToFile(ConfigFileName + ".example")
	if err != nil {	panic(err)	}}

func loadSecretConfigData() (err error) {
	sds := &SecretConfigData
	fn := ConfigFileName
	if _, err = os.Stat(fn); os.IsNotExist(err) {
		fmt.Printf("No config file %s found. Create one by copying from %s.example\n",
			fn, fn)
		return	}
	var bytes []byte
	bytes, err = ioutil.ReadFile(fn)
	if err != nil {
		fmt.Printf("Unable to read config %s\n", fn)
		return	}
		dec := json.NewDecoder(strings.NewReader(string(bytes)))
		dec.DisallowUnknownFields() 
		err = dec.Decode(sds)
	if err != nil {
		fmt.Printf("Error reading config file %s: %#v\n", fn, err)
		return	}
	fmt.Printf("playWithSecretConfigData returned %#v\n", sds)
	return }

★★★★★

Ответ на: комментарий от den73

Почему?

Ты думаешь, это совпадение, что языки, в которых можно без проблем добавить/удалить statement-строку (C-подобные, Python, …) — используются; а лиспы, паскаль и прочее

function() {
    statement}

— нет?

Cycle_A ()
Ответ на: комментарий от Cycle_A

Вообще, мой формат выбора для таких случаев - такой:

a = [1
    ,2
    ,3]
Запятая стоит в начале следующей строки. Те, кто работает с SQL и пишет эти вот ужасные портяночные процедуры со 100 параметрами, обычно приходят именно к такому варианту, совершенно не сговариваясь.

den73 ★★★★★ ()
Ответ на: комментарий от anonymous

Да не буду уже ямл брать. Либо json и в структуру вставлю поле «комментарий», либо json5. Комментарии в конфиге плохи тем, что конфиг перестаёт быть гомоиконным. Поэтому лучше встроить их прямо в структуру данных.

den73 ★★★★★ ()
Ответ на: комментарий от den73

удобство

a = [1
    ,2
    ,3]

Очень удобно, ага.

надёжность

Ты, кажется, проигнорировал вопрос; спрошу снова: почему этот ужас надёжнее?

быстроты записи

Ну давай посчитаем.

myvar = [1
<Space><Space><Space><Space><Space><Space><Space><Space>,2
<Space><Space><Space><Space><Space><Space><Space><Space>,3]
vs
myvar = [
<Tab>1,
<Tab>2,
<Tab>3,
]

Cycle_A ()
Ответ на: комментарий от den73

Зачем ты приплетаешь сюда Tab vs Space?

Эм… потому что, если я хочу выравнивания, то в твоём случае я обязан нажимать <Space>, а в моём — независимо от того, вставляет ли мой редактор пробелы или табы по нажатию <Tab>, я могу нажать <Tab>?

Вопрос некорректен, это не ужас. Если хочешь ответ - спроси нормально.

Уже спросил, см. моё первое сообщение в треде.

Cycle_A ()
Ответ на: комментарий от Cycle_A

Эм… потому что, если я хочу выравнивания, то в твоём случае я обязан нажимать <Space>

Это можно вынести за скобки. На самом деле, если эта запятая в конце обязательна, то нет проблемы с надёжностью. А вот если запятая опциональна, то забытый элемент списка неотличим от проставленной опциональной запятой. Дальше сам, пожалуйста, подумай.

den73 ★★★★★ ()
Ответ на: комментарий от den73

Это можно вынести за скобки.

Нет, нельзя.

На самом деле, если эта запятая в конце обязательна, то нет проблемы с надёжностью. А вот если запятая опциональна, то забытый элемент списка неотличим от проставленной опциональной запятой. Дальше сам, пожалуйста, подумай.

Думаю, что на практике это является гораздо меньшей проблемой, чем те проблемы, на которые указал я. Обязательная запятая неудобна при inline-записи, т.е. enum { A, B, C };

Cycle_A ()
Ответ на: комментарий от Nervous

У меня там голанг. Всем спасибо, я пока взял простой json. json5 не возьму пока что, т.к. гомоиконный конфиг с ограниченными комментариями мне больше нравится, чем кудрявый, но не гомоиконный. Но тема была познавательной.

den73 ★★★★★ ()
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от Cycle_A

Насколько я вижу, в голанге при инлайн-записи запятой не должно быть, а при переносе закрывающей на следующую скобку запятая должна быть. Это лучше, чем опциональная запятая, но я бы предпочёл, чтобы запятая в конце списка была всегда запрещена. Например, если списки вложены, то тут опять же можно перепутать и закрыть не тот уровень вложенности.

den73 ★★★★★ ()
Ответ на: комментарий от den73

Короче, вот результат:

https://github.com/budden/a/commit/42b4bfa96b643a0972bd7a13b29d9e121c36ba9b

Гора, можно сказать, родила мышь. Засим ставлю звёздочку. Надо бы, конечно, составить индекс из того, что предлагали, но лень. Всем спасибо за участие!

den73 ★★★★★ ()
Ответ на: комментарий от den73

Ну а элементы одного уровня-то что мешает форматировать одним отступом?

 func openDb(url string) (db *sqlx.DB, err error, closer func()) {
		db, err = sqlx.Open("postgres", url)
		closer = func() {
			err := db.Close()
			if err != nil {
				fmt.Printf("Error closing db: %v\n",err)
				// Not exiting because this function is called from the defer
			}}
		return }

	func genExpiryDate(db *sqlx.DB) {
		res1, err2 := db.Query(`select current_timestamp + interval '1' day`)
		if err2 != nil {
			fmt.Printf("Wow!"); os.Exit(1)	}
		if !res1.Next() {
			fmt.Printf("No rows here. Why?"); os.Exit(1) }
		var magic time.Time
		res1.Scan(&magic)
		fmt.Printf("Expiry at %s\n", magic.Format("2006-01-02 15:04 -0700"))	}
Cycle_A ()
Ответ на: комментарий от Cycle_A

Спасибо за ревю (как это будет по-русски?) Исправил. Проблема в том, что я не могу применить автоформатирование из-за того, что фигурную скобку ставлю не по писанному. Но нашлась команда «Convert Indentation to tabs»

den73 ★★★★★ ()
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от den73

И вот кстати, у меня в этом файле 68 строк стало после того, как я все закрывающие поставил в конце строки. А если применить Format Document, то строк становится 88. Итого, за счёт лиспизации - питонизации количество строк удалось сократить на 22%.

den73 ★★★★★ ()
Ответ на: комментарий от Cycle_A

А если код так писать:

func genExpiryDate(db *sqlx.DB) {


	res1, err2 := db.Query(`select current_timestamp + interval '1' day`)


	if err2 != nil {


		fmt.Printf("Wow!")


		os.Exit(1)


	}


	if !res1.Next() {


		fmt.Printf("No rows here. Why?")


		os.Exit(1)


	}


	var magic time.Time


	res1.Scan(&magic)


	fmt.Printf("Expiry at %s\n", magic.Format("2006-01-02 
15:04 -0700"))


}
то ведь читаемость совсем не страдает, правда? Например, когда функция перестаёт помещаться целиком на экране?

den73 ★★★★★ ()
Ответ на: комментарий от den73

то ведь читаемость совсем не страдает, правда?

Нет. Или гораздо меньше, чем при твоей… эм… болезни.

Если я захочу увеличить межстрочный интервал, я настрою редактор, спасибо.

Cycle_A ()
Ответ на: комментарий от Cycle_A

Какие файлы - разные или какой-то конкретный? Мне начхать на самом деле, у меня стоит Tab = 1 пробел. И я нашёл, что это хороший вариант. Я не смотрю их в гитхабе, я их смотрю только в VS Code. В принципе, IDE должна этим заниматься, а не я. Просто IDE не совсем совершенная.

den73 ★★★★★ ()
Ответ на: комментарий от Cycle_A

Ну я так и думал. Ладно, давай не будем терять время. За ревью спасибо, табы когда-нибудь поправлю, когда придумаю технологичный способ это сделать.

den73 ★★★★★ ()
Ответ на: комментарий от Cycle_A

в общем, почитал Библию, пошарил по ключам gofmt и понял, что нет простого способа автоматически поддерживать мои закрывающие скобочки, кроме как форкнуть gofmt. Сейчас я этим заниматься не буду, поэтому придётся пользоваться этим уродским способом форматирования :( Ладно, потерпим.

den73 ★★★★★ ()
Ответ на: комментарий от anonymous

Tcl - это круто но я же пишу на голанге. Соответственно, чтобы сделать конфиг на tcl, придётся сначала реализовать кусок tcl, или впилить его в программу, а это явно перебор по трудоёмкости. Или уже сделано?

den73 ★★★★★ ()
Ответ на: комментарий от system-root

На самом деле есть. Но это больше DSL, чем обобщённый формат.

В частности pf.conf, smtpd.conf, cwmrc и иже с ними.

PS: Познавший yacc о конфигах не плачет.

beastie ★★★★★ ()
Последнее исправление: beastie (всего исправлений: 2)
Ответ на: комментарий от den73

Ты всё-таки упорот :). Я был уверен, что возьмёшь tree - детище такого же фанатика NIH. Ну, а на Go есть пучок внедряемых скриптовых языков, любой из которых можно взять для конфигов. Рекомендую tengo.

hbee ★★★ ()

YAML or TOML

Используй один из двух. Сказал бы что это уже стандарты де факто, но пруфов особо нет. Но докер и к8с юзают ямл, в го пакетные менеджеры юзают томл. Собственно они для этого и придумывались, как замена кондовому джейсону. Они обратно с джейсоном совместимы. Томл отличается от ямла более расширенной граматикой что позволяет описывать более сложные условия, но ямл более читаемый, если нет особых причин юзай ямл.

DukeNukem ()

Ещё есть HCL — чуть более продвинутый json. И он даже на GO написан. Правда, там не особо с документацией на сам формат конфига.

anonymous ()