dedup feature rendering, align default limit, simplify CSS guard

This commit is contained in:
Rustem Kamalov
2026-06-02 04:21:56 +03:00
parent 327b65869b
commit d0006e30ff
6 changed files with 72 additions and 20 deletions

View File

@@ -315,6 +315,11 @@ func (q Query) IsEmpty() bool {
// MaxQueryLimit is the maximum allowed value for the limit parameter.
const MaxQueryLimit = 100
// defaultQueryLimit is the assumed limit when a request omits it (InitFromContext)
// and the fallback used by pagination math for internally-built queries that
// leave Limit unset.
const defaultQueryLimit = 10
// InitFromContext populates Query from HTTP query parameters and request
// headers. It validates numeric/boolean inputs and returns an *APIError for
// invalid client input (400) or a plain error for internal failures.
@@ -326,7 +331,7 @@ func (searchQuery *Query) InitFromContext(reqCtx *fiber.Ctx) error {
searchQuery.Filetype = strings.TrimSpace(reqCtx.Query("file"))
searchQuery.Site = strings.TrimSpace(reqCtx.Query("site"))
limitRaw := reqCtx.Query("limit", "10")
limitRaw := reqCtx.Query("limit", strconv.Itoa(defaultQueryLimit))
limit, err := strconv.Atoi(limitRaw)
if err != nil {
return errInvalidLimit("limit must be an integer")

View File

@@ -34,7 +34,7 @@ func RenderMarkdown(env *Envelope) []byte {
fmt.Fprintf(&b, "-> %s\n\n", r.URL)
}
renderMarkdownFeatures(&b, env.SerpFeatures, featureRenderOrderAfterResults())
renderMarkdownFeatures(&b, env.SerpFeatures, featureRenderOrderAfterResults(env.SerpFeatures))
return []byte(b.String())
}
@@ -59,14 +59,9 @@ func RenderMarkdownImage(env *ImageEnvelope) []byte {
}
func renderMarkdownFeatures(b *strings.Builder, features []SerpFeature, order []ResultType) {
for _, featureType := range order {
for _, feature := range features {
if feature.Type != featureType {
continue
}
renderMarkdownFeature(b, feature)
}
}
forEachFeatureInOrder(features, order, func(feature SerpFeature) {
renderMarkdownFeature(b, feature)
})
}
func renderMarkdownFeature(b *strings.Builder, feature SerpFeature) {

View File

@@ -34,7 +34,7 @@ func RenderText(env *Envelope) []byte {
fmt.Fprintf(&b, "URL: %s\n\n", r.URL)
}
renderTextFeatures(&b, env.SerpFeatures, featureRenderOrderAfterResults())
renderTextFeatures(&b, env.SerpFeatures, featureRenderOrderAfterResults(env.SerpFeatures))
return []byte(b.String())
}
@@ -76,12 +76,20 @@ func RenderNDJSONImage(env *ImageEnvelope) []byte {
}
func renderTextFeatures(b *strings.Builder, features []SerpFeature, order []ResultType) {
forEachFeatureInOrder(features, order, func(feature SerpFeature) {
renderTextFeature(b, feature)
})
}
// forEachFeatureInOrder invokes render for every feature whose Type appears in
// order, type by type. It is the single iteration shared by the text and
// markdown renderers.
func forEachFeatureInOrder(features []SerpFeature, order []ResultType, render func(SerpFeature)) {
for _, featureType := range order {
for _, feature := range features {
if feature.Type != featureType {
continue
if feature.Type == featureType {
render(feature)
}
renderTextFeature(b, feature)
}
}
}
@@ -151,9 +159,12 @@ func featureRenderOrderBeforeResults() []ResultType {
}
// featureRenderOrderAfterResults lists the feature sections rendered below the
// results list (related searches and the module gallery), in fixed order.
func featureRenderOrderAfterResults() []ResultType {
return []ResultType{
// results list (related searches and the module gallery), in fixed order. Any
// feature type present in features but absent from both fixed orders is appended
// at the end, so a newly added feature enum is never silently dropped from text
// or markdown output.
func featureRenderOrderAfterResults(features []SerpFeature) []ResultType {
order := []ResultType{
ResultTypeRelatedSearches,
ResultTypeNews,
ResultTypeVideo,
@@ -166,6 +177,20 @@ func featureRenderOrderAfterResults() []ResultType {
ResultTypeWeather,
ResultTypeDictionary,
}
placed := make(map[ResultType]bool, len(order)+len(featureRenderOrderBeforeResults()))
for _, t := range featureRenderOrderBeforeResults() {
placed[t] = true
}
for _, t := range order {
placed[t] = true
}
for _, feature := range features {
if !placed[feature.Type] {
order = append(order, feature.Type)
placed[feature.Type] = true
}
}
return order
}
func featureHeading(feature SerpFeature) string {

View File

@@ -124,7 +124,7 @@ func (e *Envelope) Finalize(startedAt time.Time, q Query) {
limit := q.Limit
if limit <= 0 {
limit = 25
limit = defaultQueryLimit
}
page := q.Start/limit + 1
e.Pagination = Pagination{
@@ -150,7 +150,7 @@ func (e *ImageEnvelope) Finalize(startedAt time.Time, q Query) {
limit := q.Limit
if limit <= 0 {
limit = 25
limit = defaultQueryLimit
}
page := q.Start/limit + 1
e.Pagination = Pagination{

View File

@@ -150,3 +150,25 @@ func TestRenderersIncludeSerpFeatures(t *testing.T) {
t.Fatalf("feature ndjson lines missing kind tag: %v", lines)
}
}
// TestRenderFeaturesUnorderedTypeStillRenders guards against silently dropping a
// feature whose Type is in neither render-order section (e.g. a newly added
// enum value not yet placed). It must still appear in text and markdown output.
func TestRenderFeaturesUnorderedTypeStillRenders(t *testing.T) {
const unplaced ResultType = "experimental_module"
env := NewEnvelope(Query{Text: "openserp"}, "req-1", time.Unix(0, 0), []string{"google"})
env.SerpFeatures = append(env.SerpFeatures, SerpFeature{
Type: unplaced,
Text: "experimental feature body",
})
text := string(RenderText(env))
if !strings.Contains(text, "experimental feature body") {
t.Fatalf("text output dropped an unordered feature type:\n%s", text)
}
markdown := string(RenderMarkdown(env))
if !strings.Contains(markdown, "experimental feature body") {
t.Fatalf("markdown output dropped an unordered feature type:\n%s", markdown)
}
}

View File

@@ -104,7 +104,12 @@ func looksLikeCSS(text string) bool {
return true
}
// CSS rule blocks look like "} .cls {" / "} #id {"; prose almost never does.
return strings.Contains(text, "} .") || strings.Contains(text, "} #") || strings.Contains(text, "{ ") && strings.Count(text, ": ") > 20 && strings.Count(text, ";") > 20
if strings.Contains(text, "} .") || strings.Contains(text, "} #") {
return true
}
// A dense run of "prop: value;" declarations inside a "{ ... }" block is the
// other tell for inlined stylesheet text.
return strings.Contains(text, "{ ") && strings.Count(text, ": ") > 20 && strings.Count(text, ";") > 20
}
func extractGoogleFeaturesFromPage(page *rod.Page) []core.SerpFeature {