package story_service import ( "context" "encoding/json" "fmt" "log" "os" "regexp" "strings" ) type StoryService struct { filepath string story *Story } func NewStoryService(filepath string) (*StoryService, error) { s := &StoryService{filepath: filepath} if err := s.Load(); err != nil { return nil, err } return s, nil } func (s *StoryService) Load() error { data, err := os.ReadFile(s.filepath) if err != nil { return fmt.Errorf("story file %s not found", s.filepath) } log.Printf("load story from: %s", s.filepath) story := &Story{} if err := json.Unmarshal(data, story); err != nil { return err } s.story = story return nil } func (s *StoryService) Save() error { story := s.story data, err := json.Marshal(story) if err != nil { return err } if err := os.WriteFile(s.filepath, data, 0x777); err != nil { return err } log.Printf("save story to: %s", s.filepath) return nil } func (s *StoryService) Update() error { if err := s.Save(); err != nil { return err } return s.Load() } func (s *StoryService) GetPlace(code string) *Place { if strings.HasPrefix(code, "[") || strings.HasSuffix(code, "]") { return &Place{ Code: code, Name: "Не найдено", Text: "Уважаемые детективы внимательно прочитайте правила.", } } code = clearCode(code) for _, place := range s.story.Places { if clearCode(place.Code) == code { re := regexp.MustCompile(`\(\[[a-zA-Zа-яА-Я\d-]+\]\)`) text := re.ReplaceAllString(place.Text, "") return &Place{ Code: place.Code, Name: place.Name, Text: text, Applications: place.Applications, } } } return &Place{ Code: code, Name: "Не найдено", Text: "Такой точки не существует.", } } func (s *StoryService) UpdatePlace(node *GraphNode) error { for i := range s.story.Places { if s.story.Places[i].Code == node.Label { s.story.Places[i] = &Place{ Code: s.story.Places[i].Code, Name: node.Name, Text: node.Text, Applications: s.story.Places[i].Applications, } break } } s.Update() return nil } func (s *StoryService) GetGraph(ctx context.Context) *Graph { m := make(map[string]int32, len(s.story.Places)) nodes := make([]*GraphNode, 0, len(s.story.Places)) for i, place := range s.story.Places { m[clearCode(place.Code)] = int32(i) applications := make([]*GraphApplication, 0, len(place.Applications)) for _, application := range place.Applications { applications = append( applications, &GraphApplication{ Name: application.Name, }, ) } nodes = append( nodes, &GraphNode{ ID: int32(i), Label: place.Code, Name: place.Name, Text: place.Text, Applications: applications, }, ) } edges := make([]*GraphEdge, 0, len(s.story.Places)*3) for _, place := range s.story.Places { re := regexp.MustCompile(`\(\[[a-zA-Zа-яА-Я\d-]+\]\)`) matches := re.FindAllString(place.Text, -1) for _, match := range matches { edges = append( edges, &GraphEdge{ From: m[clearCode(place.Code)], To: m[clearMatch(match)], }, ) } } return &Graph{ Nodes: nodes, Edges: edges, } }