pkg/checker/question_link.go (95 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package checker
import (
"github.com/apache/answer/internal/base/constant"
"github.com/apache/answer/pkg/obj"
"github.com/apache/answer/pkg/uid"
)
const (
QuestionLinkTypeURL = 1
QuestionLinkTypeID = 2
)
type QuestionLink struct {
LinkType int
QuestionID string
AnswerID string
}
func GetQuestionLink(content string) []QuestionLink {
uniqueIDs := make(map[string]struct{})
var questionLinks []QuestionLink
// use two pointer to find the link
left, right := 0, 0
for right < len(content) {
// find "/questions/" or "#"
if right+11 < len(content) && content[right:right+11] == "/questions/" {
left = right
right += 11
processURL(content, &left, &right, uniqueIDs, &questionLinks)
} else if content[right] == '#' {
left = right + 1
right = left
processID(content, &left, &right, uniqueIDs, &questionLinks)
} else {
right++
}
}
return questionLinks
}
func processURL(content string, left, right *int, uniqueIDs map[string]struct{}, questionLinks *[]QuestionLink) {
for *right < len(content) && (isDigit(content[*right]) || isLetter(content[*right])) {
*right++
}
questionID := content[*left+len("/questions/") : *right]
answerID := ""
if *right < len(content) && content[*right] == '/' {
*left = *right + 1
*right = *left
for *right < len(content) && (isDigit(content[*right]) || isLetter(content[*right])) {
*right++
}
answerID = content[*left:*right]
}
addUniqueID(questionID, answerID, QuestionLinkTypeURL, uniqueIDs, questionLinks)
}
func processID(content string, left, right *int, uniqueIDs map[string]struct{}, questionLinks *[]QuestionLink) {
for *right < len(content) && (isDigit(content[*right]) || isLetter(content[*right])) {
*right++
}
id := content[*left:*right]
addUniqueID(id, "", QuestionLinkTypeID, uniqueIDs, questionLinks)
}
func isDigit(c byte) bool {
return c >= '0' && c <= '9'
}
func isLetter(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
func addUniqueID(questionID, answerID string, linkType int, uniqueIDs map[string]struct{}, questionLinks *[]QuestionLink) {
isAdd := false
if answerID != "" {
objectType, err := obj.GetObjectTypeStrByObjectID(uid.DeShortID(answerID))
if err != nil {
answerID = ""
}
if objectType == constant.AnswerObjectType {
if _, ok := uniqueIDs[answerID]; !ok {
uniqueIDs[answerID] = struct{}{}
isAdd = true
}
}
}
if objectType, err := obj.GetObjectTypeStrByObjectID(uid.DeShortID(questionID)); err == nil {
if _, ok := uniqueIDs[questionID]; !ok {
uniqueIDs[questionID] = struct{}{}
isAdd = true
if objectType == constant.AnswerObjectType {
answerID = questionID
questionID = ""
}
}
}
if isAdd {
*questionLinks = append(*questionLinks, QuestionLink{
LinkType: linkType,
QuestionID: questionID,
AnswerID: answerID,
})
}
}