<commit_before>// Copyright (C) 2014 <NAME> and Contributors (see the CONTRIBUTORS file).
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.

package config

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/syncthing/syncthing/internal/events"
	"github.com/syncthing/syncthing/internal/osutil"
	"github.com/syncthing/syncthing/internal/protocol"
)

// An interface to handle configuration changes, and a wrapper type á la
// http.Handler

type Handler interface {
	Changed(Configuration) error
}

type HandlerFunc func(Configuration) error

func (fn HandlerFunc) Changed(cfg Configuration) error {
	return fn(cfg)
}

// A wrapper around a Configuration that manages loads, saves and published
// notifications of changes to registered Handlers

type ConfigWrapper struct {
	cfg  Configuration
	path string

	deviceMap map[protocol.DeviceID]DeviceConfiguration
	folderMap map[string]FolderConfiguration
	replaces  chan Configuration
	subs      []Handler
	mut       sync.Mutex
}

// Wrap wraps an existing Configuration structure and ties it to a file on
// disk.
func Wrap(path string, cfg Configuration) *ConfigWrapper {
	w := &ConfigWrapper{cfg: cfg, path: path}
	w.replaces = make(chan Configuration)
	go w.Serve()
	return w
}

// Load loads an existing file on disk and returns a new configuration
// wrapper.
func Load(path string, myID protocol.DeviceID) (*ConfigWrapper, error) {
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer fd.Close()

	cfg, err := ReadXML(fd, myID)
	if err != nil {
		return nil, err
	}

	return Wrap(path, cfg), nil
}

// Serve handles configuration replace events and calls any interested
// handlers. It is started automatically by Wrap() and Load() and should not
// be run manually.
func (w *ConfigWrapper) Serve() {
	for cfg := range w.replaces {
		w.mut.Lock()
		subs := w.subs
		w.mut.Unlock()
		for _, h := range subs {
			h.Changed(cfg)
		}
	}
}

// Stop stops the Serve() loop.
func (w *ConfigWrapper) Stop() {
	close(w.replaces)
}

// Subscriber registers the given handler to be called on any future
// configuration changes.
func (w *ConfigWrapper) Subscribe(h Handler) {
	w.mut.Lock()
	w.subs = append(w.subs, h)
	w.mut.Unlock()
}

// Raw returns the currently wrapped Configuration object.
func (w *ConfigWrapper) Raw() Configuration {
	return w.cfg
}

// Replace swaps the current configuration object for the given one.
func (w *ConfigWrapper) Replace(cfg Configuration) {
	w.mut.Lock()
	defer w.mut.Unlock()<commit_msg>Use a separate lock for the subscriber stuff<commit_after>// Copyright (C) 2014 <NAME> and Contributors (see the CONTRIBUTORS file).
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.

package config

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/syncthing/syncthing/internal/events"
	"github.com/syncthing/syncthing/internal/osutil"
	"github.com/syncthing/syncthing/internal/protocol"
)

// An interface to handle configuration changes, and a wrapper type á la
// http.Handler

type Handler interface {
	Changed(Configuration) error
}

type HandlerFunc func(Configuration) error

func (fn HandlerFunc) Changed(cfg Configuration) error {
	return fn(cfg)
}

// A wrapper around a Configuration that manages loads, saves and published
// notifications of changes to registered Handlers

type ConfigWrapper struct {
	cfg  Configuration
	path string

	deviceMap map[protocol.DeviceID]DeviceConfiguration
	folderMap map[string]FolderConfiguration
	replaces  chan Configuration
	mut       sync.Mutex

	subs []Handler
	sMut sync.Mutex
}

// Wrap wraps an existing Configuration structure and ties it to a file on
// disk.
func Wrap(path string, cfg Configuration) *ConfigWrapper {
	w := &ConfigWrapper{cfg: cfg, path: path}
	w.replaces = make(chan Configuration)
	go w.Serve()
	return w
}

// Load loads an existing file on disk and returns a new configuration
// wrapper.
func Load(path string, myID protocol.DeviceID) (*ConfigWrapper, error) {
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer fd.Close()

	cfg, err := ReadXML(fd, myID)
	if err != nil {
		return nil, err
	}

	return Wrap(path, cfg), nil
}

// Serve handles configuration replace events and calls any interested
// handlers. It is started automatically by Wrap() and Load() and should not
// be run manually.
func (w *ConfigWrapper) Serve() {
	for cfg := range w.replaces {
		w.sMut.Lock()
		subs := w.subs
		w.sMut.Unlock()
		for _, h := range subs {
			h.Changed(cfg)
		}
	}
}

// Stop stops the Serve() loop. Set and Replace operations will panic after a
// Stop.
func (w *ConfigWrapper) Stop() {
	close(w.replaces)
}

// Subscribe registers the given handler to be called on any future
// configuration changes.
func (w *ConfigWrapper) Subscribe(h Handler) {
	w.sMut.Lock()
	w.subs = append(w.subs, h)
	w.sMut.Unlock()
}

// Raw returns the currently wrapped Configuration object.
func (w *ConfigWrapper) Raw() Configuration {
	return w.cfg
}

// Replace swaps the current configuration object for the given one.
func (w *ConfigWrapper) Replace(cfg Configuration) {
	w.mut.Lock()
	defer w.mut.Unlock()
{'fraction_non_alphanumeric': 0.09923906150919468, 'fraction_numerical': 0.0015852885225110971, 'mean_word_length': 3.7977186311787072, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4090579', 'n_tokens_mistral': 1929, 'n_tokens_neox': 1850, 'n_words': 753}